diff --git a/CHANGELOG.md b/CHANGELOG.md index ec68bff4..1d94289c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.14.3](https://github.com/6pac/SlickGrid/compare/5.14.2...5.14.3) (2024-11-28) + +### Bug Fixes + +* ensure DOM alterations during initialization are always cleaned up when there are hidden parents and forceFitColumns=true ([#1085](https://github.com/6pac/SlickGrid/issues/1085)) ([c32fe5a](https://github.com/6pac/SlickGrid/commit/c32fe5a0401fbc9e11aa861328eb0163d78f8ad7)) + ## [5.14.2](https://github.com/6pac/SlickGrid/compare/5.14.1...5.14.2) (2024-11-16) ### Bug Fixes diff --git a/dist/browser/controls/slick.columnmenu.js.map b/dist/browser/controls/slick.columnmenu.js.map index 22cb7a95..18424f70 100644 --- a/dist/browser/controls/slick.columnmenu.js.map +++ b/dist/browser/controls/slick.columnmenu.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../../../src/controls/slick.columnmenu.ts"], - "sourcesContent": ["import { BindingEventService as BindingEventService_, Event as SlickEvent_, type SlickEventData, Utils as Utils_ } from '../slick.core';\r\nimport type { Column, ColumnPickerOption, DOMMouseOrTouchEvent, GridOption, OnColumnsChangedArgs } from '../models/index';\r\nimport type { SlickGrid } from '../slick.grid';\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 SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_;\r\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\r\n\r\n/***\r\n * A control to add a Column Picker (right+click on any column header to reveal the column picker)\r\n * NOTE: this a simplified and updated version of slick.columnpicker.js\r\n *\r\n * USAGE:\r\n *\r\n * Add the slick.columnpicker.(js|css) files and register it with the grid.\r\n *\r\n * Available options, by defining a columnPicker object:\r\n *\r\n * let options = {\r\n * enableCellNavigation: true,\r\n * columnPicker: {\r\n * columnTitle: \"Columns\", // default to empty string\r\n *\r\n * // the last 2 checkboxes titles\r\n * hideForceFitButton: false, // show/hide checkbox near the end \"Force Fit Columns\" (default:false)\r\n * hideSyncResizeButton: false, // show/hide checkbox near the end \"Synchronous Resize\" (default:false)\r\n * forceFitTitle: \"Force fit columns\", // default to \"Force fit columns\"\r\n * headerColumnValueExtractor: \"Extract the column label\" // default to column.name\r\n * syncResizeTitle: \"Synchronous resize\", // default to \"Synchronous resize\"\r\n * }\r\n * };\r\n */\r\n\r\nexport class SlickColumnMenu {\r\n // --\r\n // public API\r\n onColumnsChanged = new SlickEvent('onColumnsChanged');\r\n\r\n // --\r\n // protected props\r\n protected _gridUid: string;\r\n protected _columnTitleElm!: HTMLElement;\r\n protected _listElm!: HTMLElement;\r\n protected _menuElm!: HTMLElement;\r\n protected _columnCheckboxes: HTMLInputElement[] = [];\r\n protected _bindingEventService = new BindingEventService();\r\n protected _options: GridOption;\r\n protected _defaults: ColumnPickerOption = {\r\n fadeSpeed: 250,\r\n\r\n // the last 2 checkboxes titles\r\n hideForceFitButton: false,\r\n hideSyncResizeButton: false,\r\n forceFitTitle: 'Force fit columns',\r\n syncResizeTitle: 'Synchronous resize',\r\n headerColumnValueExtractor: (columnDef: Column) => Utils.getHtmlStringOutput(columnDef.name || '', 'innerHTML'),\r\n };\r\n\r\n constructor(protected columns: Column[], protected readonly grid: SlickGrid, options: GridOption) {\r\n this._gridUid = grid.getUID();\r\n this._options = Utils.extend({}, this._defaults, options);\r\n this.init(this.grid);\r\n }\r\n\r\n init(grid: SlickGrid) {\r\n Utils.addSlickEventPubSubWhenDefined(grid.getPubSubService(), this);\r\n grid.onHeaderContextMenu.subscribe(this.handleHeaderContextMenu.bind(this));\r\n grid.onColumnsReordered.subscribe(this.updateColumnOrder.bind(this));\r\n\r\n this._menuElm = document.createElement('div');\r\n this._menuElm.className = `slick-columnpicker ${this._gridUid}`;\r\n this._menuElm.style.display = 'none';\r\n document.body.appendChild(this._menuElm);\r\n\r\n const buttonElm = document.createElement('button');\r\n buttonElm.type = 'button';\r\n buttonElm.className = 'close';\r\n buttonElm.dataset.dismiss = 'slick-columnpicker';\r\n buttonElm.ariaLabel = 'Close';\r\n\r\n const spanCloseElm = document.createElement('span');\r\n spanCloseElm.className = 'close';\r\n spanCloseElm.ariaHidden = 'true';\r\n spanCloseElm.textContent = '\u00D7';\r\n buttonElm.appendChild(spanCloseElm);\r\n this._menuElm.appendChild(buttonElm);\r\n\r\n // user could pass a title on top of the columns list\r\n if (this._options.columnPickerTitle || (this._options.columnPicker?.columnTitle)) {\r\n const columnTitle = this._options.columnPickerTitle || this._options.columnPicker?.columnTitle;\r\n this._columnTitleElm = document.createElement('div');\r\n this._columnTitleElm.className = 'slick-gridmenu-custom';\r\n this._columnTitleElm.textContent = columnTitle || '';\r\n this._menuElm.appendChild(this._columnTitleElm);\r\n }\r\n\r\n this._bindingEventService.bind(this._menuElm, 'click', this.updateColumn.bind(this) as EventListener);\r\n\r\n this._listElm = document.createElement('span');\r\n this._listElm.className = 'slick-columnpicker-list';\r\n\r\n // Hide the menu on outside click.\r\n this._bindingEventService.bind(document.body, 'mousedown', this.handleBodyMouseDown.bind(this) as EventListener);\r\n\r\n // destroy the picker if user leaves the page\r\n this._bindingEventService.bind(document.body, 'beforeunload', this.destroy.bind(this));\r\n }\r\n\r\n destroy() {\r\n this.grid.onHeaderContextMenu.unsubscribe(this.handleHeaderContextMenu.bind(this));\r\n this.grid.onColumnsReordered.unsubscribe(this.updateColumnOrder.bind(this));\r\n this._bindingEventService.unbindAll();\r\n this._listElm?.remove();\r\n this._menuElm?.remove();\r\n }\r\n\r\n handleBodyMouseDown(e: DOMMouseOrTouchEvent) {\r\n if ((this._menuElm !== e.target && !(this._menuElm && this._menuElm.contains(e.target))) || e.target.className === 'close') {\r\n this._menuElm.setAttribute('aria-expanded', 'false');\r\n this._menuElm.style.display = 'none';\r\n }\r\n }\r\n\r\n handleHeaderContextMenu(e: SlickEventData) {\r\n e.preventDefault();\r\n Utils.emptyElement(this._listElm);\r\n this.updateColumnOrder();\r\n this._columnCheckboxes = [];\r\n\r\n let columnId, columnLabel, excludeCssClass;\r\n for (let i = 0; i < this.columns.length; i++) {\r\n columnId = this.columns[i].id;\r\n const colName: string = this.columns[i].name instanceof HTMLElement\r\n ? (this.columns[i].name as HTMLElement).innerHTML\r\n : (this.columns[i].name || '') as string;\r\n excludeCssClass = this.columns[i].excludeFromColumnPicker ? 'hidden' : '';\r\n\r\n const liElm = document.createElement('li');\r\n liElm.className = excludeCssClass;\r\n liElm.ariaLabel = colName;\r\n\r\n const checkboxElm = document.createElement('input');\r\n checkboxElm.type = 'checkbox';\r\n checkboxElm.id = `${this._gridUid}colpicker-${columnId}`;\r\n checkboxElm.dataset.columnid = String(this.columns[i].id);\r\n liElm.appendChild(checkboxElm);\r\n\r\n this._columnCheckboxes.push(checkboxElm);\r\n\r\n if (Utils.isDefined(this.grid.getColumnIndex(columnId)) && !this.columns[i].hidden) {\r\n checkboxElm.checked = true;\r\n }\r\n\r\n columnLabel = (this._options?.columnPicker?.headerColumnValueExtractor)\r\n ? this._options.columnPicker.headerColumnValueExtractor(this.columns[i], this._options)\r\n : this._defaults.headerColumnValueExtractor!(this.columns[i], this._options);\r\n\r\n const labelElm = document.createElement('label');\r\n labelElm.htmlFor = `${this._gridUid}colpicker-${columnId}`;\r\n this.grid.applyHtmlCode(labelElm, columnLabel);\r\n liElm.appendChild(labelElm);\r\n this._listElm.appendChild(liElm);\r\n }\r\n\r\n if (this._options.columnPicker && (!this._options.columnPicker.hideForceFitButton || !this._options.columnPicker.hideSyncResizeButton)) {\r\n this._listElm.appendChild(document.createElement('hr'));\r\n }\r\n\r\n if (!this._options.columnPicker?.hideForceFitButton) {\r\n const forceFitTitle = this._options.columnPicker?.forceFitTitle || this._options.forceFitTitle;\r\n\r\n const liElm = document.createElement('li');\r\n liElm.ariaLabel = forceFitTitle || '';\r\n this._listElm.appendChild(liElm);\r\n\r\n const forceFitCheckboxElm = document.createElement('input');\r\n forceFitCheckboxElm.type = 'checkbox';\r\n forceFitCheckboxElm.id = `${this._gridUid}colpicker-forcefit`;\r\n forceFitCheckboxElm.dataset.option = 'autoresize';\r\n liElm.appendChild(forceFitCheckboxElm);\r\n\r\n const labelElm = document.createElement('label');\r\n labelElm.htmlFor = `${this._gridUid}colpicker-forcefit`;\r\n labelElm.textContent = forceFitTitle || '';\r\n liElm.appendChild(labelElm);\r\n\r\n if (this.grid.getOptions().forceFitColumns) {\r\n forceFitCheckboxElm.checked = true;\r\n }\r\n }\r\n\r\n if (!this._options.columnPicker?.hideSyncResizeButton) {\r\n const syncResizeTitle = this._options.columnPicker?.syncResizeTitle || this._options.syncResizeTitle;\r\n\r\n const liElm = document.createElement('li');\r\n liElm.ariaLabel = syncResizeTitle || '';\r\n this._listElm.appendChild(liElm);\r\n\r\n const syncResizeCheckboxElm = document.createElement('input');\r\n syncResizeCheckboxElm.type = 'checkbox';\r\n syncResizeCheckboxElm.id = `${this._gridUid}colpicker-syncresize`;\r\n syncResizeCheckboxElm.dataset.option = 'syncresize';\r\n liElm.appendChild(syncResizeCheckboxElm);\r\n\r\n const labelElm = document.createElement('label');\r\n labelElm.htmlFor = `${this._gridUid}colpicker-syncresize`;\r\n labelElm.textContent = syncResizeTitle || '';\r\n liElm.appendChild(labelElm);\r\n\r\n if (this.grid.getOptions().syncColumnCellResize) {\r\n syncResizeCheckboxElm.checked = true;\r\n }\r\n }\r\n\r\n this.repositionMenu(e);\r\n }\r\n\r\n repositionMenu(event: DOMMouseOrTouchEvent | SlickEventData) {\r\n const targetEvent = (event as TouchEvent)?.touches?.[0] || event;\r\n this._menuElm.style.top = `${targetEvent.pageY - 10}px`;\r\n this._menuElm.style.left = `${targetEvent.pageX - 10}px`;\r\n this._menuElm.style.maxHeight = `${window.innerHeight - targetEvent.clientY}px`;\r\n this._menuElm.style.display = 'block';\r\n this._menuElm.setAttribute('aria-expanded', 'true');\r\n this._menuElm.appendChild(this._listElm);\r\n }\r\n\r\n updateColumnOrder() {\r\n // Because columns can be reordered, we have to update the `columns`\r\n // to reflect the new order, however we can't just take `grid.getColumns()`,\r\n // as it does not include columns currently hidden by the picker.\r\n // We create a new `columns` structure by leaving currently-hidden\r\n // columns in their original ordinal position and interleaving the results\r\n // of the current column sort.\r\n const current = this.grid.getColumns().slice(0);\r\n const ordered = new Array(this.columns.length);\r\n for (let i = 0; i < ordered.length; i++) {\r\n if (this.grid.getColumnIndex(this.columns[i].id) === undefined) {\r\n // If the column doesn't return a value from getColumnIndex,\r\n // it is hidden. Leave it in this position.\r\n ordered[i] = this.columns[i];\r\n } else {\r\n // Otherwise, grab the next visible column.\r\n ordered[i] = current.shift();\r\n }\r\n }\r\n this.columns = ordered;\r\n }\r\n\r\n /** Update the Titles of each sections (command, customTitle, ...) */\r\n updateAllTitles(pickerOptions: { columnTitle: string; }) {\r\n this.grid.applyHtmlCode(this._columnTitleElm, pickerOptions.columnTitle);\r\n }\r\n\r\n updateColumn(e: DOMMouseOrTouchEvent) {\r\n if (e.target.dataset.option === 'autoresize') {\r\n // when calling setOptions, it will resize with ALL Columns (even the hidden ones)\r\n // we can avoid this problem by keeping a reference to the visibleColumns before setOptions and then setColumns after\r\n const previousVisibleColumns = this.getVisibleColumns();\r\n const isChecked = e.target.checked;\r\n this.grid.setOptions({ forceFitColumns: isChecked });\r\n this.grid.setColumns(previousVisibleColumns);\r\n return;\r\n }\r\n\r\n if (e.target.dataset.option === 'syncresize') {\r\n if (e.target.checked) {\r\n this.grid.setOptions({ syncColumnCellResize: true });\r\n } else {\r\n this.grid.setOptions({ syncColumnCellResize: false });\r\n }\r\n return;\r\n }\r\n\r\n if (e.target.type === 'checkbox') {\r\n const isChecked = e.target.checked;\r\n const columnId = e.target.dataset.columnid || '';\r\n const visibleColumns: Column[] = [];\r\n this._columnCheckboxes.forEach((columnCheckbox, idx) => {\r\n if (this.columns[idx].hidden !== undefined) { this.columns[idx].hidden = !columnCheckbox.checked; }\r\n if (columnCheckbox.checked) {\r\n visibleColumns.push(this.columns[idx]);\r\n }\r\n });\r\n\r\n if (!visibleColumns.length) {\r\n e.target.checked = true;\r\n return;\r\n }\r\n\r\n this.grid.setColumns(visibleColumns);\r\n this.onColumnsChanged.notify({ columnId, showing: isChecked, allColumns: this.columns, columns: this.columns, visibleColumns, grid: this.grid });\r\n }\r\n }\r\n\r\n /** @deprecated because of a typo @use `setColumnVisibility()` instead */\r\n setColumnVisibiliy(idxOrId: number | string, show: boolean) {\r\n this.setColumnVisibility(idxOrId, show);\r\n }\r\n\r\n setColumnVisibility(idxOrId: number | string, show: boolean) {\r\n const idx = typeof idxOrId === 'number' ? idxOrId : this.getColumnIndexbyId(idxOrId);\r\n let visibleColumns: Column[] = this.getVisibleColumns();\r\n const col = this.columns[idx];\r\n if (show) {\r\n col.hidden = false;\r\n visibleColumns.splice(idx, 0, col);\r\n } else {\r\n const newVisibleColumns: Column[] = [];\r\n for (let i = 0; i < visibleColumns.length; i++) {\r\n if (visibleColumns[i].id !== col.id) { newVisibleColumns.push(visibleColumns[i]); }\r\n }\r\n visibleColumns = newVisibleColumns;\r\n }\r\n\r\n this.grid.setColumns(visibleColumns);\r\n this.onColumnsChanged.notify({ columnId: col.id, showing: show, allColumns: this.columns, columns: this.columns, visibleColumns, grid: this.grid });\r\n }\r\n\r\n getAllColumns() {\r\n return this.columns;\r\n }\r\n\r\n getColumnbyId(id: number | string) {\r\n for (let i = 0; i < this.columns.length; i++) {\r\n if (this.columns[i].id === id) { return this.columns[i]; }\r\n }\r\n return null;\r\n }\r\n\r\n getColumnIndexbyId(id: number | string) {\r\n for (let i = 0; i < this.columns.length; i++) {\r\n if (this.columns[i].id === id) { return i; }\r\n }\r\n return -1;\r\n }\r\n\r\n /** visible columns, we can simply get them directly from the grid */\r\n getVisibleColumns() {\r\n return this.grid.getColumns();\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 window.Slick.Controls = window.Slick.Controls || {};\r\n window.Slick.Controls.ColumnPicker = SlickColumnMenu;\r\n}\r\n"], + "sourcesContent": ["import { BindingEventService as BindingEventService_, Event as SlickEvent_, type SlickEventData, Utils as Utils_ } from '../slick.core.js';\r\nimport type { Column, ColumnPickerOption, DOMMouseOrTouchEvent, GridOption, OnColumnsChangedArgs } 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 BindingEventService = IIFE_ONLY ? Slick.BindingEventService : BindingEventService_;\r\nconst SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_;\r\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\r\n\r\n/***\r\n * A control to add a Column Picker (right+click on any column header to reveal the column picker)\r\n * NOTE: this a simplified and updated version of slick.columnpicker.js\r\n *\r\n * USAGE:\r\n *\r\n * Add the slick.columnpicker.(js|css) files and register it with the grid.\r\n *\r\n * Available options, by defining a columnPicker object:\r\n *\r\n * let options = {\r\n * enableCellNavigation: true,\r\n * columnPicker: {\r\n * columnTitle: \"Columns\", // default to empty string\r\n *\r\n * // the last 2 checkboxes titles\r\n * hideForceFitButton: false, // show/hide checkbox near the end \"Force Fit Columns\" (default:false)\r\n * hideSyncResizeButton: false, // show/hide checkbox near the end \"Synchronous Resize\" (default:false)\r\n * forceFitTitle: \"Force fit columns\", // default to \"Force fit columns\"\r\n * headerColumnValueExtractor: \"Extract the column label\" // default to column.name\r\n * syncResizeTitle: \"Synchronous resize\", // default to \"Synchronous resize\"\r\n * }\r\n * };\r\n */\r\n\r\nexport class SlickColumnMenu {\r\n // --\r\n // public API\r\n onColumnsChanged = new SlickEvent('onColumnsChanged');\r\n\r\n // --\r\n // protected props\r\n protected _gridUid: string;\r\n protected _columnTitleElm!: HTMLElement;\r\n protected _listElm!: HTMLElement;\r\n protected _menuElm!: HTMLElement;\r\n protected _columnCheckboxes: HTMLInputElement[] = [];\r\n protected _bindingEventService = new BindingEventService();\r\n protected _options: GridOption;\r\n protected _defaults: ColumnPickerOption = {\r\n fadeSpeed: 250,\r\n\r\n // the last 2 checkboxes titles\r\n hideForceFitButton: false,\r\n hideSyncResizeButton: false,\r\n forceFitTitle: 'Force fit columns',\r\n syncResizeTitle: 'Synchronous resize',\r\n headerColumnValueExtractor: (columnDef: Column) => Utils.getHtmlStringOutput(columnDef.name || '', 'innerHTML'),\r\n };\r\n\r\n constructor(protected columns: Column[], protected readonly grid: SlickGrid, options: GridOption) {\r\n this._gridUid = grid.getUID();\r\n this._options = Utils.extend({}, this._defaults, options);\r\n this.init(this.grid);\r\n }\r\n\r\n init(grid: SlickGrid) {\r\n Utils.addSlickEventPubSubWhenDefined(grid.getPubSubService(), this);\r\n grid.onHeaderContextMenu.subscribe(this.handleHeaderContextMenu.bind(this));\r\n grid.onColumnsReordered.subscribe(this.updateColumnOrder.bind(this));\r\n\r\n this._menuElm = document.createElement('div');\r\n this._menuElm.className = `slick-columnpicker ${this._gridUid}`;\r\n this._menuElm.style.display = 'none';\r\n document.body.appendChild(this._menuElm);\r\n\r\n const buttonElm = document.createElement('button');\r\n buttonElm.type = 'button';\r\n buttonElm.className = 'close';\r\n buttonElm.dataset.dismiss = 'slick-columnpicker';\r\n buttonElm.ariaLabel = 'Close';\r\n\r\n const spanCloseElm = document.createElement('span');\r\n spanCloseElm.className = 'close';\r\n spanCloseElm.ariaHidden = 'true';\r\n spanCloseElm.textContent = '\u00D7';\r\n buttonElm.appendChild(spanCloseElm);\r\n this._menuElm.appendChild(buttonElm);\r\n\r\n // user could pass a title on top of the columns list\r\n if (this._options.columnPickerTitle || (this._options.columnPicker?.columnTitle)) {\r\n const columnTitle = this._options.columnPickerTitle || this._options.columnPicker?.columnTitle;\r\n this._columnTitleElm = document.createElement('div');\r\n this._columnTitleElm.className = 'slick-gridmenu-custom';\r\n this._columnTitleElm.textContent = columnTitle || '';\r\n this._menuElm.appendChild(this._columnTitleElm);\r\n }\r\n\r\n this._bindingEventService.bind(this._menuElm, 'click', this.updateColumn.bind(this) as EventListener);\r\n\r\n this._listElm = document.createElement('span');\r\n this._listElm.className = 'slick-columnpicker-list';\r\n\r\n // Hide the menu on outside click.\r\n this._bindingEventService.bind(document.body, 'mousedown', this.handleBodyMouseDown.bind(this) as EventListener);\r\n\r\n // destroy the picker if user leaves the page\r\n this._bindingEventService.bind(document.body, 'beforeunload', this.destroy.bind(this));\r\n }\r\n\r\n destroy() {\r\n this.grid.onHeaderContextMenu.unsubscribe(this.handleHeaderContextMenu.bind(this));\r\n this.grid.onColumnsReordered.unsubscribe(this.updateColumnOrder.bind(this));\r\n this._bindingEventService.unbindAll();\r\n this._listElm?.remove();\r\n this._menuElm?.remove();\r\n }\r\n\r\n handleBodyMouseDown(e: DOMMouseOrTouchEvent) {\r\n if ((this._menuElm !== e.target && !(this._menuElm && this._menuElm.contains(e.target))) || e.target.className === 'close') {\r\n this._menuElm.setAttribute('aria-expanded', 'false');\r\n this._menuElm.style.display = 'none';\r\n }\r\n }\r\n\r\n handleHeaderContextMenu(e: SlickEventData) {\r\n e.preventDefault();\r\n Utils.emptyElement(this._listElm);\r\n this.updateColumnOrder();\r\n this._columnCheckboxes = [];\r\n\r\n let columnId, columnLabel, excludeCssClass;\r\n for (let i = 0; i < this.columns.length; i++) {\r\n columnId = this.columns[i].id;\r\n const colName: string = this.columns[i].name instanceof HTMLElement\r\n ? (this.columns[i].name as HTMLElement).innerHTML\r\n : (this.columns[i].name || '') as string;\r\n excludeCssClass = this.columns[i].excludeFromColumnPicker ? 'hidden' : '';\r\n\r\n const liElm = document.createElement('li');\r\n liElm.className = excludeCssClass;\r\n liElm.ariaLabel = colName;\r\n\r\n const checkboxElm = document.createElement('input');\r\n checkboxElm.type = 'checkbox';\r\n checkboxElm.id = `${this._gridUid}colpicker-${columnId}`;\r\n checkboxElm.dataset.columnid = String(this.columns[i].id);\r\n liElm.appendChild(checkboxElm);\r\n\r\n this._columnCheckboxes.push(checkboxElm);\r\n\r\n if (Utils.isDefined(this.grid.getColumnIndex(columnId)) && !this.columns[i].hidden) {\r\n checkboxElm.checked = true;\r\n }\r\n\r\n columnLabel = (this._options?.columnPicker?.headerColumnValueExtractor)\r\n ? this._options.columnPicker.headerColumnValueExtractor(this.columns[i], this._options)\r\n : this._defaults.headerColumnValueExtractor!(this.columns[i], this._options);\r\n\r\n const labelElm = document.createElement('label');\r\n labelElm.htmlFor = `${this._gridUid}colpicker-${columnId}`;\r\n this.grid.applyHtmlCode(labelElm, columnLabel);\r\n liElm.appendChild(labelElm);\r\n this._listElm.appendChild(liElm);\r\n }\r\n\r\n if (this._options.columnPicker && (!this._options.columnPicker.hideForceFitButton || !this._options.columnPicker.hideSyncResizeButton)) {\r\n this._listElm.appendChild(document.createElement('hr'));\r\n }\r\n\r\n if (!this._options.columnPicker?.hideForceFitButton) {\r\n const forceFitTitle = this._options.columnPicker?.forceFitTitle || this._options.forceFitTitle;\r\n\r\n const liElm = document.createElement('li');\r\n liElm.ariaLabel = forceFitTitle || '';\r\n this._listElm.appendChild(liElm);\r\n\r\n const forceFitCheckboxElm = document.createElement('input');\r\n forceFitCheckboxElm.type = 'checkbox';\r\n forceFitCheckboxElm.id = `${this._gridUid}colpicker-forcefit`;\r\n forceFitCheckboxElm.dataset.option = 'autoresize';\r\n liElm.appendChild(forceFitCheckboxElm);\r\n\r\n const labelElm = document.createElement('label');\r\n labelElm.htmlFor = `${this._gridUid}colpicker-forcefit`;\r\n labelElm.textContent = forceFitTitle || '';\r\n liElm.appendChild(labelElm);\r\n\r\n if (this.grid.getOptions().forceFitColumns) {\r\n forceFitCheckboxElm.checked = true;\r\n }\r\n }\r\n\r\n if (!this._options.columnPicker?.hideSyncResizeButton) {\r\n const syncResizeTitle = this._options.columnPicker?.syncResizeTitle || this._options.syncResizeTitle;\r\n\r\n const liElm = document.createElement('li');\r\n liElm.ariaLabel = syncResizeTitle || '';\r\n this._listElm.appendChild(liElm);\r\n\r\n const syncResizeCheckboxElm = document.createElement('input');\r\n syncResizeCheckboxElm.type = 'checkbox';\r\n syncResizeCheckboxElm.id = `${this._gridUid}colpicker-syncresize`;\r\n syncResizeCheckboxElm.dataset.option = 'syncresize';\r\n liElm.appendChild(syncResizeCheckboxElm);\r\n\r\n const labelElm = document.createElement('label');\r\n labelElm.htmlFor = `${this._gridUid}colpicker-syncresize`;\r\n labelElm.textContent = syncResizeTitle || '';\r\n liElm.appendChild(labelElm);\r\n\r\n if (this.grid.getOptions().syncColumnCellResize) {\r\n syncResizeCheckboxElm.checked = true;\r\n }\r\n }\r\n\r\n this.repositionMenu(e);\r\n }\r\n\r\n repositionMenu(event: DOMMouseOrTouchEvent | SlickEventData) {\r\n const targetEvent = (event as TouchEvent)?.touches?.[0] || event;\r\n this._menuElm.style.top = `${targetEvent.pageY - 10}px`;\r\n this._menuElm.style.left = `${targetEvent.pageX - 10}px`;\r\n this._menuElm.style.maxHeight = `${window.innerHeight - targetEvent.clientY}px`;\r\n this._menuElm.style.display = 'block';\r\n this._menuElm.setAttribute('aria-expanded', 'true');\r\n this._menuElm.appendChild(this._listElm);\r\n }\r\n\r\n updateColumnOrder() {\r\n // Because columns can be reordered, we have to update the `columns`\r\n // to reflect the new order, however we can't just take `grid.getColumns()`,\r\n // as it does not include columns currently hidden by the picker.\r\n // We create a new `columns` structure by leaving currently-hidden\r\n // columns in their original ordinal position and interleaving the results\r\n // of the current column sort.\r\n const current = this.grid.getColumns().slice(0);\r\n const ordered = new Array(this.columns.length);\r\n for (let i = 0; i < ordered.length; i++) {\r\n if (this.grid.getColumnIndex(this.columns[i].id) === undefined) {\r\n // If the column doesn't return a value from getColumnIndex,\r\n // it is hidden. Leave it in this position.\r\n ordered[i] = this.columns[i];\r\n } else {\r\n // Otherwise, grab the next visible column.\r\n ordered[i] = current.shift();\r\n }\r\n }\r\n this.columns = ordered;\r\n }\r\n\r\n /** Update the Titles of each sections (command, customTitle, ...) */\r\n updateAllTitles(pickerOptions: { columnTitle: string; }) {\r\n this.grid.applyHtmlCode(this._columnTitleElm, pickerOptions.columnTitle);\r\n }\r\n\r\n updateColumn(e: DOMMouseOrTouchEvent) {\r\n if (e.target.dataset.option === 'autoresize') {\r\n // when calling setOptions, it will resize with ALL Columns (even the hidden ones)\r\n // we can avoid this problem by keeping a reference to the visibleColumns before setOptions and then setColumns after\r\n const previousVisibleColumns = this.getVisibleColumns();\r\n const isChecked = e.target.checked;\r\n this.grid.setOptions({ forceFitColumns: isChecked });\r\n this.grid.setColumns(previousVisibleColumns);\r\n return;\r\n }\r\n\r\n if (e.target.dataset.option === 'syncresize') {\r\n if (e.target.checked) {\r\n this.grid.setOptions({ syncColumnCellResize: true });\r\n } else {\r\n this.grid.setOptions({ syncColumnCellResize: false });\r\n }\r\n return;\r\n }\r\n\r\n if (e.target.type === 'checkbox') {\r\n const isChecked = e.target.checked;\r\n const columnId = e.target.dataset.columnid || '';\r\n const visibleColumns: Column[] = [];\r\n this._columnCheckboxes.forEach((columnCheckbox, idx) => {\r\n if (this.columns[idx].hidden !== undefined) { this.columns[idx].hidden = !columnCheckbox.checked; }\r\n if (columnCheckbox.checked) {\r\n visibleColumns.push(this.columns[idx]);\r\n }\r\n });\r\n\r\n if (!visibleColumns.length) {\r\n e.target.checked = true;\r\n return;\r\n }\r\n\r\n this.grid.setColumns(visibleColumns);\r\n this.onColumnsChanged.notify({ columnId, showing: isChecked, allColumns: this.columns, columns: this.columns, visibleColumns, grid: this.grid });\r\n }\r\n }\r\n\r\n /** @deprecated because of a typo @use `setColumnVisibility()` instead */\r\n setColumnVisibiliy(idxOrId: number | string, show: boolean) {\r\n this.setColumnVisibility(idxOrId, show);\r\n }\r\n\r\n setColumnVisibility(idxOrId: number | string, show: boolean) {\r\n const idx = typeof idxOrId === 'number' ? idxOrId : this.getColumnIndexbyId(idxOrId);\r\n let visibleColumns: Column[] = this.getVisibleColumns();\r\n const col = this.columns[idx];\r\n if (show) {\r\n col.hidden = false;\r\n visibleColumns.splice(idx, 0, col);\r\n } else {\r\n const newVisibleColumns: Column[] = [];\r\n for (let i = 0; i < visibleColumns.length; i++) {\r\n if (visibleColumns[i].id !== col.id) { newVisibleColumns.push(visibleColumns[i]); }\r\n }\r\n visibleColumns = newVisibleColumns;\r\n }\r\n\r\n this.grid.setColumns(visibleColumns);\r\n this.onColumnsChanged.notify({ columnId: col.id, showing: show, allColumns: this.columns, columns: this.columns, visibleColumns, grid: this.grid });\r\n }\r\n\r\n getAllColumns() {\r\n return this.columns;\r\n }\r\n\r\n getColumnbyId(id: number | string) {\r\n for (let i = 0; i < this.columns.length; i++) {\r\n if (this.columns[i].id === id) { return this.columns[i]; }\r\n }\r\n return null;\r\n }\r\n\r\n getColumnIndexbyId(id: number | string) {\r\n for (let i = 0; i < this.columns.length; i++) {\r\n if (this.columns[i].id === id) { return i; }\r\n }\r\n return -1;\r\n }\r\n\r\n /** visible columns, we can simply get them directly from the grid */\r\n getVisibleColumns() {\r\n return this.grid.getColumns();\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 window.Slick.Controls = window.Slick.Controls || {};\r\n window.Slick.Controls.ColumnPicker = SlickColumnMenu;\r\n}\r\n"], "mappings": "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", "names": [] } diff --git a/dist/browser/controls/slick.columnpicker.js.map b/dist/browser/controls/slick.columnpicker.js.map index 996caa5a..438f6dc9 100644 --- a/dist/browser/controls/slick.columnpicker.js.map +++ b/dist/browser/controls/slick.columnpicker.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../../../src/controls/slick.columnpicker.ts"], - "sourcesContent": ["import { BindingEventService as BindingEventService_, Event as SlickEvent_, type SlickEventData, Utils as Utils_ } from '../slick.core';\r\nimport type { Column, ColumnPickerOption, DOMMouseOrTouchEvent, GridOption, OnColumnsChangedArgs } from '../models/index';\r\nimport type { SlickGrid } from '../slick.grid';\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 SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_;\r\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\r\n\r\n/***\r\n * A control to add a Column Picker (right+click on any column header to reveal the column picker)\r\n * NOTE: this is the old 'complex' column pciker that hides columns by removing them from the grid\r\n * for a more modern version that uses the column.hidden property and is a lot simpler, use slick.columnmenu.js\r\n *\r\n * USAGE:\r\n *\r\n * Add the slick.columnpicker.(js|css) files and register it with the grid.\r\n *\r\n * Available options, by defining a columnPicker object:\r\n *\r\n * let options = {\r\n * enableCellNavigation: true,\r\n * columnPicker: {\r\n * columnTitle: \"Columns\", // default to empty string\r\n *\r\n * // the last 2 checkboxes titles\r\n * hideForceFitButton: false, // show/hide checkbox near the end \"Force Fit Columns\" (default:false)\r\n * hideSyncResizeButton: false, // show/hide checkbox near the end \"Synchronous Resize\" (default:false)\r\n * forceFitTitle: \"Force fit columns\", // default to \"Force fit columns\"\r\n * headerColumnValueExtractor: \"Extract the column label\" // default to column.name\r\n * syncResizeTitle: \"Synchronous resize\", // default to \"Synchronous resize\"\r\n * }\r\n * };\r\n */\r\n\r\nexport class SlickColumnPicker {\r\n // --\r\n // public API\r\n onColumnsChanged = new SlickEvent('onColumnsChanged');\r\n\r\n // --\r\n // protected props\r\n protected _gridUid: string;\r\n protected _columnTitleElm!: HTMLElement;\r\n protected _listElm!: HTMLElement;\r\n protected _menuElm!: HTMLElement;\r\n protected _columnCheckboxes: HTMLInputElement[] = [];\r\n protected _bindingEventService = new BindingEventService();\r\n protected _gridOptions: GridOption;\r\n protected _defaults: ColumnPickerOption = {\r\n fadeSpeed: 250,\r\n\r\n // the last 2 checkboxes titles\r\n hideForceFitButton: false,\r\n hideSyncResizeButton: false,\r\n forceFitTitle: 'Force fit columns',\r\n syncResizeTitle: 'Synchronous resize',\r\n headerColumnValueExtractor: (columnDef: Column) => Utils.getHtmlStringOutput(columnDef.name || '', 'innerHTML'),\r\n };\r\n\r\n constructor(protected columns: Column[], protected readonly grid: SlickGrid, gridOptions: GridOption) {\r\n this._gridUid = grid.getUID();\r\n this._gridOptions = Utils.extend({}, this._defaults, gridOptions);\r\n this.init(this.grid);\r\n }\r\n\r\n init(grid: SlickGrid) {\r\n Utils.addSlickEventPubSubWhenDefined(grid.getPubSubService(), this);\r\n grid.onColumnsReordered.subscribe(this.updateColumnOrder.bind(this));\r\n grid.onHeaderContextMenu.subscribe(this.handleHeaderContextMenu.bind(this));\r\n grid.onPreHeaderContextMenu.subscribe((e) => {\r\n if (['slick-column-name', 'slick-header-column'].some(className => e.target?.classList.contains(className))) {\r\n this.handleHeaderContextMenu(e); // open picker only when preheader has column groups\r\n }\r\n });\r\n\r\n this._menuElm = document.createElement('div');\r\n this._menuElm.className = `slick-columnpicker ${this._gridUid}`;\r\n this._menuElm.style.display = 'none';\r\n document.body.appendChild(this._menuElm);\r\n\r\n const buttonElm = document.createElement('button');\r\n buttonElm.type = 'button';\r\n buttonElm.className = 'close';\r\n buttonElm.dataset.dismiss = 'slick-columnpicker';\r\n buttonElm.ariaLabel = 'Close';\r\n\r\n const spanCloseElm = document.createElement('span');\r\n spanCloseElm.className = 'close';\r\n spanCloseElm.ariaHidden = 'true';\r\n spanCloseElm.textContent = '\u00D7';\r\n buttonElm.appendChild(spanCloseElm);\r\n this._menuElm.appendChild(buttonElm);\r\n\r\n // user could pass a title on top of the columns list\r\n if (this._gridOptions.columnPickerTitle || (this._gridOptions.columnPicker?.columnTitle)) {\r\n const columnTitle = this._gridOptions.columnPickerTitle || this._gridOptions.columnPicker?.columnTitle;\r\n this._columnTitleElm = document.createElement('div');\r\n this._columnTitleElm.className = 'slick-gridmenu-custom';\r\n this._columnTitleElm.textContent = columnTitle || '';\r\n this._menuElm.appendChild(this._columnTitleElm);\r\n }\r\n\r\n this._bindingEventService.bind(this._menuElm, 'click', this.updateColumn.bind(this) as EventListener);\r\n\r\n this._listElm = document.createElement('span');\r\n this._listElm.className = 'slick-columnpicker-list';\r\n\r\n // Hide the menu on outside click.\r\n this._bindingEventService.bind(document.body, 'mousedown', this.handleBodyMouseDown.bind(this) as EventListener);\r\n\r\n // destroy the picker if user leaves the page\r\n this._bindingEventService.bind(document.body, 'beforeunload', this.destroy.bind(this));\r\n }\r\n\r\n destroy() {\r\n this.grid.onPreHeaderContextMenu.unsubscribe(this.handleHeaderContextMenu.bind(this));\r\n this.grid.onHeaderContextMenu.unsubscribe(this.handleHeaderContextMenu.bind(this));\r\n this.grid.onColumnsReordered.unsubscribe(this.updateColumnOrder.bind(this));\r\n this._bindingEventService.unbindAll();\r\n this._listElm?.remove();\r\n this._menuElm?.remove();\r\n }\r\n\r\n protected handleBodyMouseDown(e: DOMMouseOrTouchEvent) {\r\n if ((this._menuElm !== e.target && !this._menuElm?.contains(e.target)) || e.target.className === 'close') {\r\n this._menuElm.setAttribute('aria-expanded', 'false');\r\n this._menuElm.style.display = 'none';\r\n }\r\n }\r\n\r\n protected handleHeaderContextMenu(e: SlickEventData) {\r\n e.preventDefault();\r\n Utils.emptyElement(this._listElm);\r\n this.updateColumnOrder();\r\n this._columnCheckboxes = [];\r\n\r\n let columnId, columnLabel, excludeCssClass;\r\n for (let i = 0; i < this.columns.length; i++) {\r\n columnId = this.columns[i].id;\r\n const colName: string = this.columns[i].name instanceof HTMLElement\r\n ? (this.columns[i].name as HTMLElement).innerHTML\r\n : (this.columns[i].name || '') as string;\r\n excludeCssClass = this.columns[i].excludeFromColumnPicker ? 'hidden' : '';\r\n\r\n const liElm = document.createElement('li');\r\n liElm.className = excludeCssClass;\r\n liElm.ariaLabel = colName;\r\n\r\n const checkboxElm = document.createElement('input');\r\n checkboxElm.type = 'checkbox';\r\n checkboxElm.id = `${this._gridUid}colpicker-${columnId}`;\r\n checkboxElm.dataset.columnid = String(this.columns[i].id);\r\n liElm.appendChild(checkboxElm);\r\n\r\n this._columnCheckboxes.push(checkboxElm);\r\n\r\n if (Utils.isDefined(this.grid.getColumnIndex(columnId)) && !this.columns[i].hidden) {\r\n checkboxElm.checked = true;\r\n }\r\n\r\n columnLabel = (this._gridOptions?.columnPicker?.headerColumnValueExtractor)\r\n ? this._gridOptions.columnPicker.headerColumnValueExtractor(this.columns[i], this._gridOptions)\r\n : this._defaults.headerColumnValueExtractor!(this.columns[i], this._gridOptions);\r\n\r\n const labelElm = document.createElement('label');\r\n labelElm.htmlFor = `${this._gridUid}colpicker-${columnId}`;\r\n this.grid.applyHtmlCode(labelElm, columnLabel);\r\n liElm.appendChild(labelElm);\r\n this._listElm.appendChild(liElm);\r\n }\r\n\r\n if (this._gridOptions.columnPicker && (!this._gridOptions.columnPicker.hideForceFitButton || !this._gridOptions.columnPicker.hideSyncResizeButton)) {\r\n this._listElm.appendChild(document.createElement('hr'));\r\n }\r\n\r\n if (!(this._gridOptions.columnPicker?.hideForceFitButton)) {\r\n const forceFitTitle = this._gridOptions.columnPicker?.forceFitTitle || this._gridOptions.forceFitTitle;\r\n\r\n const liElm = document.createElement('li');\r\n liElm.ariaLabel = forceFitTitle || '';\r\n this._listElm.appendChild(liElm);\r\n\r\n const forceFitCheckboxElm = document.createElement('input');\r\n forceFitCheckboxElm.type = 'checkbox';\r\n forceFitCheckboxElm.id = `${this._gridUid}colpicker-forcefit`;\r\n forceFitCheckboxElm.dataset.option = 'autoresize';\r\n liElm.appendChild(forceFitCheckboxElm);\r\n\r\n const labelElm = document.createElement('label');\r\n labelElm.htmlFor = `${this._gridUid}colpicker-forcefit`;\r\n labelElm.textContent = forceFitTitle || '';\r\n liElm.appendChild(labelElm);\r\n\r\n if (this.grid.getOptions().forceFitColumns) {\r\n forceFitCheckboxElm.checked = true;\r\n }\r\n }\r\n\r\n if (!(this._gridOptions.columnPicker?.hideSyncResizeButton)) {\r\n const syncResizeTitle = (this._gridOptions.columnPicker?.syncResizeTitle) || this._gridOptions.syncResizeTitle;\r\n\r\n const liElm = document.createElement('li');\r\n liElm.ariaLabel = syncResizeTitle || '';\r\n this._listElm.appendChild(liElm);\r\n\r\n const syncResizeCheckboxElm = document.createElement('input');\r\n syncResizeCheckboxElm.type = 'checkbox';\r\n syncResizeCheckboxElm.id = `${this._gridUid}colpicker-syncresize`;\r\n syncResizeCheckboxElm.dataset.option = 'syncresize';\r\n liElm.appendChild(syncResizeCheckboxElm);\r\n\r\n const labelElm = document.createElement('label');\r\n labelElm.htmlFor = `${this._gridUid}colpicker-syncresize`;\r\n labelElm.textContent = syncResizeTitle || '';\r\n liElm.appendChild(labelElm);\r\n\r\n if (this.grid.getOptions().syncColumnCellResize) {\r\n syncResizeCheckboxElm.checked = true;\r\n }\r\n }\r\n\r\n this.repositionMenu(e);\r\n }\r\n\r\n protected repositionMenu(event: DOMMouseOrTouchEvent | SlickEventData) {\r\n const targetEvent: MouseEvent | Touch = (event as TouchEvent)?.touches?.[0] ?? event;\r\n if (this._menuElm) {\r\n this._menuElm.style.display = 'block';\r\n\r\n // auto-positioned menu left/right by available position\r\n const gridPos = this.grid.getGridPosition();\r\n const menuWidth = this._menuElm.clientWidth || 0;\r\n let menuOffsetLeft = targetEvent.pageX || 0;\r\n if (gridPos?.width && (menuOffsetLeft + menuWidth >= gridPos.width)) {\r\n menuOffsetLeft = menuOffsetLeft - menuWidth;\r\n }\r\n\r\n this._menuElm.style.top = `${targetEvent.pageY - 10}px`;\r\n this._menuElm.style.left = `${menuOffsetLeft}px`;\r\n this._menuElm.style.maxHeight = `${window.innerHeight - targetEvent.clientY}px`;\r\n this._menuElm.setAttribute('aria-expanded', 'true');\r\n this._menuElm.appendChild(this._listElm);\r\n }\r\n }\r\n\r\n protected updateColumnOrder() {\r\n // Because columns can be reordered, we have to update the `columns`\r\n // to reflect the new order, however we can't just take `grid.getColumns()`,\r\n // as it does not include columns currently hidden by the picker.\r\n // We create a new `columns` structure by leaving currently-hidden\r\n // columns in their original ordinal position and interleaving the results\r\n // of the current column sort.\r\n const current = this.grid.getColumns().slice(0);\r\n const ordered = new Array(this.columns.length);\r\n for (let i = 0; i < ordered.length; i++) {\r\n if (this.grid.getColumnIndex(this.columns[i].id) === undefined) {\r\n // If the column doesn't return a value from getColumnIndex,\r\n // it is hidden. Leave it in this position.\r\n ordered[i] = this.columns[i];\r\n } else {\r\n // Otherwise, grab the next visible column.\r\n ordered[i] = current.shift();\r\n }\r\n }\r\n this.columns = ordered;\r\n }\r\n\r\n /** Update the Titles of each sections (command, customTitle, ...) */\r\n updateAllTitles(pickerOptions: { columnTitle: string; }) {\r\n this.grid.applyHtmlCode(this._columnTitleElm, pickerOptions.columnTitle);\r\n }\r\n\r\n protected updateColumn(e: DOMMouseOrTouchEvent) {\r\n if (e.target.dataset.option === 'autoresize') {\r\n // when calling setOptions, it will resize with ALL Columns (even the hidden ones)\r\n // we can avoid this problem by keeping a reference to the visibleColumns before setOptions and then setColumns after\r\n const previousVisibleColumns = this.getVisibleColumns();\r\n const isChecked: boolean = e.target.checked || false;\r\n this.grid.setOptions({ forceFitColumns: isChecked });\r\n this.grid.setColumns(previousVisibleColumns);\r\n return;\r\n }\r\n\r\n if (e.target.dataset.option === 'syncresize') {\r\n if (e.target.checked) {\r\n this.grid.setOptions({ syncColumnCellResize: true });\r\n } else {\r\n this.grid.setOptions({ syncColumnCellResize: false });\r\n }\r\n return;\r\n }\r\n\r\n if (e.target.type === 'checkbox') {\r\n const isChecked = e.target.checked;\r\n const columnId = e.target.dataset.columnid || '';\r\n const visibleColumns: Column[] = [];\r\n this._columnCheckboxes.forEach((columnCheckbox, idx) => {\r\n if (this.columns[idx].hidden !== undefined) { this.columns[idx].hidden = !columnCheckbox.checked; }\r\n if (columnCheckbox.checked) {\r\n visibleColumns.push(this.columns[idx]);\r\n }\r\n });\r\n\r\n if (!visibleColumns.length) {\r\n e.target.checked = true;\r\n return;\r\n }\r\n\r\n this.grid.setColumns(visibleColumns);\r\n this.onColumnsChanged.notify({ columnId, showing: isChecked, allColumns: this.columns, columns: this.columns, visibleColumns, grid: this.grid });\r\n }\r\n }\r\n\r\n /** @deprecated because of a typo @use `setColumnVisibility()` instead */\r\n setColumnVisibiliy(idxOrId: number | string, show: boolean) {\r\n this.setColumnVisibility(idxOrId, show);\r\n }\r\n\r\n setColumnVisibility(idxOrId: number | string, show: boolean) {\r\n const idx = typeof idxOrId === 'number' ? idxOrId : this.getColumnIndexbyId(idxOrId);\r\n let visibleColumns = this.getVisibleColumns();\r\n const col = this.columns[idx];\r\n if (show) {\r\n col.hidden = false;\r\n visibleColumns.splice(idx, 0, col);\r\n } else {\r\n const newVisibleColumns: Column[] = [];\r\n for (let i = 0; i < visibleColumns.length; i++) {\r\n if (visibleColumns[i].id !== col.id) { newVisibleColumns.push(visibleColumns[i]); }\r\n }\r\n visibleColumns = newVisibleColumns;\r\n }\r\n\r\n this.grid.setColumns(visibleColumns);\r\n this.onColumnsChanged.notify({ columnId: col.id, showing: show, allColumns: this.columns, columns: this.columns, visibleColumns, grid: this.grid });\r\n }\r\n\r\n getAllColumns() {\r\n return this.columns;\r\n }\r\n\r\n getColumnbyId(id: number | string) {\r\n for (let i = 0; i < this.columns.length; i++) {\r\n if (this.columns[i].id === id) { return this.columns[i]; }\r\n }\r\n return null;\r\n }\r\n\r\n getColumnIndexbyId(id: number | string) {\r\n for (let i = 0; i < this.columns.length; i++) {\r\n if (this.columns[i].id === id) { return i; }\r\n }\r\n return -1;\r\n }\r\n\r\n /** visible columns, we can simply get them directly from the grid */\r\n getVisibleColumns() {\r\n return this.grid.getColumns();\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 window.Slick.Controls = window.Slick.Controls || {};\r\n window.Slick.Controls.ColumnPicker = SlickColumnPicker;\r\n}\r\n"], + "sourcesContent": ["import { BindingEventService as BindingEventService_, Event as SlickEvent_, type SlickEventData, Utils as Utils_ } from '../slick.core.js';\r\nimport type { Column, ColumnPickerOption, DOMMouseOrTouchEvent, GridOption, OnColumnsChangedArgs } 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 BindingEventService = IIFE_ONLY ? Slick.BindingEventService : BindingEventService_;\r\nconst SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_;\r\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\r\n\r\n/***\r\n * A control to add a Column Picker (right+click on any column header to reveal the column picker)\r\n * NOTE: this is the old 'complex' column pciker that hides columns by removing them from the grid\r\n * for a more modern version that uses the column.hidden property and is a lot simpler, use slick.columnmenu.js\r\n *\r\n * USAGE:\r\n *\r\n * Add the slick.columnpicker.(js|css) files and register it with the grid.\r\n *\r\n * Available options, by defining a columnPicker object:\r\n *\r\n * let options = {\r\n * enableCellNavigation: true,\r\n * columnPicker: {\r\n * columnTitle: \"Columns\", // default to empty string\r\n *\r\n * // the last 2 checkboxes titles\r\n * hideForceFitButton: false, // show/hide checkbox near the end \"Force Fit Columns\" (default:false)\r\n * hideSyncResizeButton: false, // show/hide checkbox near the end \"Synchronous Resize\" (default:false)\r\n * forceFitTitle: \"Force fit columns\", // default to \"Force fit columns\"\r\n * headerColumnValueExtractor: \"Extract the column label\" // default to column.name\r\n * syncResizeTitle: \"Synchronous resize\", // default to \"Synchronous resize\"\r\n * }\r\n * };\r\n */\r\n\r\nexport class SlickColumnPicker {\r\n // --\r\n // public API\r\n onColumnsChanged = new SlickEvent('onColumnsChanged');\r\n\r\n // --\r\n // protected props\r\n protected _gridUid: string;\r\n protected _columnTitleElm!: HTMLElement;\r\n protected _listElm!: HTMLElement;\r\n protected _menuElm!: HTMLElement;\r\n protected _columnCheckboxes: HTMLInputElement[] = [];\r\n protected _bindingEventService = new BindingEventService();\r\n protected _gridOptions: GridOption;\r\n protected _defaults: ColumnPickerOption = {\r\n fadeSpeed: 250,\r\n\r\n // the last 2 checkboxes titles\r\n hideForceFitButton: false,\r\n hideSyncResizeButton: false,\r\n forceFitTitle: 'Force fit columns',\r\n syncResizeTitle: 'Synchronous resize',\r\n headerColumnValueExtractor: (columnDef: Column) => Utils.getHtmlStringOutput(columnDef.name || '', 'innerHTML'),\r\n };\r\n\r\n constructor(protected columns: Column[], protected readonly grid: SlickGrid, gridOptions: GridOption) {\r\n this._gridUid = grid.getUID();\r\n this._gridOptions = Utils.extend({}, this._defaults, gridOptions);\r\n this.init(this.grid);\r\n }\r\n\r\n init(grid: SlickGrid) {\r\n Utils.addSlickEventPubSubWhenDefined(grid.getPubSubService(), this);\r\n grid.onColumnsReordered.subscribe(this.updateColumnOrder.bind(this));\r\n grid.onHeaderContextMenu.subscribe(this.handleHeaderContextMenu.bind(this));\r\n grid.onPreHeaderContextMenu.subscribe((e) => {\r\n if (['slick-column-name', 'slick-header-column'].some(className => e.target?.classList.contains(className))) {\r\n this.handleHeaderContextMenu(e); // open picker only when preheader has column groups\r\n }\r\n });\r\n\r\n this._menuElm = document.createElement('div');\r\n this._menuElm.className = `slick-columnpicker ${this._gridUid}`;\r\n this._menuElm.style.display = 'none';\r\n document.body.appendChild(this._menuElm);\r\n\r\n const buttonElm = document.createElement('button');\r\n buttonElm.type = 'button';\r\n buttonElm.className = 'close';\r\n buttonElm.dataset.dismiss = 'slick-columnpicker';\r\n buttonElm.ariaLabel = 'Close';\r\n\r\n const spanCloseElm = document.createElement('span');\r\n spanCloseElm.className = 'close';\r\n spanCloseElm.ariaHidden = 'true';\r\n spanCloseElm.textContent = '\u00D7';\r\n buttonElm.appendChild(spanCloseElm);\r\n this._menuElm.appendChild(buttonElm);\r\n\r\n // user could pass a title on top of the columns list\r\n if (this._gridOptions.columnPickerTitle || (this._gridOptions.columnPicker?.columnTitle)) {\r\n const columnTitle = this._gridOptions.columnPickerTitle || this._gridOptions.columnPicker?.columnTitle;\r\n this._columnTitleElm = document.createElement('div');\r\n this._columnTitleElm.className = 'slick-gridmenu-custom';\r\n this._columnTitleElm.textContent = columnTitle || '';\r\n this._menuElm.appendChild(this._columnTitleElm);\r\n }\r\n\r\n this._bindingEventService.bind(this._menuElm, 'click', this.updateColumn.bind(this) as EventListener);\r\n\r\n this._listElm = document.createElement('span');\r\n this._listElm.className = 'slick-columnpicker-list';\r\n\r\n // Hide the menu on outside click.\r\n this._bindingEventService.bind(document.body, 'mousedown', this.handleBodyMouseDown.bind(this) as EventListener);\r\n\r\n // destroy the picker if user leaves the page\r\n this._bindingEventService.bind(document.body, 'beforeunload', this.destroy.bind(this));\r\n }\r\n\r\n destroy() {\r\n this.grid.onPreHeaderContextMenu.unsubscribe(this.handleHeaderContextMenu.bind(this));\r\n this.grid.onHeaderContextMenu.unsubscribe(this.handleHeaderContextMenu.bind(this));\r\n this.grid.onColumnsReordered.unsubscribe(this.updateColumnOrder.bind(this));\r\n this._bindingEventService.unbindAll();\r\n this._listElm?.remove();\r\n this._menuElm?.remove();\r\n }\r\n\r\n protected handleBodyMouseDown(e: DOMMouseOrTouchEvent) {\r\n if ((this._menuElm !== e.target && !this._menuElm?.contains(e.target)) || e.target.className === 'close') {\r\n this._menuElm.setAttribute('aria-expanded', 'false');\r\n this._menuElm.style.display = 'none';\r\n }\r\n }\r\n\r\n protected handleHeaderContextMenu(e: SlickEventData) {\r\n e.preventDefault();\r\n Utils.emptyElement(this._listElm);\r\n this.updateColumnOrder();\r\n this._columnCheckboxes = [];\r\n\r\n let columnId, columnLabel, excludeCssClass;\r\n for (let i = 0; i < this.columns.length; i++) {\r\n columnId = this.columns[i].id;\r\n const colName: string = this.columns[i].name instanceof HTMLElement\r\n ? (this.columns[i].name as HTMLElement).innerHTML\r\n : (this.columns[i].name || '') as string;\r\n excludeCssClass = this.columns[i].excludeFromColumnPicker ? 'hidden' : '';\r\n\r\n const liElm = document.createElement('li');\r\n liElm.className = excludeCssClass;\r\n liElm.ariaLabel = colName;\r\n\r\n const checkboxElm = document.createElement('input');\r\n checkboxElm.type = 'checkbox';\r\n checkboxElm.id = `${this._gridUid}colpicker-${columnId}`;\r\n checkboxElm.dataset.columnid = String(this.columns[i].id);\r\n liElm.appendChild(checkboxElm);\r\n\r\n this._columnCheckboxes.push(checkboxElm);\r\n\r\n if (Utils.isDefined(this.grid.getColumnIndex(columnId)) && !this.columns[i].hidden) {\r\n checkboxElm.checked = true;\r\n }\r\n\r\n columnLabel = (this._gridOptions?.columnPicker?.headerColumnValueExtractor)\r\n ? this._gridOptions.columnPicker.headerColumnValueExtractor(this.columns[i], this._gridOptions)\r\n : this._defaults.headerColumnValueExtractor!(this.columns[i], this._gridOptions);\r\n\r\n const labelElm = document.createElement('label');\r\n labelElm.htmlFor = `${this._gridUid}colpicker-${columnId}`;\r\n this.grid.applyHtmlCode(labelElm, columnLabel);\r\n liElm.appendChild(labelElm);\r\n this._listElm.appendChild(liElm);\r\n }\r\n\r\n if (this._gridOptions.columnPicker && (!this._gridOptions.columnPicker.hideForceFitButton || !this._gridOptions.columnPicker.hideSyncResizeButton)) {\r\n this._listElm.appendChild(document.createElement('hr'));\r\n }\r\n\r\n if (!(this._gridOptions.columnPicker?.hideForceFitButton)) {\r\n const forceFitTitle = this._gridOptions.columnPicker?.forceFitTitle || this._gridOptions.forceFitTitle;\r\n\r\n const liElm = document.createElement('li');\r\n liElm.ariaLabel = forceFitTitle || '';\r\n this._listElm.appendChild(liElm);\r\n\r\n const forceFitCheckboxElm = document.createElement('input');\r\n forceFitCheckboxElm.type = 'checkbox';\r\n forceFitCheckboxElm.id = `${this._gridUid}colpicker-forcefit`;\r\n forceFitCheckboxElm.dataset.option = 'autoresize';\r\n liElm.appendChild(forceFitCheckboxElm);\r\n\r\n const labelElm = document.createElement('label');\r\n labelElm.htmlFor = `${this._gridUid}colpicker-forcefit`;\r\n labelElm.textContent = forceFitTitle || '';\r\n liElm.appendChild(labelElm);\r\n\r\n if (this.grid.getOptions().forceFitColumns) {\r\n forceFitCheckboxElm.checked = true;\r\n }\r\n }\r\n\r\n if (!(this._gridOptions.columnPicker?.hideSyncResizeButton)) {\r\n const syncResizeTitle = (this._gridOptions.columnPicker?.syncResizeTitle) || this._gridOptions.syncResizeTitle;\r\n\r\n const liElm = document.createElement('li');\r\n liElm.ariaLabel = syncResizeTitle || '';\r\n this._listElm.appendChild(liElm);\r\n\r\n const syncResizeCheckboxElm = document.createElement('input');\r\n syncResizeCheckboxElm.type = 'checkbox';\r\n syncResizeCheckboxElm.id = `${this._gridUid}colpicker-syncresize`;\r\n syncResizeCheckboxElm.dataset.option = 'syncresize';\r\n liElm.appendChild(syncResizeCheckboxElm);\r\n\r\n const labelElm = document.createElement('label');\r\n labelElm.htmlFor = `${this._gridUid}colpicker-syncresize`;\r\n labelElm.textContent = syncResizeTitle || '';\r\n liElm.appendChild(labelElm);\r\n\r\n if (this.grid.getOptions().syncColumnCellResize) {\r\n syncResizeCheckboxElm.checked = true;\r\n }\r\n }\r\n\r\n this.repositionMenu(e);\r\n }\r\n\r\n protected repositionMenu(event: DOMMouseOrTouchEvent | SlickEventData) {\r\n const targetEvent: MouseEvent | Touch = (event as TouchEvent)?.touches?.[0] ?? event;\r\n if (this._menuElm) {\r\n this._menuElm.style.display = 'block';\r\n\r\n // auto-positioned menu left/right by available position\r\n const gridPos = this.grid.getGridPosition();\r\n const menuWidth = this._menuElm.clientWidth || 0;\r\n let menuOffsetLeft = targetEvent.pageX || 0;\r\n if (gridPos?.width && (menuOffsetLeft + menuWidth >= gridPos.width)) {\r\n menuOffsetLeft = menuOffsetLeft - menuWidth;\r\n }\r\n\r\n this._menuElm.style.top = `${targetEvent.pageY - 10}px`;\r\n this._menuElm.style.left = `${menuOffsetLeft}px`;\r\n this._menuElm.style.maxHeight = `${window.innerHeight - targetEvent.clientY}px`;\r\n this._menuElm.setAttribute('aria-expanded', 'true');\r\n this._menuElm.appendChild(this._listElm);\r\n }\r\n }\r\n\r\n protected updateColumnOrder() {\r\n // Because columns can be reordered, we have to update the `columns`\r\n // to reflect the new order, however we can't just take `grid.getColumns()`,\r\n // as it does not include columns currently hidden by the picker.\r\n // We create a new `columns` structure by leaving currently-hidden\r\n // columns in their original ordinal position and interleaving the results\r\n // of the current column sort.\r\n const current = this.grid.getColumns().slice(0);\r\n const ordered = new Array(this.columns.length);\r\n for (let i = 0; i < ordered.length; i++) {\r\n if (this.grid.getColumnIndex(this.columns[i].id) === undefined) {\r\n // If the column doesn't return a value from getColumnIndex,\r\n // it is hidden. Leave it in this position.\r\n ordered[i] = this.columns[i];\r\n } else {\r\n // Otherwise, grab the next visible column.\r\n ordered[i] = current.shift();\r\n }\r\n }\r\n this.columns = ordered;\r\n }\r\n\r\n /** Update the Titles of each sections (command, customTitle, ...) */\r\n updateAllTitles(pickerOptions: { columnTitle: string; }) {\r\n this.grid.applyHtmlCode(this._columnTitleElm, pickerOptions.columnTitle);\r\n }\r\n\r\n protected updateColumn(e: DOMMouseOrTouchEvent) {\r\n if (e.target.dataset.option === 'autoresize') {\r\n // when calling setOptions, it will resize with ALL Columns (even the hidden ones)\r\n // we can avoid this problem by keeping a reference to the visibleColumns before setOptions and then setColumns after\r\n const previousVisibleColumns = this.getVisibleColumns();\r\n const isChecked: boolean = e.target.checked || false;\r\n this.grid.setOptions({ forceFitColumns: isChecked });\r\n this.grid.setColumns(previousVisibleColumns);\r\n return;\r\n }\r\n\r\n if (e.target.dataset.option === 'syncresize') {\r\n if (e.target.checked) {\r\n this.grid.setOptions({ syncColumnCellResize: true });\r\n } else {\r\n this.grid.setOptions({ syncColumnCellResize: false });\r\n }\r\n return;\r\n }\r\n\r\n if (e.target.type === 'checkbox') {\r\n const isChecked = e.target.checked;\r\n const columnId = e.target.dataset.columnid || '';\r\n const visibleColumns: Column[] = [];\r\n this._columnCheckboxes.forEach((columnCheckbox, idx) => {\r\n if (this.columns[idx].hidden !== undefined) { this.columns[idx].hidden = !columnCheckbox.checked; }\r\n if (columnCheckbox.checked) {\r\n visibleColumns.push(this.columns[idx]);\r\n }\r\n });\r\n\r\n if (!visibleColumns.length) {\r\n e.target.checked = true;\r\n return;\r\n }\r\n\r\n this.grid.setColumns(visibleColumns);\r\n this.onColumnsChanged.notify({ columnId, showing: isChecked, allColumns: this.columns, columns: this.columns, visibleColumns, grid: this.grid });\r\n }\r\n }\r\n\r\n /** @deprecated because of a typo @use `setColumnVisibility()` instead */\r\n setColumnVisibiliy(idxOrId: number | string, show: boolean) {\r\n this.setColumnVisibility(idxOrId, show);\r\n }\r\n\r\n setColumnVisibility(idxOrId: number | string, show: boolean) {\r\n const idx = typeof idxOrId === 'number' ? idxOrId : this.getColumnIndexbyId(idxOrId);\r\n let visibleColumns = this.getVisibleColumns();\r\n const col = this.columns[idx];\r\n if (show) {\r\n col.hidden = false;\r\n visibleColumns.splice(idx, 0, col);\r\n } else {\r\n const newVisibleColumns: Column[] = [];\r\n for (let i = 0; i < visibleColumns.length; i++) {\r\n if (visibleColumns[i].id !== col.id) { newVisibleColumns.push(visibleColumns[i]); }\r\n }\r\n visibleColumns = newVisibleColumns;\r\n }\r\n\r\n this.grid.setColumns(visibleColumns);\r\n this.onColumnsChanged.notify({ columnId: col.id, showing: show, allColumns: this.columns, columns: this.columns, visibleColumns, grid: this.grid });\r\n }\r\n\r\n getAllColumns() {\r\n return this.columns;\r\n }\r\n\r\n getColumnbyId(id: number | string) {\r\n for (let i = 0; i < this.columns.length; i++) {\r\n if (this.columns[i].id === id) { return this.columns[i]; }\r\n }\r\n return null;\r\n }\r\n\r\n getColumnIndexbyId(id: number | string) {\r\n for (let i = 0; i < this.columns.length; i++) {\r\n if (this.columns[i].id === id) { return i; }\r\n }\r\n return -1;\r\n }\r\n\r\n /** visible columns, we can simply get them directly from the grid */\r\n getVisibleColumns() {\r\n return this.grid.getColumns();\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 window.Slick.Controls = window.Slick.Controls || {};\r\n window.Slick.Controls.ColumnPicker = SlickColumnPicker;\r\n}\r\n"], "mappings": "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", "names": ["_a"] } diff --git a/dist/browser/controls/slick.gridmenu.js.map b/dist/browser/controls/slick.gridmenu.js.map index 2e482461..e259dcda 100644 --- a/dist/browser/controls/slick.gridmenu.js.map +++ b/dist/browser/controls/slick.gridmenu.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../../../src/controls/slick.gridmenu.ts"], - "sourcesContent": ["import type {\r\n Column,\r\n DOMMouseOrTouchEvent,\r\n GridMenuCommandItemCallbackArgs,\r\n GridMenuEventWithElementCallbackArgs,\r\n GridMenuItem,\r\n GridMenuOption,\r\n GridOption,\r\n MenuCommandItem,\r\n onGridMenuColumnsChangedCallbackArgs\r\n} from '../models/index';\r\nimport { BindingEventService as BindingEventService_, SlickEvent as SlickEvent_, Utils as Utils_ } from '../slick.core';\r\nimport type { SlickGrid } from '../slick.grid';\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 SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_;\r\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\r\n\r\n/**\r\n * A control to add a Grid Menu (hambuger menu on top-right of the grid)\r\n *\r\n * USAGE:\r\n *\r\n * Add the slick.gridmenu.(js|css) files and register it with the grid.\r\n *\r\n * To specify a menu in a column header, extend the column definition like so:\r\n * let gridMenuControl = new Slick.Controls.GridMenu(columns, grid, options);\r\n *\r\n * Available grid options, by defining a gridMenu object:\r\n *\r\n * let options = {\r\n * enableCellNavigation: true,\r\n * gridMenu: {\r\n * commandTitle: \"Command List\", // default to empty string\r\n * columnTitle: \"Columns\", // default to empty string\r\n * iconImage: \"some-image.png\", // this is the Grid Menu icon (hamburger icon)\r\n * iconCssClass: \"fa fa-bars\", // you can provide iconImage OR iconCssClass\r\n * leaveOpen: false, // do we want to leave the Grid Menu open after a command execution? (false by default)\r\n * menuWidth: 18, // width (icon) that will be use to resize the column header container (18 by default)\r\n * contentMinWidth: 0,\t\t\t\t\t\t\t // defaults to 0 (auto), minimum width of grid menu content (command, column list)\r\n * marginBottom: 15, // defaults to 15, margin to use at the bottom of the grid when using max-height (default)\r\n * resizeOnShowHeaderRow: false, // false by default\r\n * showButton: true, // true by default - it allows the user to control if the\r\n * // default gridMenu button (located on the top right corner by default CSS)\r\n * // should be created or omitted\r\n * useClickToRepositionMenu: true, // true by default\r\n *\r\n * // the last 2 checkboxes titles\r\n * hideForceFitButton: false, // show/hide checkbox near the end \"Force Fit Columns\"\r\n * hideSyncResizeButton: false, // show/hide checkbox near the end \"Synchronous Resize\"\r\n * forceFitTitle: \"Force fit columns\", // default to \"Force fit columns\"\r\n * syncResizeTitle: \"Synchronous resize\", // default to \"Synchronous resize\"\r\n *\r\n * commandItems: [\r\n * {\r\n * // command menu item options\r\n * },\r\n * {\r\n * // command menu item options\r\n * }\r\n * ]\r\n * }\r\n * };\r\n *\r\n *\r\n * Available menu options:\r\n * hideForceFitButton: Hide the \"Force fit columns\" button (defaults to false)\r\n * hideSyncResizeButton: Hide the \"Synchronous resize\" button (defaults to false)\r\n * forceFitTitle: Text of the title \"Force fit columns\"\r\n * contentMinWidth:\t\t\t\t\t\tminimum width of grid menu content (command, column list), defaults to 0 (auto)\r\n * height: Height of the Grid Menu content, when provided it will be used instead of the max-height (defaults to undefined)\r\n * menuWidth: Grid menu button width (defaults to 18)\r\n * resizeOnShowHeaderRow: Do we want to resize on the show header row event\r\n * syncResizeTitle: Text of the title \"Synchronous resize\"\r\n * useClickToRepositionMenu: Use the Click offset to reposition the Grid Menu (defaults to true), when set to False it will use the icon offset to reposition the grid menu\r\n * menuUsabilityOverride: Callback method that user can override the default behavior of enabling/disabling the menu from being usable (must be combined with a custom formatter)\r\n * marginBottom: Margin to use at the bottom of the grid menu, only in effect when height is undefined (defaults to 15)\r\n * subItemChevronClass: CSS class that can be added on the right side of a sub-item parent (typically a chevron-right icon)\r\n * subMenuOpenByEvent: defaults to \"mouseover\", what event type shoud we use to open sub-menu(s), 2 options are available: \"mouseover\" or \"click\"\r\n *\r\n * Available command menu item options:\r\n * action: Optionally define a callback function that gets executed when item is chosen (and/or use the onCommand event)\r\n * title: Menu item text.\r\n * divider: Whether the current item is a divider, not an actual command.\r\n * disabled: Whether the item/command is disabled.\r\n * hidden: Whether the item/command is hidden.\r\n * tooltip: Item tooltip.\r\n * command: A command identifier to be passed to the onCommand event handlers.\r\n * cssClass: A CSS class to be added to the menu item container.\r\n * iconCssClass: A CSS class to be added to the menu item icon.\r\n * iconImage: A url to the icon image.\r\n * textCssClass: A CSS class to be added to the menu item text.\r\n * subMenuTitle: Optional sub-menu title that will shows up when sub-menu commmands/options list is opened\r\n * subMenuTitleCssClass: Optional sub-menu title CSS class to use with `subMenuTitle`\r\n * itemVisibilityOverride: Callback method that user can override the default behavior of showing/hiding an item from the list\r\n * itemUsabilityOverride: Callback method that user can override the default behavior of enabling/disabling an item from the list\r\n *\r\n *\r\n * The plugin exposes the following events:\r\n *\r\n * onAfterMenuShow: Fired after the menu is shown. You can customize the menu or dismiss it by returning false.\r\n * * ONLY works with a JS event (as per slick.core code), so we cannot notify when it's a button event (when grid menu is attached to an external button, not the hamburger menu)\r\n * Event args:\r\n * grid: Reference to the grid.\r\n * column: Column definition.\r\n * menu: Menu options. Note that you can change the menu items here.\r\n *\r\n * onBeforeMenuShow: Fired before the menu is shown. You can customize the menu or dismiss it by returning false.\r\n * * ONLY works with a JS event (as per slick.core code), so we cannot notify when it's a button event (when grid menu is attached to an external button, not the hamburger menu)\r\n * Event args:\r\n * grid: Reference to the grid.\r\n * column: Column definition.\r\n * menu: Menu options. Note that you can change the menu items here.\r\n *\r\n * onMenuClose: Fired when the menu is closing.\r\n * Event args:\r\n * grid: Reference to the grid.\r\n * column: Column definition.\r\n * menu: Menu options. Note that you can change the menu items here.\r\n *\r\n * onCommand: Fired on menu item click for buttons with 'command' specified.\r\n * Event args:\r\n * grid: Reference to the grid.\r\n * column: Column definition.\r\n * command: Button command identified.\r\n * button: Button options. Note that you can change the button options in your\r\n * event handler, and the column header will be automatically updated to\r\n * reflect them. This is useful if you want to implement something like a\r\n * toggle button.\r\n */\r\n\r\nexport class SlickGridMenu {\r\n // --\r\n // public API\r\n onAfterMenuShow = new SlickEvent('onAfterMenuShow');\r\n onBeforeMenuShow = new SlickEvent('onBeforeMenuShow');\r\n onMenuClose = new SlickEvent('onMenuClose');\r\n onCommand = new SlickEvent('onCommand');\r\n onColumnsChanged = new SlickEvent('onColumnsChanged');\r\n\r\n // --\r\n // protected props\r\n protected _bindingEventService: BindingEventService_;\r\n protected _gridOptions: GridOption;\r\n protected _gridUid: string;\r\n protected _isMenuOpen = false;\r\n protected _columnCheckboxes: HTMLInputElement[] = [];\r\n protected _columnTitleElm!: HTMLElement;\r\n protected _commandTitleElm!: HTMLElement;\r\n protected _commandListElm!: HTMLDivElement;\r\n protected _headerElm: HTMLDivElement | null = null;\r\n protected _listElm!: HTMLElement;\r\n protected _buttonElm!: HTMLElement;\r\n protected _menuElm!: HTMLElement;\r\n protected _subMenuParentId = '';\r\n protected _gridMenuOptions: GridMenuOption | null = null;\r\n protected _defaults: GridMenuOption = {\r\n showButton: true,\r\n hideForceFitButton: false,\r\n hideSyncResizeButton: false,\r\n forceFitTitle: 'Force fit columns',\r\n marginBottom: 15,\r\n menuWidth: 18,\r\n contentMinWidth: 0,\r\n resizeOnShowHeaderRow: false,\r\n subMenuOpenByEvent: 'mouseover',\r\n syncResizeTitle: 'Synchronous resize',\r\n useClickToRepositionMenu: true,\r\n headerColumnValueExtractor: (columnDef: Column) => Utils.getHtmlStringOutput(columnDef.name || '', 'innerHTML'),\r\n };\r\n\r\n constructor(protected columns: Column[], protected readonly grid: SlickGrid, gridOptions: GridOption) {\r\n this._gridUid = grid.getUID();\r\n this._gridOptions = gridOptions;\r\n this._gridMenuOptions = Utils.extend({}, this._defaults, gridOptions.gridMenu);\r\n this._bindingEventService = new BindingEventService();\r\n\r\n // when a grid optionally changes from a regular grid to a frozen grid, we need to destroy & recreate the grid menu\r\n // we do this change because the Grid Menu is on the left container for a regular grid, it is however on the right container for a frozen grid\r\n grid.onSetOptions.subscribe((_e, args) => {\r\n if (args && args.optionsBefore && args.optionsAfter) {\r\n const switchedFromRegularToFrozen = args.optionsBefore.frozenColumn! >= 0 && args.optionsAfter.frozenColumn === -1;\r\n const switchedFromFrozenToRegular = args.optionsBefore.frozenColumn === -1 && args.optionsAfter.frozenColumn! >= 0;\r\n if (switchedFromRegularToFrozen || switchedFromFrozenToRegular) {\r\n this.recreateGridMenu();\r\n }\r\n }\r\n });\r\n this.init(this.grid);\r\n }\r\n\r\n init(grid: SlickGrid) {\r\n this._gridOptions = grid.getOptions();\r\n Utils.addSlickEventPubSubWhenDefined(grid.getPubSubService(), this);\r\n this.createGridMenu();\r\n\r\n if (this._gridMenuOptions?.customItems || this._gridMenuOptions?.customTitle) {\r\n console.warn('[SlickGrid] Grid Menu \"customItems\" and \"customTitle\" were deprecated to align with other Menu plugins, please use \"commandItems\" and \"commandTitle\" instead.');\r\n }\r\n\r\n // subscribe to the grid, when it's destroyed, we should also destroy the Grid Menu\r\n grid.onBeforeDestroy.subscribe(this.destroy.bind(this));\r\n }\r\n\r\n setOptions(newOptions: GridMenuOption) {\r\n this._gridMenuOptions = Utils.extend({}, this._gridMenuOptions, newOptions);\r\n }\r\n\r\n protected createGridMenu() {\r\n const gridMenuWidth = (this._gridMenuOptions?.menuWidth) || this._defaults.menuWidth;\r\n if (this._gridOptions && this._gridOptions.hasOwnProperty('frozenColumn') && this._gridOptions.frozenColumn! >= 0) {\r\n this._headerElm = document.querySelector(`.${this._gridUid} .slick-header-right`);\r\n } else {\r\n this._headerElm = document.querySelector(`.${this._gridUid} .slick-header-left`);\r\n }\r\n this._headerElm!.style.width = `calc(100% - ${gridMenuWidth}px)`;\r\n\r\n // if header row is enabled, we need to resize its width also\r\n const enableResizeHeaderRow = (Utils.isDefined(this._gridMenuOptions?.resizeOnShowHeaderRow)) ? this._gridMenuOptions!.resizeOnShowHeaderRow : this._defaults.resizeOnShowHeaderRow;\r\n if (enableResizeHeaderRow && this._gridOptions.showHeaderRow) {\r\n const headerRow = document.querySelector(`.${this._gridUid}.slick-headerrow`);\r\n if (headerRow) {\r\n headerRow.style.width = `calc(100% - ${gridMenuWidth}px)`;\r\n }\r\n }\r\n\r\n const showButton = (this._gridMenuOptions?.showButton !== undefined) ? this._gridMenuOptions.showButton : this._defaults.showButton;\r\n if (showButton) {\r\n this._buttonElm = document.createElement('button');\r\n this._buttonElm.className = 'slick-gridmenu-button';\r\n this._buttonElm.ariaLabel = 'Grid Menu';\r\n\r\n if (this._gridMenuOptions?.iconCssClass) {\r\n this._buttonElm.classList.add(...Utils.classNameToList(this._gridMenuOptions.iconCssClass));\r\n } else {\r\n const iconImageElm = document.createElement('img');\r\n iconImageElm.src = (this._gridMenuOptions?.iconImage) ? this._gridMenuOptions.iconImage : '../images/drag-handle.png';\r\n this._buttonElm.appendChild(iconImageElm);\r\n }\r\n\r\n // add the grid menu button in the preheader (when exists) or always in the column header (default)\r\n const buttonContainerTarget = this._gridMenuOptions?.iconButtonContainer === 'preheader' ? 'firstChild' : 'lastChild';\r\n this._headerElm!.parentElement!.insertBefore(this._buttonElm, this._headerElm!.parentElement![buttonContainerTarget]);\r\n\r\n // add on click handler for the Grid Menu itself\r\n this._bindingEventService.bind(this._buttonElm, 'click', this.showGridMenu.bind(this) as EventListener);\r\n }\r\n\r\n this._menuElm = this.createMenu(0);\r\n this.populateColumnPicker();\r\n document.body.appendChild(this._menuElm);\r\n\r\n // Hide the menu on outside click.\r\n this._bindingEventService.bind(document.body, 'mousedown', this.handleBodyMouseDown.bind(this) as EventListener);\r\n\r\n // destroy the picker if user leaves the page\r\n this._bindingEventService.bind(document.body, 'beforeunload', this.destroy.bind(this));\r\n }\r\n\r\n /** Create the menu or sub-menu(s) but without the column picker which is a separate single process */\r\n createMenu(level = 0, item?: GridMenuItem | MenuCommandItem | 'divider') {\r\n // create a new cell menu\r\n const maxHeight = isNaN(this._gridMenuOptions?.maxHeight as number) ? this._gridMenuOptions?.maxHeight : `${this._gridMenuOptions?.maxHeight ?? 0}px`;\r\n const width = isNaN(this._gridMenuOptions?.width as number) ? this._gridMenuOptions?.width : `${this._gridMenuOptions?.maxWidth ?? 0}px`;\r\n\r\n // to avoid having multiple sub-menu trees opened,\r\n // we need to somehow keep trace of which parent menu the tree belongs to\r\n // and we should keep ref of only the first sub-menu parent, we can use the command name (remove any whitespaces though)\r\n const subMenuCommand = (item as GridMenuItem)?.command;\r\n let subMenuId = (level === 1 && subMenuCommand) ? subMenuCommand.replaceAll(' ', '') : '';\r\n if (subMenuId) {\r\n this._subMenuParentId = subMenuId;\r\n }\r\n if (level > 1) {\r\n subMenuId = this._subMenuParentId;\r\n }\r\n\r\n const menuClasses = `slick-gridmenu slick-menu-level-${level} ${this._gridUid}`;\r\n const bodyMenuElm = document.body.querySelector(`.slick-gridmenu.slick-menu-level-${level}${this.getGridUidSelector()}`);\r\n\r\n // return menu/sub-menu if it's already opened unless we are on different sub-menu tree if so close them all\r\n if (bodyMenuElm) {\r\n if (bodyMenuElm.dataset.subMenuParent === subMenuId) {\r\n return bodyMenuElm;\r\n }\r\n this.destroySubMenus();\r\n }\r\n\r\n const menuElm = document.createElement('div');\r\n menuElm.role = 'menu';\r\n menuElm.className = menuClasses;\r\n if (level > 0) {\r\n menuElm.classList.add('slick-submenu');\r\n if (subMenuId) {\r\n menuElm.dataset.subMenuParent = subMenuId;\r\n }\r\n }\r\n menuElm.ariaLabel = level > 1 ? 'SubMenu' : 'Grid Menu';\r\n\r\n if (width) {\r\n menuElm.style.width = width as string;\r\n }\r\n if (maxHeight) {\r\n menuElm.style.maxHeight = maxHeight as string;\r\n }\r\n\r\n menuElm.style.display = 'none';\r\n\r\n let closeButtonElm: HTMLButtonElement | null = null;\r\n if (level === 0) {\r\n closeButtonElm = document.createElement('button');\r\n closeButtonElm.type = 'button';\r\n closeButtonElm.className = 'close';\r\n closeButtonElm.dataset.dismiss = 'slick-gridmenu';\r\n closeButtonElm.ariaLabel = 'Close';\r\n\r\n const spanCloseElm = document.createElement('span');\r\n spanCloseElm.className = 'close';\r\n spanCloseElm.ariaHidden = 'true';\r\n spanCloseElm.textContent = '\u00D7';\r\n closeButtonElm.appendChild(spanCloseElm);\r\n menuElm.appendChild(closeButtonElm);\r\n }\r\n\r\n // -- Command List section\r\n this._commandListElm = document.createElement('div');\r\n this._commandListElm.className = `slick-gridmenu-custom slick-gridmenu-command-list slick-menu-level-${level}`;\r\n this._commandListElm.role = 'menu';\r\n menuElm.appendChild(this._commandListElm);\r\n\r\n const commandItems =\r\n (item as GridMenuItem)?.commandItems\r\n ?? (item as GridMenuItem)?.customItems\r\n ?? this._gridMenuOptions?.commandItems\r\n ?? this._gridMenuOptions?.customItems\r\n ?? [];\r\n\r\n if (commandItems.length > 0) {\r\n\r\n // when creating sub-menu add its sub-menu title when exists\r\n if (item && level > 0) {\r\n this.addSubMenuTitleWhenExists(item, this._commandListElm); // add sub-menu title when exists\r\n }\r\n }\r\n this.populateCommandsMenu(commandItems, this._commandListElm, { grid: this.grid, level });\r\n\r\n // increment level for possible next sub-menus if exists\r\n level++;\r\n\r\n return menuElm;\r\n }\r\n\r\n /** Destroy the plugin by unsubscribing every events & also delete the menu DOM elements */\r\n destroy() {\r\n this.onAfterMenuShow.unsubscribe();\r\n this.onBeforeMenuShow.unsubscribe();\r\n this.onMenuClose.unsubscribe();\r\n this.onCommand.unsubscribe();\r\n this.onColumnsChanged.unsubscribe();\r\n this.grid.onColumnsReordered.unsubscribe(this.updateColumnOrder.bind(this));\r\n this.grid.onBeforeDestroy.unsubscribe();\r\n this.grid.onSetOptions.unsubscribe();\r\n this._bindingEventService.unbindAll();\r\n this._menuElm?.remove();\r\n this.deleteMenu();\r\n }\r\n\r\n /** Delete the menu DOM element but without unsubscribing any events */\r\n deleteMenu() {\r\n this._bindingEventService.unbindAll();\r\n const gridMenuElm = document.querySelector(`div.slick-gridmenu.${this._gridUid}`);\r\n if (gridMenuElm) {\r\n gridMenuElm.style.display = 'none';\r\n }\r\n if (this._headerElm) {\r\n // put back original width (fixes width and frozen+gridMenu on left header)\r\n this._headerElm.style.width = '100%';\r\n }\r\n this._buttonElm?.remove();\r\n this._menuElm?.remove();\r\n }\r\n\r\n /** Close and destroy all previously opened sub-menus */\r\n destroySubMenus() {\r\n this._bindingEventService.unbindAll('sub-menu');\r\n document.querySelectorAll(`.slick-gridmenu.slick-submenu${this.getGridUidSelector()}`)\r\n .forEach(subElm => subElm.remove());\r\n }\r\n\r\n /** Construct the custom command menu items. */\r\n protected populateCommandsMenu(commandItems: Array, commandListElm: HTMLElement, args: { grid: SlickGrid, level: number }) {\r\n // user could pass a title on top of the custom section\r\n const level = args?.level || 0;\r\n const isSubMenu = level > 0;\r\n if (!isSubMenu && (this._gridMenuOptions?.commandTitle || this._gridMenuOptions?.customTitle)) {\r\n this._commandTitleElm = document.createElement('div');\r\n this._commandTitleElm.className = 'title';\r\n this.grid.applyHtmlCode(this._commandTitleElm, this.grid.sanitizeHtmlString((this._gridMenuOptions.commandTitle || this._gridMenuOptions.customTitle) as string));\r\n commandListElm.appendChild(this._commandTitleElm);\r\n }\r\n\r\n for (let i = 0, ln = commandItems.length; i < ln; i++) {\r\n let addClickListener = true;\r\n const item = commandItems[i];\r\n const callbackArgs = {\r\n grid: this.grid,\r\n menu: this._menuElm,\r\n columns: this.columns,\r\n visibleColumns: this.getVisibleColumns()\r\n };\r\n\r\n // run each override functions to know if the item is visible and usable\r\n const isItemVisible = this.runOverrideFunctionWhenExists((item as GridMenuItem).itemVisibilityOverride, callbackArgs);\r\n const isItemUsable = this.runOverrideFunctionWhenExists((item as GridMenuItem).itemUsabilityOverride, callbackArgs);\r\n\r\n // if the result is not visible then there's no need to go further\r\n if (!isItemVisible) {\r\n continue;\r\n }\r\n\r\n // when the override is defined, we need to use its result to update the disabled property\r\n // so that \"handleMenuItemClick\" has the correct flag and won't trigger a command clicked event\r\n if (Object.prototype.hasOwnProperty.call(item, 'itemUsabilityOverride')) {\r\n (item as GridMenuItem).disabled = isItemUsable ? false : true;\r\n }\r\n\r\n const liElm = document.createElement('div');\r\n liElm.className = 'slick-gridmenu-item';\r\n liElm.role = 'menuitem';\r\n\r\n if ((item as GridMenuItem).divider || item === 'divider') {\r\n liElm.classList.add('slick-gridmenu-item-divider');\r\n addClickListener = false;\r\n }\r\n if ((item as GridMenuItem).disabled) {\r\n liElm.classList.add('slick-gridmenu-item-disabled');\r\n }\r\n\r\n if ((item as GridMenuItem).hidden) {\r\n liElm.classList.add('slick-gridmenu-item-hidden');\r\n }\r\n\r\n if ((item as GridMenuItem).cssClass) {\r\n liElm.classList.add(...Utils.classNameToList((item as GridMenuItem).cssClass));\r\n }\r\n\r\n if ((item as GridMenuItem).tooltip) {\r\n liElm.title = (item as GridMenuItem).tooltip || '';\r\n }\r\n\r\n const iconElm = document.createElement('div');\r\n iconElm.className = 'slick-gridmenu-icon';\r\n\r\n liElm.appendChild(iconElm);\r\n\r\n if ((item as GridMenuItem).iconCssClass) {\r\n iconElm.classList.add(...Utils.classNameToList((item as GridMenuItem).iconCssClass));\r\n }\r\n\r\n if ((item as GridMenuItem).iconImage) {\r\n iconElm.style.backgroundImage = `url(${(item as GridMenuItem).iconImage})`;\r\n }\r\n\r\n const textElm = document.createElement('span');\r\n textElm.className = 'slick-gridmenu-content';\r\n this.grid.applyHtmlCode(textElm, this.grid.sanitizeHtmlString((item as GridMenuItem).title || ''));\r\n\r\n liElm.appendChild(textElm);\r\n\r\n if ((item as GridMenuItem).textCssClass) {\r\n textElm.classList.add(...Utils.classNameToList((item as GridMenuItem).textCssClass));\r\n }\r\n\r\n commandListElm.appendChild(liElm);\r\n\r\n if (addClickListener) {\r\n const eventGroup = isSubMenu ? 'sub-menu' : 'parent-menu';\r\n this._bindingEventService.bind(liElm, 'click', this.handleMenuItemClick.bind(this, item, level) as EventListener, undefined, eventGroup);\r\n }\r\n\r\n // optionally open sub-menu(s) by mouseover\r\n if (this._gridMenuOptions?.subMenuOpenByEvent === 'mouseover') {\r\n this._bindingEventService.bind(liElm, 'mouseover', ((e: DOMMouseOrTouchEvent) => {\r\n if ((item as GridMenuItem).commandItems || (item as GridMenuItem).customItems) {\r\n this.repositionSubMenu(item, level, e);\r\n } else if (!isSubMenu) {\r\n this.destroySubMenus();\r\n }\r\n }) as EventListener);\r\n }\r\n\r\n // the option/command item could be a sub-menu if it has another list of commands/options\r\n if ((item as GridMenuItem).commandItems || (item as GridMenuItem).customItems) {\r\n const chevronElm = document.createElement('span');\r\n chevronElm.className = 'sub-item-chevron';\r\n if (this._gridMenuOptions?.subItemChevronClass) {\r\n chevronElm.classList.add(...Utils.classNameToList(this._gridMenuOptions.subItemChevronClass));\r\n } else {\r\n chevronElm.textContent = '\u2B9E'; // \u2B9E or \u25B8\r\n }\r\n\r\n liElm.classList.add('slick-submenu-item');\r\n liElm.appendChild(chevronElm);\r\n continue;\r\n }\r\n }\r\n }\r\n\r\n /** Build the column picker, the code comes almost untouched from the file \"slick.columnpicker.js\" */\r\n protected populateColumnPicker() {\r\n this.grid.onColumnsReordered.subscribe(this.updateColumnOrder.bind(this));\r\n\r\n // user could pass a title on top of the columns list\r\n if (this._gridMenuOptions?.columnTitle) {\r\n this._columnTitleElm = document.createElement('div');\r\n this._columnTitleElm.className = 'title';\r\n this.grid.applyHtmlCode(this._columnTitleElm, this.grid.sanitizeHtmlString(this._gridMenuOptions.columnTitle));\r\n this._menuElm.appendChild(this._columnTitleElm);\r\n }\r\n\r\n this._bindingEventService.bind(this._menuElm, 'click', this.updateColumn.bind(this) as EventListener);\r\n this._listElm = document.createElement('span');\r\n this._listElm.className = 'slick-gridmenu-list';\r\n this._listElm.role = 'menu';\r\n }\r\n\r\n /** Delete and then Recreate the Grid Menu (for example when we switch from regular to a frozen grid) */\r\n recreateGridMenu() {\r\n this.deleteMenu();\r\n this.init(this.grid);\r\n }\r\n\r\n showGridMenu(e: DOMMouseOrTouchEvent) {\r\n const targetEvent = e.touches ? e.touches[0] : e;\r\n e.preventDefault();\r\n\r\n // empty both the picker list & the command list\r\n Utils.emptyElement(this._listElm);\r\n Utils.emptyElement(this._commandListElm);\r\n\r\n const commandItems = this._gridMenuOptions?.commandItems ?? this._gridMenuOptions?.customItems ?? [];\r\n this.populateCommandsMenu(commandItems, this._commandListElm, { grid: this.grid, level: 0 });\r\n this.updateColumnOrder();\r\n this._columnCheckboxes = [];\r\n\r\n const callbackArgs = {\r\n grid: this.grid,\r\n menu: this._menuElm,\r\n allColumns: this.columns,\r\n visibleColumns: this.getVisibleColumns()\r\n };\r\n\r\n // run the override function (when defined), if the result is false it won't go further\r\n if (this._gridMenuOptions && !this.runOverrideFunctionWhenExists(this._gridMenuOptions.menuUsabilityOverride, callbackArgs)) {\r\n return;\r\n }\r\n\r\n // notify of the onBeforeMenuShow only works when\r\n // this mean that we cannot notify when the grid menu is attach to a button event\r\n if (typeof e.stopPropagation === 'function') {\r\n if (this.onBeforeMenuShow.notify(callbackArgs, e, this).getReturnValue() === false) {\r\n return;\r\n }\r\n }\r\n\r\n let columnId, columnLabel, excludeCssClass;\r\n for (let i = 0; i < this.columns.length; i++) {\r\n columnId = this.columns[i].id;\r\n excludeCssClass = this.columns[i].excludeFromGridMenu ? 'hidden' : '';\r\n const colName: string = this.columns[i].name instanceof HTMLElement\r\n ? (this.columns[i].name as HTMLElement).innerHTML\r\n : (this.columns[i].name || '') as string;\r\n\r\n const liElm = document.createElement('li');\r\n liElm.className = excludeCssClass;\r\n liElm.ariaLabel = colName;\r\n\r\n const checkboxElm = document.createElement('input');\r\n checkboxElm.type = 'checkbox';\r\n checkboxElm.id = `${this._gridUid}-gridmenu-colpicker-${columnId}`;\r\n checkboxElm.dataset.columnid = String(this.columns[i].id);\r\n liElm.appendChild(checkboxElm);\r\n\r\n if (Utils.isDefined(this.grid.getColumnIndex(this.columns[i].id)) && !this.columns[i].hidden) {\r\n checkboxElm.checked = true;\r\n }\r\n\r\n this._columnCheckboxes.push(checkboxElm);\r\n\r\n // get the column label from the picker value extractor (user can optionally provide a custom extractor)\r\n columnLabel = (this._gridMenuOptions?.headerColumnValueExtractor)\r\n ? this._gridMenuOptions.headerColumnValueExtractor(this.columns[i], this._gridOptions)\r\n : this._defaults.headerColumnValueExtractor!(this.columns[i]);\r\n\r\n const labelElm = document.createElement('label');\r\n labelElm.htmlFor = `${this._gridUid}-gridmenu-colpicker-${columnId}`;\r\n this.grid.applyHtmlCode(labelElm, this.grid.sanitizeHtmlString(Utils.getHtmlStringOutput(columnLabel || '')));\r\n liElm.appendChild(labelElm);\r\n this._listElm.appendChild(liElm);\r\n }\r\n\r\n if (this._gridMenuOptions && (!this._gridMenuOptions.hideForceFitButton || !this._gridMenuOptions.hideSyncResizeButton)) {\r\n this._listElm.appendChild(document.createElement('hr'));\r\n }\r\n\r\n if (!(this._gridMenuOptions?.hideForceFitButton)) {\r\n const forceFitTitle = (this._gridMenuOptions?.forceFitTitle) || this._defaults.forceFitTitle as string;\r\n\r\n const liElm = document.createElement('li');\r\n liElm.ariaLabel = forceFitTitle;\r\n liElm.role = 'menuitem';\r\n this._listElm.appendChild(liElm);\r\n\r\n const forceFitCheckboxElm = document.createElement('input');\r\n forceFitCheckboxElm.type = 'checkbox';\r\n forceFitCheckboxElm.id = `${this._gridUid}-gridmenu-colpicker-forcefit`;\r\n forceFitCheckboxElm.dataset.option = 'autoresize';\r\n liElm.appendChild(forceFitCheckboxElm);\r\n\r\n const labelElm = document.createElement('label');\r\n labelElm.htmlFor = `${this._gridUid}-gridmenu-colpicker-forcefit`;\r\n labelElm.textContent = forceFitTitle;\r\n liElm.appendChild(labelElm);\r\n\r\n if (this.grid.getOptions().forceFitColumns) {\r\n forceFitCheckboxElm.checked = true;\r\n }\r\n }\r\n\r\n if (!(this._gridMenuOptions?.hideSyncResizeButton)) {\r\n const syncResizeTitle = (this._gridMenuOptions?.syncResizeTitle) || this._defaults.syncResizeTitle as string;\r\n\r\n const liElm = document.createElement('li');\r\n liElm.ariaLabel = syncResizeTitle;\r\n this._listElm.appendChild(liElm);\r\n\r\n const syncResizeCheckboxElm = document.createElement('input');\r\n syncResizeCheckboxElm.type = 'checkbox';\r\n syncResizeCheckboxElm.id = `${this._gridUid}-gridmenu-colpicker-syncresize`;\r\n syncResizeCheckboxElm.dataset.option = 'syncresize';\r\n liElm.appendChild(syncResizeCheckboxElm);\r\n\r\n const labelElm = document.createElement('label');\r\n labelElm.htmlFor = `${this._gridUid}-gridmenu-colpicker-syncresize`;\r\n labelElm.textContent = syncResizeTitle;\r\n liElm.appendChild(labelElm);\r\n\r\n if (this.grid.getOptions().syncColumnCellResize) {\r\n syncResizeCheckboxElm.checked = true;\r\n }\r\n }\r\n\r\n let buttonElm = (e.target.nodeName === 'BUTTON' ? e.target : e.target.querySelector('button')) as HTMLButtonElement; // get button element\r\n if (!buttonElm) {\r\n buttonElm = e.target.parentElement as HTMLButtonElement; // external grid menu might fall in this last case if wrapped in a span/div\r\n }\r\n\r\n // we need to display the menu to properly calculate its width but we can however make it invisible\r\n this._menuElm.style.display = 'block';\r\n this._menuElm.style.opacity = '0';\r\n\r\n this.repositionMenu(e, this._menuElm, buttonElm);\r\n\r\n // set \"height\" when defined OR ELSE use the \"max-height\" with available window size and optional margin bottom\r\n const menuMarginBottom = (this._gridMenuOptions?.marginBottom !== undefined) ? this._gridMenuOptions.marginBottom : this._defaults.marginBottom as number;\r\n if (this._gridMenuOptions?.height !== undefined) {\r\n this._menuElm.style.height = `${this._gridMenuOptions.height}px`;\r\n } else {\r\n this._menuElm.style.maxHeight = `${window.innerHeight - targetEvent.clientY - menuMarginBottom}px`;\r\n }\r\n\r\n this._menuElm.style.display = 'block';\r\n this._menuElm.style.opacity = '1'; // restore menu visibility\r\n this._menuElm.appendChild(this._listElm);\r\n this._isMenuOpen = true;\r\n\r\n if (typeof e.stopPropagation === 'function') {\r\n if (this.onAfterMenuShow.notify(callbackArgs, e, this).getReturnValue() === false) {\r\n return;\r\n }\r\n }\r\n }\r\n\r\n protected getGridUidSelector() {\r\n const gridUid = this.grid.getUID() || '';\r\n return gridUid ? `.${gridUid}` : '';\r\n }\r\n\r\n protected handleBodyMouseDown(e: DOMMouseOrTouchEvent) {\r\n // did we click inside the menu or any of its sub-menu(s)\r\n let isMenuClicked = false;\r\n if (this._menuElm?.contains(e.target)) {\r\n isMenuClicked = true;\r\n }\r\n if (!isMenuClicked) {\r\n document\r\n .querySelectorAll(`.slick-gridmenu.slick-submenu${this.getGridUidSelector()}`)\r\n .forEach(subElm => {\r\n if (subElm.contains(e.target)) {\r\n isMenuClicked = true;\r\n }\r\n });\r\n }\r\n\r\n if ((this._menuElm !== e.target && !isMenuClicked && !e.defaultPrevented && this._isMenuOpen) || e.target.className === 'close') {\r\n this.hideMenu(e);\r\n }\r\n }\r\n\r\n protected handleMenuItemClick(item: GridMenuItem | MenuCommandItem | 'divider', level = 0, e: DOMMouseOrTouchEvent) {\r\n if (item !== 'divider' && !item.disabled && !item.divider) {\r\n const command = item.command || '';\r\n\r\n if (Utils.isDefined(command) && !item.commandItems && !(item as GridMenuItem).customItems) {\r\n const callbackArgs: GridMenuCommandItemCallbackArgs = {\r\n grid: this.grid,\r\n command,\r\n item,\r\n allColumns: this.columns,\r\n visibleColumns: this.getVisibleColumns()\r\n };\r\n this.onCommand.notify(callbackArgs, e, this);\r\n\r\n // execute action callback when defined\r\n if (typeof item.action === 'function') {\r\n (item as MenuCommandItem).action!.call(this, e, callbackArgs);\r\n }\r\n\r\n // does the user want to leave open the Grid Menu after executing a command?\r\n const leaveOpen = !!(this._gridMenuOptions?.leaveOpen);\r\n if (!leaveOpen && !e.defaultPrevented) {\r\n this.hideMenu(e);\r\n }\r\n\r\n // Stop propagation so that it doesn't register as a header click event.\r\n e.preventDefault();\r\n e.stopPropagation();\r\n } else if (item.commandItems || (item as GridMenuItem).customItems) {\r\n this.repositionSubMenu(item, level, e);\r\n } else {\r\n this.destroySubMenus();\r\n }\r\n }\r\n }\r\n\r\n hideMenu(e: DOMMouseOrTouchEvent) {\r\n if (this._menuElm) {\r\n const callbackArgs = {\r\n grid: this.grid,\r\n menu: this._menuElm,\r\n allColumns: this.columns,\r\n visibleColumns: this.getVisibleColumns()\r\n };\r\n if (this._isMenuOpen && this.onMenuClose.notify(callbackArgs, e, this).getReturnValue() === false) {\r\n return;\r\n }\r\n this._isMenuOpen = false;\r\n Utils.hide(this._menuElm);\r\n }\r\n this.destroySubMenus();\r\n }\r\n\r\n /** Update the Titles of each sections (command, commandTitle, ...) */\r\n updateAllTitles(gridMenuOptions: GridMenuOption) {\r\n if (this._commandTitleElm) {\r\n this.grid.applyHtmlCode(this._commandTitleElm, this.grid.sanitizeHtmlString(gridMenuOptions.commandTitle || gridMenuOptions.customTitle || ''));\r\n }\r\n if (this._columnTitleElm) {\r\n this.grid.applyHtmlCode(this._columnTitleElm, this.grid.sanitizeHtmlString(gridMenuOptions.columnTitle || ''));\r\n }\r\n }\r\n\r\n protected addSubMenuTitleWhenExists(item: GridMenuItem | MenuCommandItem | 'divider', commandOrOptionMenu: HTMLDivElement) {\r\n if (item !== 'divider' && item?.subMenuTitle) {\r\n const subMenuTitleElm = document.createElement('div');\r\n subMenuTitleElm.className = 'slick-menu-title';\r\n subMenuTitleElm.textContent = item.subMenuTitle as string;\r\n const subMenuTitleClass = item.subMenuTitleCssClass as string;\r\n if (subMenuTitleClass) {\r\n subMenuTitleElm.classList.add(...Utils.classNameToList(subMenuTitleClass));\r\n }\r\n\r\n commandOrOptionMenu.appendChild(subMenuTitleElm);\r\n }\r\n }\r\n\r\n protected repositionSubMenu(item: GridMenuItem | MenuCommandItem | 'divider', level: number, e: DOMMouseOrTouchEvent) {\r\n // when we're clicking a grid cell OR our last menu type (command/option) differs then we know that we need to start fresh and close any sub-menus that might still be open\r\n if (e.target.classList.contains('slick-cell')) {\r\n this.destroySubMenus();\r\n }\r\n\r\n // creating sub-menu, we'll also pass level & the item object since we might have \"subMenuTitle\" to show\r\n const subMenuElm = this.createMenu(level + 1, item);\r\n subMenuElm.style.display = 'block';\r\n document.body.appendChild(subMenuElm);\r\n this.repositionMenu(e, subMenuElm);\r\n }\r\n\r\n /**\r\n * Reposition the menu drop (up/down) and the side (left/right)\r\n * @param {*} event\r\n */\r\n protected repositionMenu(e: DOMMouseOrTouchEvent, menuElm: HTMLElement, buttonElm?: HTMLButtonElement) {\r\n const targetEvent = e.touches ? e.touches[0] : e;\r\n const isSubMenu = menuElm.classList.contains('slick-submenu');\r\n const parentElm = isSubMenu\r\n ? e.target.closest('.slick-gridmenu-item') as HTMLDivElement\r\n : targetEvent.target as HTMLElement;\r\n\r\n const menuIconOffset = Utils.offset(buttonElm || this._buttonElm); // get button offset position\r\n const menuWidth = menuElm.offsetWidth;\r\n const useClickToRepositionMenu = (this._gridMenuOptions?.useClickToRepositionMenu !== undefined) ? this._gridMenuOptions.useClickToRepositionMenu : this._defaults.useClickToRepositionMenu;\r\n const contentMinWidth = (this._gridMenuOptions?.contentMinWidth) ? this._gridMenuOptions.contentMinWidth : this._defaults.contentMinWidth as number;\r\n const currentMenuWidth = (contentMinWidth > menuWidth) ? contentMinWidth : menuWidth + 5;\r\n let menuOffsetTop = (useClickToRepositionMenu && targetEvent.pageY > 0) ? targetEvent.pageY : menuIconOffset!.top + 10;\r\n let menuOffsetLeft = (useClickToRepositionMenu && targetEvent.pageX > 0) ? targetEvent.pageX : menuIconOffset!.left + 10;\r\n\r\n if (isSubMenu && parentElm) {\r\n const parentOffset = Utils.offset(parentElm);\r\n menuOffsetLeft = parentOffset?.left ?? 0;\r\n menuOffsetTop = parentOffset?.top ?? 0;\r\n const gridPos = this.grid.getGridPosition();\r\n let subMenuPosCalc = menuOffsetLeft + Number(menuWidth); // calculate coordinate at caller element far right\r\n if (isSubMenu) {\r\n subMenuPosCalc += parentElm.clientWidth;\r\n }\r\n const browserWidth = document.documentElement.clientWidth;\r\n const dropSide = (subMenuPosCalc >= gridPos.width || subMenuPosCalc >= browserWidth) ? 'left' : 'right';\r\n if (dropSide === 'left') {\r\n menuElm.classList.remove('dropright');\r\n menuElm.classList.add('dropleft');\r\n menuOffsetLeft -= menuWidth;\r\n } else {\r\n menuElm.classList.remove('dropleft');\r\n menuElm.classList.add('dropright');\r\n if (isSubMenu) {\r\n menuOffsetLeft += parentElm.offsetWidth;\r\n }\r\n }\r\n } else {\r\n menuOffsetTop += 10;\r\n menuOffsetLeft = menuOffsetLeft - currentMenuWidth + 10;\r\n }\r\n\r\n menuElm.style.top = `${menuOffsetTop}px`;\r\n menuElm.style.left = `${menuOffsetLeft}px`;\r\n\r\n if (contentMinWidth > 0) {\r\n this._menuElm.style.minWidth = `${contentMinWidth}px`;\r\n }\r\n }\r\n\r\n protected updateColumnOrder() {\r\n // Because columns can be reordered, we have to update the `columns`\r\n // to reflect the new order, however we can't just take `grid.getColumns()`,\r\n // as it does not include columns currently hidden by the picker.\r\n // We create a new `columns` structure by leaving currently-hidden\r\n // columns in their original ordinal position and interleaving the results\r\n // of the current column sort.\r\n const current = this.grid.getColumns().slice(0);\r\n const ordered = new Array(this.columns.length);\r\n for (let i = 0; i < ordered.length; i++) {\r\n if (this.grid.getColumnIndex(this.columns[i].id) === undefined) {\r\n // If the column doesn't return a value from getColumnIndex,\r\n // it is hidden. Leave it in this position.\r\n ordered[i] = this.columns[i];\r\n } else {\r\n // Otherwise, grab the next visible column.\r\n ordered[i] = current.shift();\r\n }\r\n }\r\n this.columns = ordered;\r\n }\r\n\r\n protected updateColumn(e: DOMMouseOrTouchEvent) {\r\n if (e.target.dataset.option === 'autoresize') {\r\n // when calling setOptions, it will resize with ALL Columns (even the hidden ones)\r\n // we can avoid this problem by keeping a reference to the visibleColumns before setOptions and then setColumns after\r\n const previousVisibleColumns = this.getVisibleColumns();\r\n const isChecked = e.target.checked;\r\n this.grid.setOptions({ forceFitColumns: isChecked });\r\n this.grid.setColumns(previousVisibleColumns);\r\n return;\r\n }\r\n\r\n if (e.target.dataset.option === 'syncresize') {\r\n this.grid.setOptions({ syncColumnCellResize: !!(e.target.checked) });\r\n return;\r\n }\r\n\r\n if (e.target.type === 'checkbox') {\r\n const isChecked = e.target.checked;\r\n const columnId = e.target.dataset.columnid || '';\r\n const visibleColumns: Column[] = [];\r\n this._columnCheckboxes.forEach((columnCheckbox, idx) => {\r\n if (columnCheckbox.checked) {\r\n if (this.columns[idx].hidden) { this.columns[idx].hidden = false; }\r\n visibleColumns.push(this.columns[idx]);\r\n }\r\n });\r\n\r\n if (!visibleColumns.length) {\r\n e.target.checked = true;\r\n return;\r\n }\r\n\r\n const callbackArgs = {\r\n columnId,\r\n showing: isChecked,\r\n grid: this.grid,\r\n allColumns: this.columns,\r\n columns: visibleColumns,\r\n visibleColumns: this.getVisibleColumns()\r\n };\r\n this.grid.setColumns(visibleColumns);\r\n this.onColumnsChanged.notify(callbackArgs, e, this);\r\n }\r\n }\r\n\r\n getAllColumns() {\r\n return this.columns;\r\n }\r\n\r\n /** visible columns, we can simply get them directly from the grid */\r\n getVisibleColumns() {\r\n return this.grid.getColumns();\r\n }\r\n\r\n /**\r\n * Method that user can pass to override the default behavior.\r\n * In order word, user can choose or an item is (usable/visible/enable) by providing his own logic.\r\n * @param overrideFn: override function callback\r\n * @param args: multiple arguments provided to the override (cell, row, columnDef, dataContext, grid)\r\n */\r\n protected runOverrideFunctionWhenExists(overrideFn: ((args: any) => boolean) | undefined, args: T): boolean {\r\n if (typeof overrideFn === 'function') {\r\n return overrideFn.call(this, args);\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 window.Slick.Controls = window.Slick.Controls || {};\r\n window.Slick.Controls.GridMenu = SlickGridMenu;\r\n}\r\n"], + "sourcesContent": ["import type {\r\n Column,\r\n DOMMouseOrTouchEvent,\r\n GridMenuCommandItemCallbackArgs,\r\n GridMenuEventWithElementCallbackArgs,\r\n GridMenuItem,\r\n GridMenuOption,\r\n GridOption,\r\n MenuCommandItem,\r\n onGridMenuColumnsChangedCallbackArgs\r\n} from '../models/index.js';\r\nimport { BindingEventService as BindingEventService_, SlickEvent as SlickEvent_, Utils as Utils_ } 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 BindingEventService = IIFE_ONLY ? Slick.BindingEventService : BindingEventService_;\r\nconst SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_;\r\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\r\n\r\n/**\r\n * A control to add a Grid Menu (hambuger menu on top-right of the grid)\r\n *\r\n * USAGE:\r\n *\r\n * Add the slick.gridmenu.(js|css) files and register it with the grid.\r\n *\r\n * To specify a menu in a column header, extend the column definition like so:\r\n * let gridMenuControl = new Slick.Controls.GridMenu(columns, grid, options);\r\n *\r\n * Available grid options, by defining a gridMenu object:\r\n *\r\n * let options = {\r\n * enableCellNavigation: true,\r\n * gridMenu: {\r\n * commandTitle: \"Command List\", // default to empty string\r\n * columnTitle: \"Columns\", // default to empty string\r\n * iconImage: \"some-image.png\", // this is the Grid Menu icon (hamburger icon)\r\n * iconCssClass: \"fa fa-bars\", // you can provide iconImage OR iconCssClass\r\n * leaveOpen: false, // do we want to leave the Grid Menu open after a command execution? (false by default)\r\n * menuWidth: 18, // width (icon) that will be use to resize the column header container (18 by default)\r\n * contentMinWidth: 0,\t\t\t\t\t\t\t // defaults to 0 (auto), minimum width of grid menu content (command, column list)\r\n * marginBottom: 15, // defaults to 15, margin to use at the bottom of the grid when using max-height (default)\r\n * resizeOnShowHeaderRow: false, // false by default\r\n * showButton: true, // true by default - it allows the user to control if the\r\n * // default gridMenu button (located on the top right corner by default CSS)\r\n * // should be created or omitted\r\n * useClickToRepositionMenu: true, // true by default\r\n *\r\n * // the last 2 checkboxes titles\r\n * hideForceFitButton: false, // show/hide checkbox near the end \"Force Fit Columns\"\r\n * hideSyncResizeButton: false, // show/hide checkbox near the end \"Synchronous Resize\"\r\n * forceFitTitle: \"Force fit columns\", // default to \"Force fit columns\"\r\n * syncResizeTitle: \"Synchronous resize\", // default to \"Synchronous resize\"\r\n *\r\n * commandItems: [\r\n * {\r\n * // command menu item options\r\n * },\r\n * {\r\n * // command menu item options\r\n * }\r\n * ]\r\n * }\r\n * };\r\n *\r\n *\r\n * Available menu options:\r\n * hideForceFitButton: Hide the \"Force fit columns\" button (defaults to false)\r\n * hideSyncResizeButton: Hide the \"Synchronous resize\" button (defaults to false)\r\n * forceFitTitle: Text of the title \"Force fit columns\"\r\n * contentMinWidth:\t\t\t\t\t\tminimum width of grid menu content (command, column list), defaults to 0 (auto)\r\n * height: Height of the Grid Menu content, when provided it will be used instead of the max-height (defaults to undefined)\r\n * menuWidth: Grid menu button width (defaults to 18)\r\n * resizeOnShowHeaderRow: Do we want to resize on the show header row event\r\n * syncResizeTitle: Text of the title \"Synchronous resize\"\r\n * useClickToRepositionMenu: Use the Click offset to reposition the Grid Menu (defaults to true), when set to False it will use the icon offset to reposition the grid menu\r\n * menuUsabilityOverride: Callback method that user can override the default behavior of enabling/disabling the menu from being usable (must be combined with a custom formatter)\r\n * marginBottom: Margin to use at the bottom of the grid menu, only in effect when height is undefined (defaults to 15)\r\n * subItemChevronClass: CSS class that can be added on the right side of a sub-item parent (typically a chevron-right icon)\r\n * subMenuOpenByEvent: defaults to \"mouseover\", what event type shoud we use to open sub-menu(s), 2 options are available: \"mouseover\" or \"click\"\r\n *\r\n * Available command menu item options:\r\n * action: Optionally define a callback function that gets executed when item is chosen (and/or use the onCommand event)\r\n * title: Menu item text.\r\n * divider: Whether the current item is a divider, not an actual command.\r\n * disabled: Whether the item/command is disabled.\r\n * hidden: Whether the item/command is hidden.\r\n * tooltip: Item tooltip.\r\n * command: A command identifier to be passed to the onCommand event handlers.\r\n * cssClass: A CSS class to be added to the menu item container.\r\n * iconCssClass: A CSS class to be added to the menu item icon.\r\n * iconImage: A url to the icon image.\r\n * textCssClass: A CSS class to be added to the menu item text.\r\n * subMenuTitle: Optional sub-menu title that will shows up when sub-menu commmands/options list is opened\r\n * subMenuTitleCssClass: Optional sub-menu title CSS class to use with `subMenuTitle`\r\n * itemVisibilityOverride: Callback method that user can override the default behavior of showing/hiding an item from the list\r\n * itemUsabilityOverride: Callback method that user can override the default behavior of enabling/disabling an item from the list\r\n *\r\n *\r\n * The plugin exposes the following events:\r\n *\r\n * onAfterMenuShow: Fired after the menu is shown. You can customize the menu or dismiss it by returning false.\r\n * * ONLY works with a JS event (as per slick.core code), so we cannot notify when it's a button event (when grid menu is attached to an external button, not the hamburger menu)\r\n * Event args:\r\n * grid: Reference to the grid.\r\n * column: Column definition.\r\n * menu: Menu options. Note that you can change the menu items here.\r\n *\r\n * onBeforeMenuShow: Fired before the menu is shown. You can customize the menu or dismiss it by returning false.\r\n * * ONLY works with a JS event (as per slick.core code), so we cannot notify when it's a button event (when grid menu is attached to an external button, not the hamburger menu)\r\n * Event args:\r\n * grid: Reference to the grid.\r\n * column: Column definition.\r\n * menu: Menu options. Note that you can change the menu items here.\r\n *\r\n * onMenuClose: Fired when the menu is closing.\r\n * Event args:\r\n * grid: Reference to the grid.\r\n * column: Column definition.\r\n * menu: Menu options. Note that you can change the menu items here.\r\n *\r\n * onCommand: Fired on menu item click for buttons with 'command' specified.\r\n * Event args:\r\n * grid: Reference to the grid.\r\n * column: Column definition.\r\n * command: Button command identified.\r\n * button: Button options. Note that you can change the button options in your\r\n * event handler, and the column header will be automatically updated to\r\n * reflect them. This is useful if you want to implement something like a\r\n * toggle button.\r\n */\r\n\r\nexport class SlickGridMenu {\r\n // --\r\n // public API\r\n onAfterMenuShow = new SlickEvent('onAfterMenuShow');\r\n onBeforeMenuShow = new SlickEvent('onBeforeMenuShow');\r\n onMenuClose = new SlickEvent('onMenuClose');\r\n onCommand = new SlickEvent('onCommand');\r\n onColumnsChanged = new SlickEvent('onColumnsChanged');\r\n\r\n // --\r\n // protected props\r\n protected _bindingEventService: BindingEventService_;\r\n protected _gridOptions: GridOption;\r\n protected _gridUid: string;\r\n protected _isMenuOpen = false;\r\n protected _columnCheckboxes: HTMLInputElement[] = [];\r\n protected _columnTitleElm!: HTMLElement;\r\n protected _commandTitleElm!: HTMLElement;\r\n protected _commandListElm!: HTMLDivElement;\r\n protected _headerElm: HTMLDivElement | null = null;\r\n protected _listElm!: HTMLElement;\r\n protected _buttonElm!: HTMLElement;\r\n protected _menuElm!: HTMLElement;\r\n protected _subMenuParentId = '';\r\n protected _gridMenuOptions: GridMenuOption | null = null;\r\n protected _defaults: GridMenuOption = {\r\n showButton: true,\r\n hideForceFitButton: false,\r\n hideSyncResizeButton: false,\r\n forceFitTitle: 'Force fit columns',\r\n marginBottom: 15,\r\n menuWidth: 18,\r\n contentMinWidth: 0,\r\n resizeOnShowHeaderRow: false,\r\n subMenuOpenByEvent: 'mouseover',\r\n syncResizeTitle: 'Synchronous resize',\r\n useClickToRepositionMenu: true,\r\n headerColumnValueExtractor: (columnDef: Column) => Utils.getHtmlStringOutput(columnDef.name || '', 'innerHTML'),\r\n };\r\n\r\n constructor(protected columns: Column[], protected readonly grid: SlickGrid, gridOptions: GridOption) {\r\n this._gridUid = grid.getUID();\r\n this._gridOptions = gridOptions;\r\n this._gridMenuOptions = Utils.extend({}, this._defaults, gridOptions.gridMenu);\r\n this._bindingEventService = new BindingEventService();\r\n\r\n // when a grid optionally changes from a regular grid to a frozen grid, we need to destroy & recreate the grid menu\r\n // we do this change because the Grid Menu is on the left container for a regular grid, it is however on the right container for a frozen grid\r\n grid.onSetOptions.subscribe((_e, args) => {\r\n if (args && args.optionsBefore && args.optionsAfter) {\r\n const switchedFromRegularToFrozen = args.optionsBefore.frozenColumn! >= 0 && args.optionsAfter.frozenColumn === -1;\r\n const switchedFromFrozenToRegular = args.optionsBefore.frozenColumn === -1 && args.optionsAfter.frozenColumn! >= 0;\r\n if (switchedFromRegularToFrozen || switchedFromFrozenToRegular) {\r\n this.recreateGridMenu();\r\n }\r\n }\r\n });\r\n this.init(this.grid);\r\n }\r\n\r\n init(grid: SlickGrid) {\r\n this._gridOptions = grid.getOptions();\r\n Utils.addSlickEventPubSubWhenDefined(grid.getPubSubService(), this);\r\n this.createGridMenu();\r\n\r\n if (this._gridMenuOptions?.customItems || this._gridMenuOptions?.customTitle) {\r\n console.warn('[SlickGrid] Grid Menu \"customItems\" and \"customTitle\" were deprecated to align with other Menu plugins, please use \"commandItems\" and \"commandTitle\" instead.');\r\n }\r\n\r\n // subscribe to the grid, when it's destroyed, we should also destroy the Grid Menu\r\n grid.onBeforeDestroy.subscribe(this.destroy.bind(this));\r\n }\r\n\r\n setOptions(newOptions: GridMenuOption) {\r\n this._gridMenuOptions = Utils.extend({}, this._gridMenuOptions, newOptions);\r\n }\r\n\r\n protected createGridMenu() {\r\n const gridMenuWidth = (this._gridMenuOptions?.menuWidth) || this._defaults.menuWidth;\r\n if (this._gridOptions && this._gridOptions.hasOwnProperty('frozenColumn') && this._gridOptions.frozenColumn! >= 0) {\r\n this._headerElm = document.querySelector(`.${this._gridUid} .slick-header-right`);\r\n } else {\r\n this._headerElm = document.querySelector(`.${this._gridUid} .slick-header-left`);\r\n }\r\n this._headerElm!.style.width = `calc(100% - ${gridMenuWidth}px)`;\r\n\r\n // if header row is enabled, we need to resize its width also\r\n const enableResizeHeaderRow = (Utils.isDefined(this._gridMenuOptions?.resizeOnShowHeaderRow)) ? this._gridMenuOptions!.resizeOnShowHeaderRow : this._defaults.resizeOnShowHeaderRow;\r\n if (enableResizeHeaderRow && this._gridOptions.showHeaderRow) {\r\n const headerRow = document.querySelector(`.${this._gridUid}.slick-headerrow`);\r\n if (headerRow) {\r\n headerRow.style.width = `calc(100% - ${gridMenuWidth}px)`;\r\n }\r\n }\r\n\r\n const showButton = (this._gridMenuOptions?.showButton !== undefined) ? this._gridMenuOptions.showButton : this._defaults.showButton;\r\n if (showButton) {\r\n this._buttonElm = document.createElement('button');\r\n this._buttonElm.className = 'slick-gridmenu-button';\r\n this._buttonElm.ariaLabel = 'Grid Menu';\r\n\r\n if (this._gridMenuOptions?.iconCssClass) {\r\n this._buttonElm.classList.add(...Utils.classNameToList(this._gridMenuOptions.iconCssClass));\r\n } else {\r\n const iconImageElm = document.createElement('img');\r\n iconImageElm.src = (this._gridMenuOptions?.iconImage) ? this._gridMenuOptions.iconImage : '../images/drag-handle.png';\r\n this._buttonElm.appendChild(iconImageElm);\r\n }\r\n\r\n // add the grid menu button in the preheader (when exists) or always in the column header (default)\r\n const buttonContainerTarget = this._gridMenuOptions?.iconButtonContainer === 'preheader' ? 'firstChild' : 'lastChild';\r\n this._headerElm!.parentElement!.insertBefore(this._buttonElm, this._headerElm!.parentElement![buttonContainerTarget]);\r\n\r\n // add on click handler for the Grid Menu itself\r\n this._bindingEventService.bind(this._buttonElm, 'click', this.showGridMenu.bind(this) as EventListener);\r\n }\r\n\r\n this._menuElm = this.createMenu(0);\r\n this.populateColumnPicker();\r\n document.body.appendChild(this._menuElm);\r\n\r\n // Hide the menu on outside click.\r\n this._bindingEventService.bind(document.body, 'mousedown', this.handleBodyMouseDown.bind(this) as EventListener);\r\n\r\n // destroy the picker if user leaves the page\r\n this._bindingEventService.bind(document.body, 'beforeunload', this.destroy.bind(this));\r\n }\r\n\r\n /** Create the menu or sub-menu(s) but without the column picker which is a separate single process */\r\n createMenu(level = 0, item?: GridMenuItem | MenuCommandItem | 'divider') {\r\n // create a new cell menu\r\n const maxHeight = isNaN(this._gridMenuOptions?.maxHeight as number) ? this._gridMenuOptions?.maxHeight : `${this._gridMenuOptions?.maxHeight ?? 0}px`;\r\n const width = isNaN(this._gridMenuOptions?.width as number) ? this._gridMenuOptions?.width : `${this._gridMenuOptions?.maxWidth ?? 0}px`;\r\n\r\n // to avoid having multiple sub-menu trees opened,\r\n // we need to somehow keep trace of which parent menu the tree belongs to\r\n // and we should keep ref of only the first sub-menu parent, we can use the command name (remove any whitespaces though)\r\n const subMenuCommand = (item as GridMenuItem)?.command;\r\n let subMenuId = (level === 1 && subMenuCommand) ? subMenuCommand.replaceAll(' ', '') : '';\r\n if (subMenuId) {\r\n this._subMenuParentId = subMenuId;\r\n }\r\n if (level > 1) {\r\n subMenuId = this._subMenuParentId;\r\n }\r\n\r\n const menuClasses = `slick-gridmenu slick-menu-level-${level} ${this._gridUid}`;\r\n const bodyMenuElm = document.body.querySelector(`.slick-gridmenu.slick-menu-level-${level}${this.getGridUidSelector()}`);\r\n\r\n // return menu/sub-menu if it's already opened unless we are on different sub-menu tree if so close them all\r\n if (bodyMenuElm) {\r\n if (bodyMenuElm.dataset.subMenuParent === subMenuId) {\r\n return bodyMenuElm;\r\n }\r\n this.destroySubMenus();\r\n }\r\n\r\n const menuElm = document.createElement('div');\r\n menuElm.role = 'menu';\r\n menuElm.className = menuClasses;\r\n if (level > 0) {\r\n menuElm.classList.add('slick-submenu');\r\n if (subMenuId) {\r\n menuElm.dataset.subMenuParent = subMenuId;\r\n }\r\n }\r\n menuElm.ariaLabel = level > 1 ? 'SubMenu' : 'Grid Menu';\r\n\r\n if (width) {\r\n menuElm.style.width = width as string;\r\n }\r\n if (maxHeight) {\r\n menuElm.style.maxHeight = maxHeight as string;\r\n }\r\n\r\n menuElm.style.display = 'none';\r\n\r\n let closeButtonElm: HTMLButtonElement | null = null;\r\n if (level === 0) {\r\n closeButtonElm = document.createElement('button');\r\n closeButtonElm.type = 'button';\r\n closeButtonElm.className = 'close';\r\n closeButtonElm.dataset.dismiss = 'slick-gridmenu';\r\n closeButtonElm.ariaLabel = 'Close';\r\n\r\n const spanCloseElm = document.createElement('span');\r\n spanCloseElm.className = 'close';\r\n spanCloseElm.ariaHidden = 'true';\r\n spanCloseElm.textContent = '\u00D7';\r\n closeButtonElm.appendChild(spanCloseElm);\r\n menuElm.appendChild(closeButtonElm);\r\n }\r\n\r\n // -- Command List section\r\n this._commandListElm = document.createElement('div');\r\n this._commandListElm.className = `slick-gridmenu-custom slick-gridmenu-command-list slick-menu-level-${level}`;\r\n this._commandListElm.role = 'menu';\r\n menuElm.appendChild(this._commandListElm);\r\n\r\n const commandItems =\r\n (item as GridMenuItem)?.commandItems\r\n ?? (item as GridMenuItem)?.customItems\r\n ?? this._gridMenuOptions?.commandItems\r\n ?? this._gridMenuOptions?.customItems\r\n ?? [];\r\n\r\n if (commandItems.length > 0) {\r\n\r\n // when creating sub-menu add its sub-menu title when exists\r\n if (item && level > 0) {\r\n this.addSubMenuTitleWhenExists(item, this._commandListElm); // add sub-menu title when exists\r\n }\r\n }\r\n this.populateCommandsMenu(commandItems, this._commandListElm, { grid: this.grid, level });\r\n\r\n // increment level for possible next sub-menus if exists\r\n level++;\r\n\r\n return menuElm;\r\n }\r\n\r\n /** Destroy the plugin by unsubscribing every events & also delete the menu DOM elements */\r\n destroy() {\r\n this.onAfterMenuShow.unsubscribe();\r\n this.onBeforeMenuShow.unsubscribe();\r\n this.onMenuClose.unsubscribe();\r\n this.onCommand.unsubscribe();\r\n this.onColumnsChanged.unsubscribe();\r\n this.grid.onColumnsReordered.unsubscribe(this.updateColumnOrder.bind(this));\r\n this.grid.onBeforeDestroy.unsubscribe();\r\n this.grid.onSetOptions.unsubscribe();\r\n this._bindingEventService.unbindAll();\r\n this._menuElm?.remove();\r\n this.deleteMenu();\r\n }\r\n\r\n /** Delete the menu DOM element but without unsubscribing any events */\r\n deleteMenu() {\r\n this._bindingEventService.unbindAll();\r\n const gridMenuElm = document.querySelector(`div.slick-gridmenu.${this._gridUid}`);\r\n if (gridMenuElm) {\r\n gridMenuElm.style.display = 'none';\r\n }\r\n if (this._headerElm) {\r\n // put back original width (fixes width and frozen+gridMenu on left header)\r\n this._headerElm.style.width = '100%';\r\n }\r\n this._buttonElm?.remove();\r\n this._menuElm?.remove();\r\n }\r\n\r\n /** Close and destroy all previously opened sub-menus */\r\n destroySubMenus() {\r\n this._bindingEventService.unbindAll('sub-menu');\r\n document.querySelectorAll(`.slick-gridmenu.slick-submenu${this.getGridUidSelector()}`)\r\n .forEach(subElm => subElm.remove());\r\n }\r\n\r\n /** Construct the custom command menu items. */\r\n protected populateCommandsMenu(commandItems: Array, commandListElm: HTMLElement, args: { grid: SlickGrid, level: number }) {\r\n // user could pass a title on top of the custom section\r\n const level = args?.level || 0;\r\n const isSubMenu = level > 0;\r\n if (!isSubMenu && (this._gridMenuOptions?.commandTitle || this._gridMenuOptions?.customTitle)) {\r\n this._commandTitleElm = document.createElement('div');\r\n this._commandTitleElm.className = 'title';\r\n this.grid.applyHtmlCode(this._commandTitleElm, this.grid.sanitizeHtmlString((this._gridMenuOptions.commandTitle || this._gridMenuOptions.customTitle) as string));\r\n commandListElm.appendChild(this._commandTitleElm);\r\n }\r\n\r\n for (let i = 0, ln = commandItems.length; i < ln; i++) {\r\n let addClickListener = true;\r\n const item = commandItems[i];\r\n const callbackArgs = {\r\n grid: this.grid,\r\n menu: this._menuElm,\r\n columns: this.columns,\r\n visibleColumns: this.getVisibleColumns()\r\n };\r\n\r\n // run each override functions to know if the item is visible and usable\r\n const isItemVisible = this.runOverrideFunctionWhenExists((item as GridMenuItem).itemVisibilityOverride, callbackArgs);\r\n const isItemUsable = this.runOverrideFunctionWhenExists((item as GridMenuItem).itemUsabilityOverride, callbackArgs);\r\n\r\n // if the result is not visible then there's no need to go further\r\n if (!isItemVisible) {\r\n continue;\r\n }\r\n\r\n // when the override is defined, we need to use its result to update the disabled property\r\n // so that \"handleMenuItemClick\" has the correct flag and won't trigger a command clicked event\r\n if (Object.prototype.hasOwnProperty.call(item, 'itemUsabilityOverride')) {\r\n (item as GridMenuItem).disabled = isItemUsable ? false : true;\r\n }\r\n\r\n const liElm = document.createElement('div');\r\n liElm.className = 'slick-gridmenu-item';\r\n liElm.role = 'menuitem';\r\n\r\n if ((item as GridMenuItem).divider || item === 'divider') {\r\n liElm.classList.add('slick-gridmenu-item-divider');\r\n addClickListener = false;\r\n }\r\n if ((item as GridMenuItem).disabled) {\r\n liElm.classList.add('slick-gridmenu-item-disabled');\r\n }\r\n\r\n if ((item as GridMenuItem).hidden) {\r\n liElm.classList.add('slick-gridmenu-item-hidden');\r\n }\r\n\r\n if ((item as GridMenuItem).cssClass) {\r\n liElm.classList.add(...Utils.classNameToList((item as GridMenuItem).cssClass));\r\n }\r\n\r\n if ((item as GridMenuItem).tooltip) {\r\n liElm.title = (item as GridMenuItem).tooltip || '';\r\n }\r\n\r\n const iconElm = document.createElement('div');\r\n iconElm.className = 'slick-gridmenu-icon';\r\n\r\n liElm.appendChild(iconElm);\r\n\r\n if ((item as GridMenuItem).iconCssClass) {\r\n iconElm.classList.add(...Utils.classNameToList((item as GridMenuItem).iconCssClass));\r\n }\r\n\r\n if ((item as GridMenuItem).iconImage) {\r\n iconElm.style.backgroundImage = `url(${(item as GridMenuItem).iconImage})`;\r\n }\r\n\r\n const textElm = document.createElement('span');\r\n textElm.className = 'slick-gridmenu-content';\r\n this.grid.applyHtmlCode(textElm, this.grid.sanitizeHtmlString((item as GridMenuItem).title || ''));\r\n\r\n liElm.appendChild(textElm);\r\n\r\n if ((item as GridMenuItem).textCssClass) {\r\n textElm.classList.add(...Utils.classNameToList((item as GridMenuItem).textCssClass));\r\n }\r\n\r\n commandListElm.appendChild(liElm);\r\n\r\n if (addClickListener) {\r\n const eventGroup = isSubMenu ? 'sub-menu' : 'parent-menu';\r\n this._bindingEventService.bind(liElm, 'click', this.handleMenuItemClick.bind(this, item, level) as EventListener, undefined, eventGroup);\r\n }\r\n\r\n // optionally open sub-menu(s) by mouseover\r\n if (this._gridMenuOptions?.subMenuOpenByEvent === 'mouseover') {\r\n this._bindingEventService.bind(liElm, 'mouseover', ((e: DOMMouseOrTouchEvent) => {\r\n if ((item as GridMenuItem).commandItems || (item as GridMenuItem).customItems) {\r\n this.repositionSubMenu(item, level, e);\r\n } else if (!isSubMenu) {\r\n this.destroySubMenus();\r\n }\r\n }) as EventListener);\r\n }\r\n\r\n // the option/command item could be a sub-menu if it has another list of commands/options\r\n if ((item as GridMenuItem).commandItems || (item as GridMenuItem).customItems) {\r\n const chevronElm = document.createElement('span');\r\n chevronElm.className = 'sub-item-chevron';\r\n if (this._gridMenuOptions?.subItemChevronClass) {\r\n chevronElm.classList.add(...Utils.classNameToList(this._gridMenuOptions.subItemChevronClass));\r\n } else {\r\n chevronElm.textContent = '\u2B9E'; // \u2B9E or \u25B8\r\n }\r\n\r\n liElm.classList.add('slick-submenu-item');\r\n liElm.appendChild(chevronElm);\r\n continue;\r\n }\r\n }\r\n }\r\n\r\n /** Build the column picker, the code comes almost untouched from the file \"slick.columnpicker.js\" */\r\n protected populateColumnPicker() {\r\n this.grid.onColumnsReordered.subscribe(this.updateColumnOrder.bind(this));\r\n\r\n // user could pass a title on top of the columns list\r\n if (this._gridMenuOptions?.columnTitle) {\r\n this._columnTitleElm = document.createElement('div');\r\n this._columnTitleElm.className = 'title';\r\n this.grid.applyHtmlCode(this._columnTitleElm, this.grid.sanitizeHtmlString(this._gridMenuOptions.columnTitle));\r\n this._menuElm.appendChild(this._columnTitleElm);\r\n }\r\n\r\n this._bindingEventService.bind(this._menuElm, 'click', this.updateColumn.bind(this) as EventListener);\r\n this._listElm = document.createElement('span');\r\n this._listElm.className = 'slick-gridmenu-list';\r\n this._listElm.role = 'menu';\r\n }\r\n\r\n /** Delete and then Recreate the Grid Menu (for example when we switch from regular to a frozen grid) */\r\n recreateGridMenu() {\r\n this.deleteMenu();\r\n this.init(this.grid);\r\n }\r\n\r\n showGridMenu(e: DOMMouseOrTouchEvent) {\r\n const targetEvent = e.touches ? e.touches[0] : e;\r\n e.preventDefault();\r\n\r\n // empty both the picker list & the command list\r\n Utils.emptyElement(this._listElm);\r\n Utils.emptyElement(this._commandListElm);\r\n\r\n const commandItems = this._gridMenuOptions?.commandItems ?? this._gridMenuOptions?.customItems ?? [];\r\n this.populateCommandsMenu(commandItems, this._commandListElm, { grid: this.grid, level: 0 });\r\n this.updateColumnOrder();\r\n this._columnCheckboxes = [];\r\n\r\n const callbackArgs = {\r\n grid: this.grid,\r\n menu: this._menuElm,\r\n allColumns: this.columns,\r\n visibleColumns: this.getVisibleColumns()\r\n };\r\n\r\n // run the override function (when defined), if the result is false it won't go further\r\n if (this._gridMenuOptions && !this.runOverrideFunctionWhenExists(this._gridMenuOptions.menuUsabilityOverride, callbackArgs)) {\r\n return;\r\n }\r\n\r\n // notify of the onBeforeMenuShow only works when\r\n // this mean that we cannot notify when the grid menu is attach to a button event\r\n if (typeof e.stopPropagation === 'function') {\r\n if (this.onBeforeMenuShow.notify(callbackArgs, e, this).getReturnValue() === false) {\r\n return;\r\n }\r\n }\r\n\r\n let columnId, columnLabel, excludeCssClass;\r\n for (let i = 0; i < this.columns.length; i++) {\r\n columnId = this.columns[i].id;\r\n excludeCssClass = this.columns[i].excludeFromGridMenu ? 'hidden' : '';\r\n const colName: string = this.columns[i].name instanceof HTMLElement\r\n ? (this.columns[i].name as HTMLElement).innerHTML\r\n : (this.columns[i].name || '') as string;\r\n\r\n const liElm = document.createElement('li');\r\n liElm.className = excludeCssClass;\r\n liElm.ariaLabel = colName;\r\n\r\n const checkboxElm = document.createElement('input');\r\n checkboxElm.type = 'checkbox';\r\n checkboxElm.id = `${this._gridUid}-gridmenu-colpicker-${columnId}`;\r\n checkboxElm.dataset.columnid = String(this.columns[i].id);\r\n liElm.appendChild(checkboxElm);\r\n\r\n if (Utils.isDefined(this.grid.getColumnIndex(this.columns[i].id)) && !this.columns[i].hidden) {\r\n checkboxElm.checked = true;\r\n }\r\n\r\n this._columnCheckboxes.push(checkboxElm);\r\n\r\n // get the column label from the picker value extractor (user can optionally provide a custom extractor)\r\n columnLabel = (this._gridMenuOptions?.headerColumnValueExtractor)\r\n ? this._gridMenuOptions.headerColumnValueExtractor(this.columns[i], this._gridOptions)\r\n : this._defaults.headerColumnValueExtractor!(this.columns[i]);\r\n\r\n const labelElm = document.createElement('label');\r\n labelElm.htmlFor = `${this._gridUid}-gridmenu-colpicker-${columnId}`;\r\n this.grid.applyHtmlCode(labelElm, this.grid.sanitizeHtmlString(Utils.getHtmlStringOutput(columnLabel || '')));\r\n liElm.appendChild(labelElm);\r\n this._listElm.appendChild(liElm);\r\n }\r\n\r\n if (this._gridMenuOptions && (!this._gridMenuOptions.hideForceFitButton || !this._gridMenuOptions.hideSyncResizeButton)) {\r\n this._listElm.appendChild(document.createElement('hr'));\r\n }\r\n\r\n if (!(this._gridMenuOptions?.hideForceFitButton)) {\r\n const forceFitTitle = (this._gridMenuOptions?.forceFitTitle) || this._defaults.forceFitTitle as string;\r\n\r\n const liElm = document.createElement('li');\r\n liElm.ariaLabel = forceFitTitle;\r\n liElm.role = 'menuitem';\r\n this._listElm.appendChild(liElm);\r\n\r\n const forceFitCheckboxElm = document.createElement('input');\r\n forceFitCheckboxElm.type = 'checkbox';\r\n forceFitCheckboxElm.id = `${this._gridUid}-gridmenu-colpicker-forcefit`;\r\n forceFitCheckboxElm.dataset.option = 'autoresize';\r\n liElm.appendChild(forceFitCheckboxElm);\r\n\r\n const labelElm = document.createElement('label');\r\n labelElm.htmlFor = `${this._gridUid}-gridmenu-colpicker-forcefit`;\r\n labelElm.textContent = forceFitTitle;\r\n liElm.appendChild(labelElm);\r\n\r\n if (this.grid.getOptions().forceFitColumns) {\r\n forceFitCheckboxElm.checked = true;\r\n }\r\n }\r\n\r\n if (!(this._gridMenuOptions?.hideSyncResizeButton)) {\r\n const syncResizeTitle = (this._gridMenuOptions?.syncResizeTitle) || this._defaults.syncResizeTitle as string;\r\n\r\n const liElm = document.createElement('li');\r\n liElm.ariaLabel = syncResizeTitle;\r\n this._listElm.appendChild(liElm);\r\n\r\n const syncResizeCheckboxElm = document.createElement('input');\r\n syncResizeCheckboxElm.type = 'checkbox';\r\n syncResizeCheckboxElm.id = `${this._gridUid}-gridmenu-colpicker-syncresize`;\r\n syncResizeCheckboxElm.dataset.option = 'syncresize';\r\n liElm.appendChild(syncResizeCheckboxElm);\r\n\r\n const labelElm = document.createElement('label');\r\n labelElm.htmlFor = `${this._gridUid}-gridmenu-colpicker-syncresize`;\r\n labelElm.textContent = syncResizeTitle;\r\n liElm.appendChild(labelElm);\r\n\r\n if (this.grid.getOptions().syncColumnCellResize) {\r\n syncResizeCheckboxElm.checked = true;\r\n }\r\n }\r\n\r\n let buttonElm = (e.target.nodeName === 'BUTTON' ? e.target : e.target.querySelector('button')) as HTMLButtonElement; // get button element\r\n if (!buttonElm) {\r\n buttonElm = e.target.parentElement as HTMLButtonElement; // external grid menu might fall in this last case if wrapped in a span/div\r\n }\r\n\r\n // we need to display the menu to properly calculate its width but we can however make it invisible\r\n this._menuElm.style.display = 'block';\r\n this._menuElm.style.opacity = '0';\r\n\r\n this.repositionMenu(e, this._menuElm, buttonElm);\r\n\r\n // set \"height\" when defined OR ELSE use the \"max-height\" with available window size and optional margin bottom\r\n const menuMarginBottom = (this._gridMenuOptions?.marginBottom !== undefined) ? this._gridMenuOptions.marginBottom : this._defaults.marginBottom as number;\r\n if (this._gridMenuOptions?.height !== undefined) {\r\n this._menuElm.style.height = `${this._gridMenuOptions.height}px`;\r\n } else {\r\n this._menuElm.style.maxHeight = `${window.innerHeight - targetEvent.clientY - menuMarginBottom}px`;\r\n }\r\n\r\n this._menuElm.style.display = 'block';\r\n this._menuElm.style.opacity = '1'; // restore menu visibility\r\n this._menuElm.appendChild(this._listElm);\r\n this._isMenuOpen = true;\r\n\r\n if (typeof e.stopPropagation === 'function') {\r\n if (this.onAfterMenuShow.notify(callbackArgs, e, this).getReturnValue() === false) {\r\n return;\r\n }\r\n }\r\n }\r\n\r\n protected getGridUidSelector() {\r\n const gridUid = this.grid.getUID() || '';\r\n return gridUid ? `.${gridUid}` : '';\r\n }\r\n\r\n protected handleBodyMouseDown(e: DOMMouseOrTouchEvent) {\r\n // did we click inside the menu or any of its sub-menu(s)\r\n let isMenuClicked = false;\r\n if (this._menuElm?.contains(e.target)) {\r\n isMenuClicked = true;\r\n }\r\n if (!isMenuClicked) {\r\n document\r\n .querySelectorAll(`.slick-gridmenu.slick-submenu${this.getGridUidSelector()}`)\r\n .forEach(subElm => {\r\n if (subElm.contains(e.target)) {\r\n isMenuClicked = true;\r\n }\r\n });\r\n }\r\n\r\n if ((this._menuElm !== e.target && !isMenuClicked && !e.defaultPrevented && this._isMenuOpen) || e.target.className === 'close') {\r\n this.hideMenu(e);\r\n }\r\n }\r\n\r\n protected handleMenuItemClick(item: GridMenuItem | MenuCommandItem | 'divider', level = 0, e: DOMMouseOrTouchEvent) {\r\n if (item !== 'divider' && !item.disabled && !item.divider) {\r\n const command = item.command || '';\r\n\r\n if (Utils.isDefined(command) && !item.commandItems && !(item as GridMenuItem).customItems) {\r\n const callbackArgs: GridMenuCommandItemCallbackArgs = {\r\n grid: this.grid,\r\n command,\r\n item,\r\n allColumns: this.columns,\r\n visibleColumns: this.getVisibleColumns()\r\n };\r\n this.onCommand.notify(callbackArgs, e, this);\r\n\r\n // execute action callback when defined\r\n if (typeof item.action === 'function') {\r\n (item as MenuCommandItem).action!.call(this, e, callbackArgs);\r\n }\r\n\r\n // does the user want to leave open the Grid Menu after executing a command?\r\n const leaveOpen = !!(this._gridMenuOptions?.leaveOpen);\r\n if (!leaveOpen && !e.defaultPrevented) {\r\n this.hideMenu(e);\r\n }\r\n\r\n // Stop propagation so that it doesn't register as a header click event.\r\n e.preventDefault();\r\n e.stopPropagation();\r\n } else if (item.commandItems || (item as GridMenuItem).customItems) {\r\n this.repositionSubMenu(item, level, e);\r\n } else {\r\n this.destroySubMenus();\r\n }\r\n }\r\n }\r\n\r\n hideMenu(e: DOMMouseOrTouchEvent) {\r\n if (this._menuElm) {\r\n const callbackArgs = {\r\n grid: this.grid,\r\n menu: this._menuElm,\r\n allColumns: this.columns,\r\n visibleColumns: this.getVisibleColumns()\r\n };\r\n if (this._isMenuOpen && this.onMenuClose.notify(callbackArgs, e, this).getReturnValue() === false) {\r\n return;\r\n }\r\n this._isMenuOpen = false;\r\n Utils.hide(this._menuElm);\r\n }\r\n this.destroySubMenus();\r\n }\r\n\r\n /** Update the Titles of each sections (command, commandTitle, ...) */\r\n updateAllTitles(gridMenuOptions: GridMenuOption) {\r\n if (this._commandTitleElm) {\r\n this.grid.applyHtmlCode(this._commandTitleElm, this.grid.sanitizeHtmlString(gridMenuOptions.commandTitle || gridMenuOptions.customTitle || ''));\r\n }\r\n if (this._columnTitleElm) {\r\n this.grid.applyHtmlCode(this._columnTitleElm, this.grid.sanitizeHtmlString(gridMenuOptions.columnTitle || ''));\r\n }\r\n }\r\n\r\n protected addSubMenuTitleWhenExists(item: GridMenuItem | MenuCommandItem | 'divider', commandOrOptionMenu: HTMLDivElement) {\r\n if (item !== 'divider' && item?.subMenuTitle) {\r\n const subMenuTitleElm = document.createElement('div');\r\n subMenuTitleElm.className = 'slick-menu-title';\r\n subMenuTitleElm.textContent = item.subMenuTitle as string;\r\n const subMenuTitleClass = item.subMenuTitleCssClass as string;\r\n if (subMenuTitleClass) {\r\n subMenuTitleElm.classList.add(...Utils.classNameToList(subMenuTitleClass));\r\n }\r\n\r\n commandOrOptionMenu.appendChild(subMenuTitleElm);\r\n }\r\n }\r\n\r\n protected repositionSubMenu(item: GridMenuItem | MenuCommandItem | 'divider', level: number, e: DOMMouseOrTouchEvent) {\r\n // when we're clicking a grid cell OR our last menu type (command/option) differs then we know that we need to start fresh and close any sub-menus that might still be open\r\n if (e.target.classList.contains('slick-cell')) {\r\n this.destroySubMenus();\r\n }\r\n\r\n // creating sub-menu, we'll also pass level & the item object since we might have \"subMenuTitle\" to show\r\n const subMenuElm = this.createMenu(level + 1, item);\r\n subMenuElm.style.display = 'block';\r\n document.body.appendChild(subMenuElm);\r\n this.repositionMenu(e, subMenuElm);\r\n }\r\n\r\n /**\r\n * Reposition the menu drop (up/down) and the side (left/right)\r\n * @param {*} event\r\n */\r\n protected repositionMenu(e: DOMMouseOrTouchEvent, menuElm: HTMLElement, buttonElm?: HTMLButtonElement) {\r\n const targetEvent = e.touches ? e.touches[0] : e;\r\n const isSubMenu = menuElm.classList.contains('slick-submenu');\r\n const parentElm = isSubMenu\r\n ? e.target.closest('.slick-gridmenu-item') as HTMLDivElement\r\n : targetEvent.target as HTMLElement;\r\n\r\n const menuIconOffset = Utils.offset(buttonElm || this._buttonElm); // get button offset position\r\n const menuWidth = menuElm.offsetWidth;\r\n const useClickToRepositionMenu = (this._gridMenuOptions?.useClickToRepositionMenu !== undefined) ? this._gridMenuOptions.useClickToRepositionMenu : this._defaults.useClickToRepositionMenu;\r\n const contentMinWidth = (this._gridMenuOptions?.contentMinWidth) ? this._gridMenuOptions.contentMinWidth : this._defaults.contentMinWidth as number;\r\n const currentMenuWidth = (contentMinWidth > menuWidth) ? contentMinWidth : menuWidth + 5;\r\n let menuOffsetTop = (useClickToRepositionMenu && targetEvent.pageY > 0) ? targetEvent.pageY : menuIconOffset!.top + 10;\r\n let menuOffsetLeft = (useClickToRepositionMenu && targetEvent.pageX > 0) ? targetEvent.pageX : menuIconOffset!.left + 10;\r\n\r\n if (isSubMenu && parentElm) {\r\n const parentOffset = Utils.offset(parentElm);\r\n menuOffsetLeft = parentOffset?.left ?? 0;\r\n menuOffsetTop = parentOffset?.top ?? 0;\r\n const gridPos = this.grid.getGridPosition();\r\n let subMenuPosCalc = menuOffsetLeft + Number(menuWidth); // calculate coordinate at caller element far right\r\n if (isSubMenu) {\r\n subMenuPosCalc += parentElm.clientWidth;\r\n }\r\n const browserWidth = document.documentElement.clientWidth;\r\n const dropSide = (subMenuPosCalc >= gridPos.width || subMenuPosCalc >= browserWidth) ? 'left' : 'right';\r\n if (dropSide === 'left') {\r\n menuElm.classList.remove('dropright');\r\n menuElm.classList.add('dropleft');\r\n menuOffsetLeft -= menuWidth;\r\n } else {\r\n menuElm.classList.remove('dropleft');\r\n menuElm.classList.add('dropright');\r\n if (isSubMenu) {\r\n menuOffsetLeft += parentElm.offsetWidth;\r\n }\r\n }\r\n } else {\r\n menuOffsetTop += 10;\r\n menuOffsetLeft = menuOffsetLeft - currentMenuWidth + 10;\r\n }\r\n\r\n menuElm.style.top = `${menuOffsetTop}px`;\r\n menuElm.style.left = `${menuOffsetLeft}px`;\r\n\r\n if (contentMinWidth > 0) {\r\n this._menuElm.style.minWidth = `${contentMinWidth}px`;\r\n }\r\n }\r\n\r\n protected updateColumnOrder() {\r\n // Because columns can be reordered, we have to update the `columns`\r\n // to reflect the new order, however we can't just take `grid.getColumns()`,\r\n // as it does not include columns currently hidden by the picker.\r\n // We create a new `columns` structure by leaving currently-hidden\r\n // columns in their original ordinal position and interleaving the results\r\n // of the current column sort.\r\n const current = this.grid.getColumns().slice(0);\r\n const ordered = new Array(this.columns.length);\r\n for (let i = 0; i < ordered.length; i++) {\r\n if (this.grid.getColumnIndex(this.columns[i].id) === undefined) {\r\n // If the column doesn't return a value from getColumnIndex,\r\n // it is hidden. Leave it in this position.\r\n ordered[i] = this.columns[i];\r\n } else {\r\n // Otherwise, grab the next visible column.\r\n ordered[i] = current.shift();\r\n }\r\n }\r\n this.columns = ordered;\r\n }\r\n\r\n protected updateColumn(e: DOMMouseOrTouchEvent) {\r\n if (e.target.dataset.option === 'autoresize') {\r\n // when calling setOptions, it will resize with ALL Columns (even the hidden ones)\r\n // we can avoid this problem by keeping a reference to the visibleColumns before setOptions and then setColumns after\r\n const previousVisibleColumns = this.getVisibleColumns();\r\n const isChecked = e.target.checked;\r\n this.grid.setOptions({ forceFitColumns: isChecked });\r\n this.grid.setColumns(previousVisibleColumns);\r\n return;\r\n }\r\n\r\n if (e.target.dataset.option === 'syncresize') {\r\n this.grid.setOptions({ syncColumnCellResize: !!(e.target.checked) });\r\n return;\r\n }\r\n\r\n if (e.target.type === 'checkbox') {\r\n const isChecked = e.target.checked;\r\n const columnId = e.target.dataset.columnid || '';\r\n const visibleColumns: Column[] = [];\r\n this._columnCheckboxes.forEach((columnCheckbox, idx) => {\r\n if (columnCheckbox.checked) {\r\n if (this.columns[idx].hidden) { this.columns[idx].hidden = false; }\r\n visibleColumns.push(this.columns[idx]);\r\n }\r\n });\r\n\r\n if (!visibleColumns.length) {\r\n e.target.checked = true;\r\n return;\r\n }\r\n\r\n const callbackArgs = {\r\n columnId,\r\n showing: isChecked,\r\n grid: this.grid,\r\n allColumns: this.columns,\r\n columns: visibleColumns,\r\n visibleColumns: this.getVisibleColumns()\r\n };\r\n this.grid.setColumns(visibleColumns);\r\n this.onColumnsChanged.notify(callbackArgs, e, this);\r\n }\r\n }\r\n\r\n getAllColumns() {\r\n return this.columns;\r\n }\r\n\r\n /** visible columns, we can simply get them directly from the grid */\r\n getVisibleColumns() {\r\n return this.grid.getColumns();\r\n }\r\n\r\n /**\r\n * Method that user can pass to override the default behavior.\r\n * In order word, user can choose or an item is (usable/visible/enable) by providing his own logic.\r\n * @param overrideFn: override function callback\r\n * @param args: multiple arguments provided to the override (cell, row, columnDef, dataContext, grid)\r\n */\r\n protected runOverrideFunctionWhenExists(overrideFn: ((args: any) => boolean) | undefined, args: T): boolean {\r\n if (typeof overrideFn === 'function') {\r\n return overrideFn.call(this, args);\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 window.Slick.Controls = window.Slick.Controls || {};\r\n window.Slick.Controls.GridMenu = SlickGridMenu;\r\n}\r\n"], "mappings": ";;;;;;;AAeA,MAAM,sBAAkC,MAAM,qBACxC,aAAyB,MAAM,OAC/B,QAAoB,MAAM,OAmHnB,gBAAN,MAAoB;AAAA,IAwCzB,YAAsB,SAAsC,MAAiB,aAAyB;AAAhF;AAAsC;AArC5D;AAAA;AAAA,6CAAkB,IAAI,WAAiD,iBAAiB;AACxF,8CAAmB,IAAI,WAAiD,kBAAkB;AAC1F,yCAAc,IAAI,WAAiD,aAAa;AAChF,uCAAY,IAAI,WAA4C,WAAW;AACvE,8CAAmB,IAAI,WAAiD,kBAAkB;AAI1F;AAAA;AAAA,0BAAU;AACV,0BAAU;AACV,0BAAU;AACV,0BAAU,eAAc;AACxB,0BAAU,qBAAwC,CAAC;AACnD,0BAAU;AACV,0BAAU;AACV,0BAAU;AACV,0BAAU,cAAoC;AAC9C,0BAAU;AACV,0BAAU;AACV,0BAAU;AACV,0BAAU,oBAAmB;AAC7B,0BAAU,oBAA0C;AACpD,0BAAU,aAA4B;AAAA,QACpC,YAAY;AAAA,QACZ,oBAAoB;AAAA,QACpB,sBAAsB;AAAA,QACtB,eAAe;AAAA,QACf,cAAc;AAAA,QACd,WAAW;AAAA,QACX,iBAAiB;AAAA,QACjB,uBAAuB;AAAA,QACvB,oBAAoB;AAAA,QACpB,iBAAiB;AAAA,QACjB,0BAA0B;AAAA,QAC1B,4BAA4B,CAAC,cAAsB,MAAM,oBAAoB,UAAU,QAAQ,IAAI,WAAW;AAAA,MAChH;AAGE,WAAK,WAAW,KAAK,OAAO,GAC5B,KAAK,eAAe,aACpB,KAAK,mBAAmB,MAAM,OAAO,CAAC,GAAG,KAAK,WAAW,YAAY,QAAQ,GAC7E,KAAK,uBAAuB,IAAI,oBAAoB,GAIpD,KAAK,aAAa,UAAU,CAAC,IAAI,SAAS;AACxC,YAAI,QAAQ,KAAK,iBAAiB,KAAK,cAAc;AACnD,cAAM,8BAA8B,KAAK,cAAc,gBAAiB,KAAK,KAAK,aAAa,iBAAiB,IAC1G,8BAA8B,KAAK,cAAc,iBAAiB,MAAM,KAAK,aAAa,gBAAiB;AACjH,WAAI,+BAA+B,gCACjC,KAAK,iBAAiB;AAAA,QAE1B;AAAA,MACF,CAAC,GACD,KAAK,KAAK,KAAK,IAAI;AAAA,IACrB;AAAA,IAEA,KAAK,MAAiB;AAhMxB;AAiMI,WAAK,eAAe,KAAK,WAAW,GACpC,MAAM,+BAA+B,KAAK,iBAAiB,GAAG,IAAI,GAClE,KAAK,eAAe,KAEhB,UAAK,qBAAL,WAAuB,gBAAe,UAAK,qBAAL,WAAuB,gBAC/D,QAAQ,KAAK,+JAA+J,GAI9K,KAAK,gBAAgB,UAAU,KAAK,QAAQ,KAAK,IAAI,CAAC;AAAA,IACxD;AAAA,IAEA,WAAW,YAA4B;AACrC,WAAK,mBAAmB,MAAM,OAAO,CAAC,GAAG,KAAK,kBAAkB,UAAU;AAAA,IAC5E;AAAA,IAEU,iBAAiB;AAjN7B;AAkNI,UAAM,kBAAiB,UAAK,qBAAL,mBAAuB,cAAc,KAAK,UAAU;AAU3E,UATI,KAAK,gBAAgB,KAAK,aAAa,eAAe,cAAc,KAAK,KAAK,aAAa,gBAAiB,IAC9G,KAAK,aAAa,SAAS,cAAc,IAAI,KAAK,QAAQ,sBAAsB,IAEhF,KAAK,aAAa,SAAS,cAAc,IAAI,KAAK,QAAQ,qBAAqB,GAEjF,KAAK,WAAY,MAAM,QAAQ,eAAe,aAAa,QAG5B,MAAM,WAAU,UAAK,qBAAL,mBAAuB,qBAAqB,IAAK,KAAK,iBAAkB,wBAAwB,KAAK,UAAU,0BACjI,KAAK,aAAa,eAAe;AAC5D,YAAM,YAAY,SAAS,cAA8B,IAAI,KAAK,QAAQ,kBAAkB;AAC5F,QAAI,cACF,UAAU,MAAM,QAAQ,eAAe,aAAa;AAAA,MAExD;AAGA,YADoB,UAAK,qBAAL,mBAAuB,gBAAe,SAAa,KAAK,iBAAiB,aAAa,KAAK,UAAU,YACzG;AAKd,YAJA,KAAK,aAAa,SAAS,cAAc,QAAQ,GACjD,KAAK,WAAW,YAAY,yBAC5B,KAAK,WAAW,YAAY,cAExB,UAAK,qBAAL,WAAuB;AACzB,eAAK,WAAW,UAAU,IAAI,GAAG,MAAM,gBAAgB,KAAK,iBAAiB,YAAY,CAAC;AAAA,aACrF;AACL,cAAM,eAAe,SAAS,cAAc,KAAK;AACjD,uBAAa,OAAO,UAAK,qBAAL,WAAuB,YAAa,KAAK,iBAAiB,YAAY,6BAC1F,KAAK,WAAW,YAAY,YAAY;AAAA,QAC1C;AAGA,YAAM,0BAAwB,UAAK,qBAAL,mBAAuB,yBAAwB,cAAc,eAAe;AAC1G,aAAK,WAAY,cAAe,aAAa,KAAK,YAAY,KAAK,WAAY,cAAe,qBAAqB,CAAC,GAGpH,KAAK,qBAAqB,KAAK,KAAK,YAAY,SAAS,KAAK,aAAa,KAAK,IAAI,CAAkB;AAAA,MACxG;AAEA,WAAK,WAAW,KAAK,WAAW,CAAC,GACjC,KAAK,qBAAqB,GAC1B,SAAS,KAAK,YAAY,KAAK,QAAQ,GAGvC,KAAK,qBAAqB,KAAK,SAAS,MAAM,aAAa,KAAK,oBAAoB,KAAK,IAAI,CAAkB,GAG/G,KAAK,qBAAqB,KAAK,SAAS,MAAM,gBAAgB,KAAK,QAAQ,KAAK,IAAI,CAAC;AAAA,IACvF;AAAA;AAAA,IAGA,WAAW,QAAQ,GAAG,MAAmD;AArQ3E;AAuQI,UAAM,YAAY,OAAM,UAAK,qBAAL,mBAAuB,SAAmB,KAAI,UAAK,qBAAL,mBAAuB,YAAY,IAAG,gBAAK,qBAAL,mBAAuB,cAAvB,YAAoC,CAAC,MAC3I,QAAQ,OAAM,UAAK,qBAAL,mBAAuB,KAAe,KAAI,UAAK,qBAAL,mBAAuB,QAAQ,IAAG,gBAAK,qBAAL,mBAAuB,aAAvB,YAAmC,CAAC,MAK9H,iBAAkB,6BAAuB,SAC3C,YAAa,UAAU,KAAK,iBAAkB,eAAe,WAAW,KAAK,EAAE,IAAI;AACvF,MAAI,cACF,KAAK,mBAAmB,YAEtB,QAAQ,MACV,YAAY,KAAK;AAGnB,UAAM,cAAc,mCAAmC,KAAK,IAAI,KAAK,QAAQ,IACvE,cAAc,SAAS,KAAK,cAA8B,oCAAoC,KAAK,GAAG,KAAK,mBAAmB,CAAC,EAAE;AAGvI,UAAI,aAAa;AACf,YAAI,YAAY,QAAQ,kBAAkB;AACxC,iBAAO;AAET,aAAK,gBAAgB;AAAA,MACvB;AAEA,UAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,cAAQ,OAAO,QACf,QAAQ,YAAY,aAChB,QAAQ,MACV,QAAQ,UAAU,IAAI,eAAe,GACjC,cACF,QAAQ,QAAQ,gBAAgB,aAGpC,QAAQ,YAAY,QAAQ,IAAI,YAAY,aAExC,UACF,QAAQ,MAAM,QAAQ,QAEpB,cACF,QAAQ,MAAM,YAAY,YAG5B,QAAQ,MAAM,UAAU;AAExB,UAAI,iBAA2C;AAC/C,UAAI,UAAU,GAAG;AACf,yBAAiB,SAAS,cAAc,QAAQ,GAChD,eAAe,OAAO,UACtB,eAAe,YAAY,SAC3B,eAAe,QAAQ,UAAU,kBACjC,eAAe,YAAY;AAE3B,YAAM,eAAe,SAAS,cAAc,MAAM;AAClD,qBAAa,YAAY,SACzB,aAAa,aAAa,QAC1B,aAAa,cAAc,QAC3B,eAAe,YAAY,YAAY,GACvC,QAAQ,YAAY,cAAc;AAAA,MACpC;AAGA,WAAK,kBAAkB,SAAS,cAAc,KAAK,GACnD,KAAK,gBAAgB,YAAY,sEAAsE,KAAK,IAC5G,KAAK,gBAAgB,OAAO,QAC5B,QAAQ,YAAY,KAAK,eAAe;AAExC,UAAM,gBACH,oDAAuB,iBAAvB,YACG,6BAAuB,gBAD1B,aAEE,UAAK,qBAAL,mBAAuB,iBAFzB,aAGE,UAAK,qBAAL,mBAAuB,gBAHzB,YAIE,CAAC;AAEN,aAAI,aAAa,SAAS,KAGpB,QAAQ,QAAQ,KAClB,KAAK,0BAA0B,MAAM,KAAK,eAAe,GAG7D,KAAK,qBAAqB,cAAc,KAAK,iBAAiB,EAAE,MAAM,KAAK,MAAM,MAAM,CAAC,GAGxF,SAEO;AAAA,IACT;AAAA;AAAA,IAGA,UAAU;AAlWZ;AAmWI,WAAK,gBAAgB,YAAY,GACjC,KAAK,iBAAiB,YAAY,GAClC,KAAK,YAAY,YAAY,GAC7B,KAAK,UAAU,YAAY,GAC3B,KAAK,iBAAiB,YAAY,GAClC,KAAK,KAAK,mBAAmB,YAAY,KAAK,kBAAkB,KAAK,IAAI,CAAC,GAC1E,KAAK,KAAK,gBAAgB,YAAY,GACtC,KAAK,KAAK,aAAa,YAAY,GACnC,KAAK,qBAAqB,UAAU,IACpC,UAAK,aAAL,WAAe,UACf,KAAK,WAAW;AAAA,IAClB;AAAA;AAAA,IAGA,aAAa;AAjXf;AAkXI,WAAK,qBAAqB,UAAU;AACpC,UAAM,cAAc,SAAS,cAA8B,sBAAsB,KAAK,QAAQ,EAAE;AAChG,MAAI,gBACF,YAAY,MAAM,UAAU,SAE1B,KAAK,eAEP,KAAK,WAAW,MAAM,QAAQ,UAEhC,UAAK,eAAL,WAAiB,WACjB,UAAK,aAAL,WAAe;AAAA,IACjB;AAAA;AAAA,IAGA,kBAAkB;AAChB,WAAK,qBAAqB,UAAU,UAAU,GAC9C,SAAS,iBAAiB,gCAAgC,KAAK,mBAAmB,CAAC,EAAE,EAClF,QAAQ,YAAU,OAAO,OAAO,CAAC;AAAA,IACtC;AAAA;AAAA,IAGU,qBAAqB,cAAiE,gBAA6B,MAA0C;AAvYzK;AAyYI,UAAM,SAAQ,6BAAM,UAAS,GACvB,YAAY,QAAQ;AAC1B,MAAI,CAAC,eAAc,UAAK,qBAAL,WAAuB,iBAAgB,UAAK,qBAAL,WAAuB,iBAC/E,KAAK,mBAAmB,SAAS,cAAc,KAAK,GACpD,KAAK,iBAAiB,YAAY,SAClC,KAAK,KAAK,cAAc,KAAK,kBAAkB,KAAK,KAAK,mBAAoB,KAAK,iBAAiB,gBAAgB,KAAK,iBAAiB,WAAsB,CAAC,GAChK,eAAe,YAAY,KAAK,gBAAgB;AAGlD,eAAS,IAAI,GAAG,KAAK,aAAa,QAAQ,IAAI,IAAI,KAAK;AACrD,YAAI,mBAAmB,IACjB,OAAO,aAAa,CAAC,GACrB,eAAe;AAAA,UACnB,MAAM,KAAK;AAAA,UACX,MAAM,KAAK;AAAA,UACX,SAAS,KAAK;AAAA,UACd,gBAAgB,KAAK,kBAAkB;AAAA,QACzC,GAGM,gBAAgB,KAAK,8BAAoD,KAAsB,wBAAwB,YAAY,GACnI,eAAe,KAAK,8BAAoD,KAAsB,uBAAuB,YAAY;AAGvI,YAAI,CAAC;AACH;AAKF,QAAI,OAAO,UAAU,eAAe,KAAK,MAAM,uBAAuB,MACnE,KAAsB,WAAW;AAGpC,YAAM,QAAQ,SAAS,cAAc,KAAK;AAC1C,cAAM,YAAY,uBAClB,MAAM,OAAO,aAER,KAAsB,WAAW,SAAS,eAC7C,MAAM,UAAU,IAAI,6BAA6B,GACjD,mBAAmB,KAEhB,KAAsB,YACzB,MAAM,UAAU,IAAI,8BAA8B,GAG/C,KAAsB,UACzB,MAAM,UAAU,IAAI,4BAA4B,GAG7C,KAAsB,YACzB,MAAM,UAAU,IAAI,GAAG,MAAM,gBAAiB,KAAsB,QAAQ,CAAC,GAG1E,KAAsB,YACzB,MAAM,QAAS,KAAsB,WAAW;AAGlD,YAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,gBAAQ,YAAY,uBAEpB,MAAM,YAAY,OAAO,GAEpB,KAAsB,gBACzB,QAAQ,UAAU,IAAI,GAAG,MAAM,gBAAiB,KAAsB,YAAY,CAAC,GAGhF,KAAsB,cACzB,QAAQ,MAAM,kBAAkB,OAAQ,KAAsB,SAAS;AAGzE,YAAM,UAAU,SAAS,cAAc,MAAM;AAY7C,YAXA,QAAQ,YAAY,0BACpB,KAAK,KAAK,cAAc,SAAS,KAAK,KAAK,mBAAoB,KAAsB,SAAS,EAAE,CAAC,GAEjG,MAAM,YAAY,OAAO,GAEpB,KAAsB,gBACzB,QAAQ,UAAU,IAAI,GAAG,MAAM,gBAAiB,KAAsB,YAAY,CAAC,GAGrF,eAAe,YAAY,KAAK,GAE5B,kBAAkB;AACpB,cAAM,aAAa,YAAY,aAAa;AAC5C,eAAK,qBAAqB,KAAK,OAAO,SAAS,KAAK,oBAAoB,KAAK,MAAM,MAAM,KAAK,GAAoB,QAAW,UAAU;AAAA,QACzI;AAcA,cAXI,UAAK,qBAAL,mBAAuB,wBAAuB,eAChD,KAAK,qBAAqB,KAAK,OAAO,aAAc,CAAC,MAA4C;AAC/F,UAAK,KAAsB,gBAAiB,KAAsB,cAChE,KAAK,kBAAkB,MAAM,OAAO,CAAC,IAC3B,aACV,KAAK,gBAAgB;AAAA,QAEzB,CAAmB,GAIhB,KAAsB,gBAAiB,KAAsB,aAAa;AAC7E,cAAM,aAAa,SAAS,cAAc,MAAM;AAChD,qBAAW,YAAY,qBACnB,UAAK,qBAAL,WAAuB,sBACzB,WAAW,UAAU,IAAI,GAAG,MAAM,gBAAgB,KAAK,iBAAiB,mBAAmB,CAAC,IAE5F,WAAW,cAAc,UAG3B,MAAM,UAAU,IAAI,oBAAoB,GACxC,MAAM,YAAY,UAAU;AAC5B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAGU,uBAAuB;AA9fnC;AA+fI,WAAK,KAAK,mBAAmB,UAAU,KAAK,kBAAkB,KAAK,IAAI,CAAC,IAGpE,UAAK,qBAAL,WAAuB,gBACzB,KAAK,kBAAkB,SAAS,cAAc,KAAK,GACnD,KAAK,gBAAgB,YAAY,SACjC,KAAK,KAAK,cAAc,KAAK,iBAAiB,KAAK,KAAK,mBAAmB,KAAK,iBAAiB,WAAW,CAAC,GAC7G,KAAK,SAAS,YAAY,KAAK,eAAe,IAGhD,KAAK,qBAAqB,KAAK,KAAK,UAAU,SAAS,KAAK,aAAa,KAAK,IAAI,CAAkB,GACpG,KAAK,WAAW,SAAS,cAAc,MAAM,GAC7C,KAAK,SAAS,YAAY,uBAC1B,KAAK,SAAS,OAAO;AAAA,IACvB;AAAA;AAAA,IAGA,mBAAmB;AACjB,WAAK,WAAW,GAChB,KAAK,KAAK,KAAK,IAAI;AAAA,IACrB;AAAA,IAEA,aAAa,GAA4C;AArhB3D;AAshBI,UAAM,cAAc,EAAE,UAAU,EAAE,QAAQ,CAAC,IAAI;AAC/C,QAAE,eAAe,GAGjB,MAAM,aAAa,KAAK,QAAQ,GAChC,MAAM,aAAa,KAAK,eAAe;AAEvC,UAAM,gBAAe,sBAAK,qBAAL,mBAAuB,iBAAvB,aAAuC,UAAK,qBAAL,mBAAuB,gBAA9D,YAA6E,CAAC;AACnG,WAAK,qBAAqB,cAAc,KAAK,iBAAiB,EAAE,MAAM,KAAK,MAAM,OAAO,EAAE,CAAC,GAC3F,KAAK,kBAAkB,GACvB,KAAK,oBAAoB,CAAC;AAE1B,UAAM,eAAe;AAAA,QACnB,MAAM,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,QACX,YAAY,KAAK;AAAA,QACjB,gBAAgB,KAAK,kBAAkB;AAAA,MACzC;AASA,UANI,KAAK,oBAAoB,CAAC,KAAK,8BAAmD,KAAK,iBAAiB,uBAAuB,YAAY,KAM3I,OAAO,EAAE,mBAAoB,cAC3B,KAAK,iBAAiB,OAAO,cAAc,GAAG,IAAI,EAAE,eAAe,MAAM;AAC3E;AAIJ,UAAI,UAAU,aAAa;AAC3B,eAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,QAAQ,KAAK;AAC5C,mBAAW,KAAK,QAAQ,CAAC,EAAE,IAC3B,kBAAkB,KAAK,QAAQ,CAAC,EAAE,sBAAsB,WAAW;AACnE,YAAM,UAAkB,KAAK,QAAQ,CAAC,EAAE,gBAAgB,cACnD,KAAK,QAAQ,CAAC,EAAE,KAAqB,YACrC,KAAK,QAAQ,CAAC,EAAE,QAAQ,IAEvB,QAAQ,SAAS,cAAc,IAAI;AACzC,cAAM,YAAY,iBAClB,MAAM,YAAY;AAElB,YAAM,cAAc,SAAS,cAAc,OAAO;AAClD,oBAAY,OAAO,YACnB,YAAY,KAAK,GAAG,KAAK,QAAQ,uBAAuB,QAAQ,IAChE,YAAY,QAAQ,WAAW,OAAO,KAAK,QAAQ,CAAC,EAAE,EAAE,GACxD,MAAM,YAAY,WAAW,GAEzB,MAAM,UAAU,KAAK,KAAK,eAAe,KAAK,QAAQ,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,QAAQ,CAAC,EAAE,WACpF,YAAY,UAAU,KAGxB,KAAK,kBAAkB,KAAK,WAAW,GAGvC,eAAe,UAAK,qBAAL,WAAuB,6BAClC,KAAK,iBAAiB,2BAA2B,KAAK,QAAQ,CAAC,GAAG,KAAK,YAAY,IACnF,KAAK,UAAU,2BAA4B,KAAK,QAAQ,CAAC,CAAC;AAE9D,YAAM,WAAW,SAAS,cAAc,OAAO;AAC/C,iBAAS,UAAU,GAAG,KAAK,QAAQ,uBAAuB,QAAQ,IAClE,KAAK,KAAK,cAAc,UAAU,KAAK,KAAK,mBAAmB,MAAM,oBAAoB,eAAe,EAAE,CAAC,CAAC,GAC5G,MAAM,YAAY,QAAQ,GAC1B,KAAK,SAAS,YAAY,KAAK;AAAA,MACjC;AAMA,UAJI,KAAK,qBAAqB,CAAC,KAAK,iBAAiB,sBAAsB,CAAC,KAAK,iBAAiB,yBAChG,KAAK,SAAS,YAAY,SAAS,cAAc,IAAI,CAAC,GAGpD,GAAE,UAAK,qBAAL,WAAuB,qBAAqB;AAChD,YAAM,kBAAiB,UAAK,qBAAL,mBAAuB,kBAAkB,KAAK,UAAU,eAEzE,QAAQ,SAAS,cAAc,IAAI;AACzC,cAAM,YAAY,eAClB,MAAM,OAAO,YACb,KAAK,SAAS,YAAY,KAAK;AAE/B,YAAM,sBAAsB,SAAS,cAAc,OAAO;AAC1D,4BAAoB,OAAO,YAC3B,oBAAoB,KAAK,GAAG,KAAK,QAAQ,gCACzC,oBAAoB,QAAQ,SAAS,cACrC,MAAM,YAAY,mBAAmB;AAErC,YAAM,WAAW,SAAS,cAAc,OAAO;AAC/C,iBAAS,UAAU,GAAG,KAAK,QAAQ,gCACnC,SAAS,cAAc,eACvB,MAAM,YAAY,QAAQ,GAEtB,KAAK,KAAK,WAAW,EAAE,oBACzB,oBAAoB,UAAU;AAAA,MAElC;AAEA,UAAI,GAAE,UAAK,qBAAL,WAAuB,uBAAuB;AAClD,YAAM,oBAAmB,UAAK,qBAAL,mBAAuB,oBAAoB,KAAK,UAAU,iBAE7E,QAAQ,SAAS,cAAc,IAAI;AACzC,cAAM,YAAY,iBAClB,KAAK,SAAS,YAAY,KAAK;AAE/B,YAAM,wBAAwB,SAAS,cAAc,OAAO;AAC5D,8BAAsB,OAAO,YAC7B,sBAAsB,KAAK,GAAG,KAAK,QAAQ,kCAC3C,sBAAsB,QAAQ,SAAS,cACvC,MAAM,YAAY,qBAAqB;AAEvC,YAAM,WAAW,SAAS,cAAc,OAAO;AAC/C,iBAAS,UAAU,GAAG,KAAK,QAAQ,kCACnC,SAAS,cAAc,iBACvB,MAAM,YAAY,QAAQ,GAEtB,KAAK,KAAK,WAAW,EAAE,yBACzB,sBAAsB,UAAU;AAAA,MAEpC;AAEA,UAAI,YAAa,EAAE,OAAO,aAAa,WAAW,EAAE,SAAS,EAAE,OAAO,cAAc,QAAQ;AAC5F,MAAK,cACH,YAAY,EAAE,OAAO,gBAIvB,KAAK,SAAS,MAAM,UAAU,SAC9B,KAAK,SAAS,MAAM,UAAU,KAE9B,KAAK,eAAe,GAAG,KAAK,UAAU,SAAS;AAG/C,UAAM,qBAAoB,UAAK,qBAAL,mBAAuB,kBAAiB,SAAa,KAAK,iBAAiB,eAAe,KAAK,UAAU;AAYnI,QAXI,UAAK,qBAAL,mBAAuB,YAAW,SACpC,KAAK,SAAS,MAAM,SAAS,GAAG,KAAK,iBAAiB,MAAM,OAE5D,KAAK,SAAS,MAAM,YAAY,GAAG,OAAO,cAAc,YAAY,UAAU,gBAAgB,MAGhG,KAAK,SAAS,MAAM,UAAU,SAC9B,KAAK,SAAS,MAAM,UAAU,KAC9B,KAAK,SAAS,YAAY,KAAK,QAAQ,GACvC,KAAK,cAAc,IAEf,OAAO,EAAE,mBAAoB,cAC3B,KAAK,gBAAgB,OAAO,cAAc,GAAG,IAAI,EAAE,eAAe;AAAA,IAI1E;AAAA,IAEU,qBAAqB;AAC7B,UAAM,UAAU,KAAK,KAAK,OAAO,KAAK;AACtC,aAAO,UAAU,IAAI,OAAO,KAAK;AAAA,IACnC;AAAA,IAEU,oBAAoB,GAAsC;AAjrBtE;AAmrBI,UAAI,gBAAgB;AACpB,OAAI,UAAK,aAAL,WAAe,SAAS,EAAE,YAC5B,gBAAgB,KAEb,iBACH,SACG,iBAAiB,gCAAgC,KAAK,mBAAmB,CAAC,EAAE,EAC5E,QAAQ,YAAU;AACjB,QAAI,OAAO,SAAS,EAAE,MAAM,MAC1B,gBAAgB;AAAA,MAEpB,CAAC,IAGA,KAAK,aAAa,EAAE,UAAU,CAAC,iBAAiB,CAAC,EAAE,oBAAoB,KAAK,eAAgB,EAAE,OAAO,cAAc,YACtH,KAAK,SAAS,CAAC;AAAA,IAEnB;AAAA,IAEU,oBAAoB,MAAkD,QAAQ,GAAG,GAA6D;AAtsB1J;AAusBI,UAAI,SAAS,aAAa,CAAC,KAAK,YAAY,CAAC,KAAK,SAAS;AACzD,YAAM,UAAU,KAAK,WAAW;AAEhC,YAAI,MAAM,UAAU,OAAO,KAAK,CAAC,KAAK,gBAAgB,CAAE,KAAsB,aAAa;AACzF,cAAM,eAAgD;AAAA,YACpD,MAAM,KAAK;AAAA,YACX;AAAA,YACA;AAAA,YACA,YAAY,KAAK;AAAA,YACjB,gBAAgB,KAAK,kBAAkB;AAAA,UACzC;AACA,eAAK,UAAU,OAAO,cAAc,GAAG,IAAI,GAGvC,OAAO,KAAK,UAAW,cACxB,KAAyB,OAAQ,KAAK,MAAM,GAAG,YAAY,GAK1D,CADc,CAAC,GAAE,UAAK,qBAAL,WAAuB,cAC1B,CAAC,EAAE,oBACnB,KAAK,SAAS,CAAC,GAIjB,EAAE,eAAe,GACjB,EAAE,gBAAgB;AAAA,QACpB,MAAO,CAAI,KAAK,gBAAiB,KAAsB,cACrD,KAAK,kBAAkB,MAAM,OAAO,CAAC,IAErC,KAAK,gBAAgB;AAAA,MAEzB;AAAA,IACF;AAAA,IAEA,SAAS,GAAsC;AAC7C,UAAI,KAAK,UAAU;AACjB,YAAM,eAAe;AAAA,UACnB,MAAM,KAAK;AAAA,UACX,MAAM,KAAK;AAAA,UACX,YAAY,KAAK;AAAA,UACjB,gBAAgB,KAAK,kBAAkB;AAAA,QACzC;AACA,YAAI,KAAK,eAAe,KAAK,YAAY,OAAO,cAAc,GAAG,IAAI,EAAE,eAAe,MAAM;AAC1F;AAEF,aAAK,cAAc,IACnB,MAAM,KAAK,KAAK,QAAQ;AAAA,MAC1B;AACA,WAAK,gBAAgB;AAAA,IACvB;AAAA;AAAA,IAGA,gBAAgB,iBAAiC;AAC/C,MAAI,KAAK,oBACP,KAAK,KAAK,cAAc,KAAK,kBAAkB,KAAK,KAAK,mBAAmB,gBAAgB,gBAAgB,gBAAgB,eAAe,EAAE,CAAC,GAE5I,KAAK,mBACP,KAAK,KAAK,cAAc,KAAK,iBAAiB,KAAK,KAAK,mBAAmB,gBAAgB,eAAe,EAAE,CAAC;AAAA,IAEjH;AAAA,IAEU,0BAA0B,MAAkD,qBAAqC;AACzH,UAAI,SAAS,cAAa,qBAAM,eAAc;AAC5C,YAAM,kBAAkB,SAAS,cAAc,KAAK;AACpD,wBAAgB,YAAY,oBAC5B,gBAAgB,cAAc,KAAK;AACnC,YAAM,oBAAoB,KAAK;AAC/B,QAAI,qBACF,gBAAgB,UAAU,IAAI,GAAG,MAAM,gBAAgB,iBAAiB,CAAC,GAG3E,oBAAoB,YAAY,eAAe;AAAA,MACjD;AAAA,IACF;AAAA,IAEU,kBAAkB,MAAkD,OAAe,GAA6D;AAExJ,MAAI,EAAE,OAAO,UAAU,SAAS,YAAY,KAC1C,KAAK,gBAAgB;AAIvB,UAAM,aAAa,KAAK,WAAW,QAAQ,GAAG,IAAI;AAClD,iBAAW,MAAM,UAAU,SAC3B,SAAS,KAAK,YAAY,UAAU,GACpC,KAAK,eAAe,GAAG,UAAU;AAAA,IACnC;AAAA;AAAA;AAAA;AAAA;AAAA,IAMU,eAAe,GAA6D,SAAsB,WAA+B;AApyB7I;AAqyBI,UAAM,cAAc,EAAE,UAAU,EAAE,QAAQ,CAAC,IAAI,GACzC,YAAY,QAAQ,UAAU,SAAS,eAAe,GACtD,YAAY,YACd,EAAE,OAAO,QAAQ,sBAAsB,IACvC,YAAY,QAEV,iBAAiB,MAAM,OAAO,aAAa,KAAK,UAAU,GAC1D,YAAY,QAAQ,aACpB,6BAA4B,UAAK,qBAAL,mBAAuB,8BAA6B,SAAa,KAAK,iBAAiB,2BAA2B,KAAK,UAAU,0BAC7J,mBAAmB,UAAK,qBAAL,WAAuB,kBAAmB,KAAK,iBAAiB,kBAAkB,KAAK,UAAU,iBACpH,mBAAoB,kBAAkB,YAAa,kBAAkB,YAAY,GACnF,gBAAiB,4BAA4B,YAAY,QAAQ,IAAK,YAAY,QAAQ,eAAgB,MAAM,IAChH,iBAAkB,4BAA4B,YAAY,QAAQ,IAAK,YAAY,QAAQ,eAAgB,OAAO;AAEtH,UAAI,aAAa,WAAW;AAC1B,YAAM,eAAe,MAAM,OAAO,SAAS;AAC3C,0BAAiB,kDAAc,SAAd,YAAsB,GACvC,iBAAgB,kDAAc,QAAd,YAAqB;AACrC,YAAM,UAAU,KAAK,KAAK,gBAAgB,GACtC,iBAAiB,iBAAiB,OAAO,SAAS;AACtD,QAAI,cACF,kBAAkB,UAAU;AAE9B,YAAM,eAAe,SAAS,gBAAgB;AAE9C,SADkB,kBAAkB,QAAQ,SAAS,kBAAkB,eAAgB,SAAS,aAC/E,UACf,QAAQ,UAAU,OAAO,WAAW,GACpC,QAAQ,UAAU,IAAI,UAAU,GAChC,kBAAkB,cAElB,QAAQ,UAAU,OAAO,UAAU,GACnC,QAAQ,UAAU,IAAI,WAAW,GAC7B,cACF,kBAAkB,UAAU;AAAA,MAGlC;AACE,yBAAiB,IACjB,iBAAiB,iBAAiB,mBAAmB;AAGvD,cAAQ,MAAM,MAAM,GAAG,aAAa,MACpC,QAAQ,MAAM,OAAO,GAAG,cAAc,MAElC,kBAAkB,MACpB,KAAK,SAAS,MAAM,WAAW,GAAG,eAAe;AAAA,IAErD;AAAA,IAEU,oBAAoB;AAO5B,UAAM,UAAU,KAAK,KAAK,WAAW,EAAE,MAAM,CAAC,GACxC,UAAU,IAAI,MAAM,KAAK,QAAQ,MAAM;AAC7C,eAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ;AAClC,QAAI,KAAK,KAAK,eAAe,KAAK,QAAQ,CAAC,EAAE,EAAE,MAAM,SAGnD,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,IAG3B,QAAQ,CAAC,IAAI,QAAQ,MAAM;AAG/B,WAAK,UAAU;AAAA,IACjB;AAAA,IAEU,aAAa,GAA2C;AAChE,UAAI,EAAE,OAAO,QAAQ,WAAW,cAAc;AAG5C,YAAM,yBAAyB,KAAK,kBAAkB,GAChD,YAAY,EAAE,OAAO;AAC3B,aAAK,KAAK,WAAW,EAAE,iBAAiB,UAAU,CAAC,GACnD,KAAK,KAAK,WAAW,sBAAsB;AAC3C;AAAA,MACF;AAEA,UAAI,EAAE,OAAO,QAAQ,WAAW,cAAc;AAC5C,aAAK,KAAK,WAAW,EAAE,sBAAsB,CAAC,CAAE,EAAE,OAAO,QAAS,CAAC;AACnE;AAAA,MACF;AAEA,UAAI,EAAE,OAAO,SAAS,YAAY;AAChC,YAAM,YAAY,EAAE,OAAO,SACrB,WAAW,EAAE,OAAO,QAAQ,YAAY,IACxC,iBAA2B,CAAC;AAQlC,YAPA,KAAK,kBAAkB,QAAQ,CAAC,gBAAgB,QAAQ;AACtD,UAAI,eAAe,YACb,KAAK,QAAQ,GAAG,EAAE,WAAU,KAAK,QAAQ,GAAG,EAAE,SAAS,KAC3D,eAAe,KAAK,KAAK,QAAQ,GAAG,CAAC;AAAA,QAEzC,CAAC,GAEG,CAAC,eAAe,QAAQ;AAC1B,YAAE,OAAO,UAAU;AACnB;AAAA,QACF;AAEA,YAAM,eAAe;AAAA,UACnB;AAAA,UACA,SAAS;AAAA,UACT,MAAM,KAAK;AAAA,UACX,YAAY,KAAK;AAAA,UACjB,SAAS;AAAA,UACT,gBAAgB,KAAK,kBAAkB;AAAA,QACzC;AACA,aAAK,KAAK,WAAW,cAAc,GACnC,KAAK,iBAAiB,OAAO,cAAc,GAAG,IAAI;AAAA,MACpD;AAAA,IACF;AAAA,IAEA,gBAAgB;AACd,aAAO,KAAK;AAAA,IACd;AAAA;AAAA,IAGA,oBAAoB;AAClB,aAAO,KAAK,KAAK,WAAW;AAAA,IAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQU,8BAAuC,YAAkD,MAAkB;AACnH,aAAI,OAAO,cAAe,aACjB,WAAW,KAAK,MAAM,IAAI,IAE5B;AAAA,IACT;AAAA,EACF;AAGA,EAAiB,OAAO,UACtB,OAAO,MAAM,WAAW,OAAO,MAAM,YAAY,CAAC,GAClD,OAAO,MAAM,SAAS,WAAW;", "names": [] } diff --git a/dist/browser/controls/slick.pager.js.map b/dist/browser/controls/slick.pager.js.map index 152bf651..0d6a7811 100644 --- a/dist/browser/controls/slick.pager.js.map +++ b/dist/browser/controls/slick.pager.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../../../src/controls/slick.pager.ts"], - "sourcesContent": ["import type { PagingInfo } from '../models/index';\r\nimport { BindingEventService as BindingEventService_, SlickGlobalEditorLock as SlickGlobalEditorLock_, Utils as Utils_ } from '../slick.core';\r\nimport type { SlickDataView } from '../slick.dataview';\r\nimport type { SlickGrid } from '../slick.grid';\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 SlickGlobalEditorLock = IIFE_ONLY ? Slick.GlobalEditorLock : SlickGlobalEditorLock_;\r\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\r\n\r\nexport interface GridPagerOption {\r\n showAllText: string;\r\n showPageText: string;\r\n showCountText: string;\r\n showCount: boolean;\r\n pagingOptions: Array<{ data: number; name: string; ariaLabel: string; }>;\r\n showPageSizes: boolean;\r\n}\r\n\r\nexport class SlickGridPager {\r\n // --\r\n // public API\r\n\r\n // --\r\n // protected props\r\n protected _container: HTMLElement; // the container might be a string, a jQuery object or a native element\r\n protected _statusElm!: HTMLElement;\r\n protected _bindingEventService: BindingEventService_;\r\n protected _options: GridPagerOption;\r\n protected _defaults: GridPagerOption = {\r\n showAllText: 'Showing all {rowCount} rows',\r\n showPageText: 'Showing page {pageNum} of {pageCount}',\r\n showCountText: 'From {countBegin} to {countEnd} of {rowCount} rows',\r\n showCount: false,\r\n pagingOptions: [\r\n { data: 0, name: 'All', ariaLabel: 'Show All Pages' },\r\n { data: -1, name: 'Auto', ariaLabel: 'Auto Page Size' },\r\n { data: 25, name: '25', ariaLabel: 'Show 25 rows per page' },\r\n { data: 50, name: '50', ariaLabel: 'Show 50 rows per page' },\r\n { data: 100, name: '100', ariaLabel: 'Show 100 rows per page' },\r\n ],\r\n showPageSizes: false\r\n };\r\n\r\n constructor(protected readonly dataView: SlickDataView, protected readonly grid: SlickGrid, selectorOrElm: HTMLElement | string, options?: Partial) {\r\n this._container = this.getContainerElement(selectorOrElm) as HTMLElement;\r\n this._options = Utils.extend(true, {}, this._defaults, options);\r\n this._bindingEventService = new BindingEventService();\r\n this.init();\r\n }\r\n\r\n init() {\r\n this.constructPagerUI();\r\n this.updatePager(this.dataView.getPagingInfo());\r\n this.dataView.onPagingInfoChanged.subscribe((_e, pagingInfo) => {\r\n this.updatePager(pagingInfo);\r\n });\r\n }\r\n\r\n /** Destroy function when element is destroyed */\r\n destroy() {\r\n this.setPageSize(0);\r\n this._bindingEventService.unbindAll();\r\n Utils.emptyElement(this._container);\r\n }\r\n\r\n protected getNavState() {\r\n const cannotLeaveEditMode = !SlickGlobalEditorLock.commitCurrentEdit();\r\n const pagingInfo = this.dataView.getPagingInfo();\r\n const lastPage = pagingInfo.totalPages - 1;\r\n\r\n return {\r\n canGotoFirst: !cannotLeaveEditMode && pagingInfo.pageSize !== 0 && pagingInfo.pageNum > 0,\r\n canGotoLast: !cannotLeaveEditMode && pagingInfo.pageSize !== 0 && pagingInfo.pageNum !== lastPage,\r\n canGotoPrev: !cannotLeaveEditMode && pagingInfo.pageSize !== 0 && pagingInfo.pageNum > 0,\r\n canGotoNext: !cannotLeaveEditMode && pagingInfo.pageSize !== 0 && pagingInfo.pageNum < lastPage,\r\n pagingInfo\r\n };\r\n }\r\n\r\n protected setPageSize(n: number) {\r\n this.dataView.setRefreshHints({\r\n isFilterUnchanged: true\r\n });\r\n this.dataView.setPagingOptions({ pageSize: n });\r\n }\r\n\r\n protected gotoFirst() {\r\n if (this.getNavState().canGotoFirst) {\r\n this.dataView.setPagingOptions({ pageNum: 0 });\r\n }\r\n }\r\n\r\n protected gotoLast() {\r\n const state = this.getNavState();\r\n if (state.canGotoLast) {\r\n this.dataView.setPagingOptions({ pageNum: state.pagingInfo.totalPages - 1 });\r\n }\r\n }\r\n\r\n protected gotoPrev() {\r\n const state = this.getNavState();\r\n if (state.canGotoPrev) {\r\n this.dataView.setPagingOptions({ pageNum: state.pagingInfo.pageNum - 1 });\r\n }\r\n }\r\n\r\n protected gotoNext() {\r\n const state = this.getNavState();\r\n if (state.canGotoNext) {\r\n this.dataView.setPagingOptions({ pageNum: state.pagingInfo.pageNum + 1 });\r\n }\r\n }\r\n\r\n protected getContainerElement(selectorOrElm: object | HTMLElement | string) {\r\n // the container might be a string, a jQuery object or a native element\r\n return typeof selectorOrElm === 'string'\r\n ? document.querySelector(selectorOrElm)\r\n : typeof selectorOrElm === 'object' && (selectorOrElm as any)[0]\r\n ? (selectorOrElm as any)[0] as HTMLElement\r\n : selectorOrElm;\r\n }\r\n\r\n protected constructPagerUI() {\r\n // the container might be a string, a jQuery object or a native element\r\n const container = this.getContainerElement(this._container) as HTMLElement | HTMLElement[];\r\n if (!container || ((container as any).jquery && !(container as HTMLElement[])[0])) { return; }\r\n\r\n const navElm = document.createElement('span');\r\n navElm.className = 'slick-pager-nav';\r\n\r\n const settingsElm = document.createElement('span');\r\n settingsElm.className = 'slick-pager-settings';\r\n\r\n this._statusElm = document.createElement('span');\r\n this._statusElm.className = 'slick-pager-status';\r\n\r\n const pagerSettingsElm = document.createElement('span');\r\n pagerSettingsElm.className = 'slick-pager-settings-expanded';\r\n pagerSettingsElm.textContent = 'Show: ';\r\n\r\n for (let o = 0; o < this._options.pagingOptions.length; o++) {\r\n const p = this._options.pagingOptions[o];\r\n\r\n const anchorElm = document.createElement('a');\r\n anchorElm.textContent = p.name;\r\n anchorElm.ariaLabel = p.ariaLabel;\r\n anchorElm.dataset.val = String(p.data);\r\n pagerSettingsElm.appendChild(anchorElm);\r\n\r\n this._bindingEventService.bind(anchorElm, 'click', ((e: any) => {\r\n const pagesize = e.target.dataset.val;\r\n if (pagesize !== undefined) {\r\n if (Number(pagesize) === -1) {\r\n const vp = this.grid.getViewport();\r\n this.setPageSize(vp.bottom - vp.top);\r\n } else {\r\n this.setPageSize(parseInt(pagesize));\r\n }\r\n }\r\n }));\r\n }\r\n\r\n pagerSettingsElm.style.display = this._options.showPageSizes ? 'block' : 'none';\r\n\r\n settingsElm.appendChild(pagerSettingsElm);\r\n\r\n // light bulb icon\r\n const displayPaginationContainer = document.createElement('span');\r\n const displayIconElm = document.createElement('span');\r\n displayPaginationContainer.className = 'sgi-container';\r\n displayIconElm.ariaLabel = 'Show Pagination Options';\r\n displayIconElm.role = 'button';\r\n displayIconElm.className = 'sgi sgi-lightbulb';\r\n displayPaginationContainer.appendChild(displayIconElm);\r\n\r\n this._bindingEventService.bind(displayIconElm, 'click', () => {\r\n const styleDisplay = pagerSettingsElm.style.display;\r\n pagerSettingsElm.style.display = styleDisplay === 'none' ? 'inline-flex' : 'none';\r\n });\r\n settingsElm.appendChild(displayPaginationContainer);\r\n\r\n const pageButtons = [\r\n { key: 'start', ariaLabel: 'First Page', callback: this.gotoFirst },\r\n { key: 'left', ariaLabel: 'Previous Page', callback: this.gotoPrev },\r\n { key: 'right', ariaLabel: 'Next Page', callback: this.gotoNext },\r\n { key: 'end', ariaLabel: 'Last Page', callback: this.gotoLast },\r\n ];\r\n\r\n pageButtons.forEach(pageBtn => {\r\n const iconElm = document.createElement('span');\r\n iconElm.className = 'sgi-container';\r\n\r\n const innerIconElm = document.createElement('span');\r\n innerIconElm.role = 'button';\r\n innerIconElm.ariaLabel = pageBtn.ariaLabel;\r\n innerIconElm.className = `sgi sgi-chevron-${pageBtn.key}`;\r\n this._bindingEventService.bind(innerIconElm, 'click', pageBtn.callback.bind(this));\r\n\r\n iconElm.appendChild(innerIconElm);\r\n navElm.appendChild(iconElm);\r\n });\r\n\r\n const slickPagerElm = document.createElement('div');\r\n slickPagerElm.className = 'slick-pager';\r\n\r\n slickPagerElm.appendChild(navElm);\r\n slickPagerElm.appendChild(this._statusElm);\r\n slickPagerElm.appendChild(settingsElm);\r\n\r\n (container as HTMLElement).appendChild(slickPagerElm);\r\n }\r\n\r\n protected updatePager(pagingInfo: PagingInfo) {\r\n if (!this._container || ((this._container as any).jquery && !(this._container as any)[0])) { return; }\r\n const state = this.getNavState();\r\n\r\n // remove disabled class on all icons\r\n this._container.querySelectorAll('.slick-pager-nav span')\r\n .forEach(pagerIcon => pagerIcon.classList.remove('sgi-state-disabled'));\r\n\r\n // add back disabled class to only necessary icons\r\n if (!state.canGotoFirst) {\r\n this._container!.querySelector('.sgi-chevron-start')?.classList.add('sgi-state-disabled');\r\n }\r\n if (!state.canGotoLast) {\r\n this._container!.querySelector('.sgi-chevron-end')?.classList.add('sgi-state-disabled');\r\n }\r\n if (!state.canGotoNext) {\r\n this._container!.querySelector('.sgi-chevron-right')?.classList.add('sgi-state-disabled');\r\n }\r\n if (!state.canGotoPrev) {\r\n this._container!.querySelector('.sgi-chevron-left')?.classList.add('sgi-state-disabled');\r\n }\r\n\r\n if (pagingInfo.pageSize === 0) {\r\n this._statusElm.textContent = (this._options.showAllText.replace('{rowCount}', pagingInfo.totalRows + '').replace('{pageCount}', pagingInfo.totalPages + ''));\r\n } else {\r\n this._statusElm.textContent = (this._options.showPageText.replace('{pageNum}', pagingInfo.pageNum + 1 + '').replace('{pageCount}', pagingInfo.totalPages + ''));\r\n }\r\n\r\n if (this._options.showCount && pagingInfo.pageSize !== 0) {\r\n const pageBegin = pagingInfo.pageNum * pagingInfo.pageSize;\r\n let currentText = this._statusElm.textContent;\r\n\r\n if (currentText) {\r\n currentText += ' - ';\r\n }\r\n\r\n this._statusElm.textContent =\r\n currentText +\r\n this._options.showCountText\r\n .replace('{rowCount}', String(pagingInfo.totalRows))\r\n .replace('{countBegin}', String(pageBegin + 1))\r\n .replace('{countEnd}', String(Math.min(pageBegin + pagingInfo.pageSize, pagingInfo.totalRows)));\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 window.Slick.Controls = window.Slick.Controls || {};\r\n window.Slick.Controls.Pager = SlickGridPager;\r\n}\r\n\r\n"], + "sourcesContent": ["import type { PagingInfo } from '../models/index.js';\r\nimport { BindingEventService as BindingEventService_, SlickGlobalEditorLock as SlickGlobalEditorLock_, Utils as Utils_ } from '../slick.core.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 BindingEventService = IIFE_ONLY ? Slick.BindingEventService : BindingEventService_;\r\nconst SlickGlobalEditorLock = IIFE_ONLY ? Slick.GlobalEditorLock : SlickGlobalEditorLock_;\r\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\r\n\r\nexport interface GridPagerOption {\r\n showAllText: string;\r\n showPageText: string;\r\n showCountText: string;\r\n showCount: boolean;\r\n pagingOptions: Array<{ data: number; name: string; ariaLabel: string; }>;\r\n showPageSizes: boolean;\r\n}\r\n\r\nexport class SlickGridPager {\r\n // --\r\n // public API\r\n\r\n // --\r\n // protected props\r\n protected _container: HTMLElement; // the container might be a string, a jQuery object or a native element\r\n protected _statusElm!: HTMLElement;\r\n protected _bindingEventService: BindingEventService_;\r\n protected _options: GridPagerOption;\r\n protected _defaults: GridPagerOption = {\r\n showAllText: 'Showing all {rowCount} rows',\r\n showPageText: 'Showing page {pageNum} of {pageCount}',\r\n showCountText: 'From {countBegin} to {countEnd} of {rowCount} rows',\r\n showCount: false,\r\n pagingOptions: [\r\n { data: 0, name: 'All', ariaLabel: 'Show All Pages' },\r\n { data: -1, name: 'Auto', ariaLabel: 'Auto Page Size' },\r\n { data: 25, name: '25', ariaLabel: 'Show 25 rows per page' },\r\n { data: 50, name: '50', ariaLabel: 'Show 50 rows per page' },\r\n { data: 100, name: '100', ariaLabel: 'Show 100 rows per page' },\r\n ],\r\n showPageSizes: false\r\n };\r\n\r\n constructor(protected readonly dataView: SlickDataView, protected readonly grid: SlickGrid, selectorOrElm: HTMLElement | string, options?: Partial) {\r\n this._container = this.getContainerElement(selectorOrElm) as HTMLElement;\r\n this._options = Utils.extend(true, {}, this._defaults, options);\r\n this._bindingEventService = new BindingEventService();\r\n this.init();\r\n }\r\n\r\n init() {\r\n this.constructPagerUI();\r\n this.updatePager(this.dataView.getPagingInfo());\r\n this.dataView.onPagingInfoChanged.subscribe((_e, pagingInfo) => {\r\n this.updatePager(pagingInfo);\r\n });\r\n }\r\n\r\n /** Destroy function when element is destroyed */\r\n destroy() {\r\n this.setPageSize(0);\r\n this._bindingEventService.unbindAll();\r\n Utils.emptyElement(this._container);\r\n }\r\n\r\n protected getNavState() {\r\n const cannotLeaveEditMode = !SlickGlobalEditorLock.commitCurrentEdit();\r\n const pagingInfo = this.dataView.getPagingInfo();\r\n const lastPage = pagingInfo.totalPages - 1;\r\n\r\n return {\r\n canGotoFirst: !cannotLeaveEditMode && pagingInfo.pageSize !== 0 && pagingInfo.pageNum > 0,\r\n canGotoLast: !cannotLeaveEditMode && pagingInfo.pageSize !== 0 && pagingInfo.pageNum !== lastPage,\r\n canGotoPrev: !cannotLeaveEditMode && pagingInfo.pageSize !== 0 && pagingInfo.pageNum > 0,\r\n canGotoNext: !cannotLeaveEditMode && pagingInfo.pageSize !== 0 && pagingInfo.pageNum < lastPage,\r\n pagingInfo\r\n };\r\n }\r\n\r\n protected setPageSize(n: number) {\r\n this.dataView.setRefreshHints({\r\n isFilterUnchanged: true\r\n });\r\n this.dataView.setPagingOptions({ pageSize: n });\r\n }\r\n\r\n protected gotoFirst() {\r\n if (this.getNavState().canGotoFirst) {\r\n this.dataView.setPagingOptions({ pageNum: 0 });\r\n }\r\n }\r\n\r\n protected gotoLast() {\r\n const state = this.getNavState();\r\n if (state.canGotoLast) {\r\n this.dataView.setPagingOptions({ pageNum: state.pagingInfo.totalPages - 1 });\r\n }\r\n }\r\n\r\n protected gotoPrev() {\r\n const state = this.getNavState();\r\n if (state.canGotoPrev) {\r\n this.dataView.setPagingOptions({ pageNum: state.pagingInfo.pageNum - 1 });\r\n }\r\n }\r\n\r\n protected gotoNext() {\r\n const state = this.getNavState();\r\n if (state.canGotoNext) {\r\n this.dataView.setPagingOptions({ pageNum: state.pagingInfo.pageNum + 1 });\r\n }\r\n }\r\n\r\n protected getContainerElement(selectorOrElm: object | HTMLElement | string) {\r\n // the container might be a string, a jQuery object or a native element\r\n return typeof selectorOrElm === 'string'\r\n ? document.querySelector(selectorOrElm)\r\n : typeof selectorOrElm === 'object' && (selectorOrElm as any)[0]\r\n ? (selectorOrElm as any)[0] as HTMLElement\r\n : selectorOrElm;\r\n }\r\n\r\n protected constructPagerUI() {\r\n // the container might be a string, a jQuery object or a native element\r\n const container = this.getContainerElement(this._container) as HTMLElement | HTMLElement[];\r\n if (!container || ((container as any).jquery && !(container as HTMLElement[])[0])) { return; }\r\n\r\n const navElm = document.createElement('span');\r\n navElm.className = 'slick-pager-nav';\r\n\r\n const settingsElm = document.createElement('span');\r\n settingsElm.className = 'slick-pager-settings';\r\n\r\n this._statusElm = document.createElement('span');\r\n this._statusElm.className = 'slick-pager-status';\r\n\r\n const pagerSettingsElm = document.createElement('span');\r\n pagerSettingsElm.className = 'slick-pager-settings-expanded';\r\n pagerSettingsElm.textContent = 'Show: ';\r\n\r\n for (let o = 0; o < this._options.pagingOptions.length; o++) {\r\n const p = this._options.pagingOptions[o];\r\n\r\n const anchorElm = document.createElement('a');\r\n anchorElm.textContent = p.name;\r\n anchorElm.ariaLabel = p.ariaLabel;\r\n anchorElm.dataset.val = String(p.data);\r\n pagerSettingsElm.appendChild(anchorElm);\r\n\r\n this._bindingEventService.bind(anchorElm, 'click', ((e: any) => {\r\n const pagesize = e.target.dataset.val;\r\n if (pagesize !== undefined) {\r\n if (Number(pagesize) === -1) {\r\n const vp = this.grid.getViewport();\r\n this.setPageSize(vp.bottom - vp.top);\r\n } else {\r\n this.setPageSize(parseInt(pagesize));\r\n }\r\n }\r\n }));\r\n }\r\n\r\n pagerSettingsElm.style.display = this._options.showPageSizes ? 'block' : 'none';\r\n\r\n settingsElm.appendChild(pagerSettingsElm);\r\n\r\n // light bulb icon\r\n const displayPaginationContainer = document.createElement('span');\r\n const displayIconElm = document.createElement('span');\r\n displayPaginationContainer.className = 'sgi-container';\r\n displayIconElm.ariaLabel = 'Show Pagination Options';\r\n displayIconElm.role = 'button';\r\n displayIconElm.className = 'sgi sgi-lightbulb';\r\n displayPaginationContainer.appendChild(displayIconElm);\r\n\r\n this._bindingEventService.bind(displayIconElm, 'click', () => {\r\n const styleDisplay = pagerSettingsElm.style.display;\r\n pagerSettingsElm.style.display = styleDisplay === 'none' ? 'inline-flex' : 'none';\r\n });\r\n settingsElm.appendChild(displayPaginationContainer);\r\n\r\n const pageButtons = [\r\n { key: 'start', ariaLabel: 'First Page', callback: this.gotoFirst },\r\n { key: 'left', ariaLabel: 'Previous Page', callback: this.gotoPrev },\r\n { key: 'right', ariaLabel: 'Next Page', callback: this.gotoNext },\r\n { key: 'end', ariaLabel: 'Last Page', callback: this.gotoLast },\r\n ];\r\n\r\n pageButtons.forEach(pageBtn => {\r\n const iconElm = document.createElement('span');\r\n iconElm.className = 'sgi-container';\r\n\r\n const innerIconElm = document.createElement('span');\r\n innerIconElm.role = 'button';\r\n innerIconElm.ariaLabel = pageBtn.ariaLabel;\r\n innerIconElm.className = `sgi sgi-chevron-${pageBtn.key}`;\r\n this._bindingEventService.bind(innerIconElm, 'click', pageBtn.callback.bind(this));\r\n\r\n iconElm.appendChild(innerIconElm);\r\n navElm.appendChild(iconElm);\r\n });\r\n\r\n const slickPagerElm = document.createElement('div');\r\n slickPagerElm.className = 'slick-pager';\r\n\r\n slickPagerElm.appendChild(navElm);\r\n slickPagerElm.appendChild(this._statusElm);\r\n slickPagerElm.appendChild(settingsElm);\r\n\r\n (container as HTMLElement).appendChild(slickPagerElm);\r\n }\r\n\r\n protected updatePager(pagingInfo: PagingInfo) {\r\n if (!this._container || ((this._container as any).jquery && !(this._container as any)[0])) { return; }\r\n const state = this.getNavState();\r\n\r\n // remove disabled class on all icons\r\n this._container.querySelectorAll('.slick-pager-nav span')\r\n .forEach(pagerIcon => pagerIcon.classList.remove('sgi-state-disabled'));\r\n\r\n // add back disabled class to only necessary icons\r\n if (!state.canGotoFirst) {\r\n this._container!.querySelector('.sgi-chevron-start')?.classList.add('sgi-state-disabled');\r\n }\r\n if (!state.canGotoLast) {\r\n this._container!.querySelector('.sgi-chevron-end')?.classList.add('sgi-state-disabled');\r\n }\r\n if (!state.canGotoNext) {\r\n this._container!.querySelector('.sgi-chevron-right')?.classList.add('sgi-state-disabled');\r\n }\r\n if (!state.canGotoPrev) {\r\n this._container!.querySelector('.sgi-chevron-left')?.classList.add('sgi-state-disabled');\r\n }\r\n\r\n if (pagingInfo.pageSize === 0) {\r\n this._statusElm.textContent = (this._options.showAllText.replace('{rowCount}', pagingInfo.totalRows + '').replace('{pageCount}', pagingInfo.totalPages + ''));\r\n } else {\r\n this._statusElm.textContent = (this._options.showPageText.replace('{pageNum}', pagingInfo.pageNum + 1 + '').replace('{pageCount}', pagingInfo.totalPages + ''));\r\n }\r\n\r\n if (this._options.showCount && pagingInfo.pageSize !== 0) {\r\n const pageBegin = pagingInfo.pageNum * pagingInfo.pageSize;\r\n let currentText = this._statusElm.textContent;\r\n\r\n if (currentText) {\r\n currentText += ' - ';\r\n }\r\n\r\n this._statusElm.textContent =\r\n currentText +\r\n this._options.showCountText\r\n .replace('{rowCount}', String(pagingInfo.totalRows))\r\n .replace('{countBegin}', String(pageBegin + 1))\r\n .replace('{countEnd}', String(Math.min(pageBegin + pagingInfo.pageSize, pagingInfo.totalRows)));\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 window.Slick.Controls = window.Slick.Controls || {};\r\n window.Slick.Controls.Pager = SlickGridPager;\r\n}\r\n\r\n"], "mappings": "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", "names": [] } diff --git a/dist/browser/plugins/slick.autotooltips.js.map b/dist/browser/plugins/slick.autotooltips.js.map index 641ca7f1..2cb95327 100644 --- a/dist/browser/plugins/slick.autotooltips.js.map +++ b/dist/browser/plugins/slick.autotooltips.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../../../src/plugins/slick.autotooltips.ts"], - "sourcesContent": ["import type { AutoTooltipOption, Column, SlickPlugin } from '../models/index';\nimport { type SlickEventData, Utils as Utils_ } from '../slick.core';\nimport type { SlickGrid } from '../slick.grid';\n\n// for (iife) load Slick methods from global Slick object, or use imports for (esm)\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\n\n/**\n * AutoTooltips plugin to show/hide tooltips when columns are too narrow to fit content.\n */\nexport class SlickAutoTooltips implements SlickPlugin {\n // --\n // public API\n pluginName = 'AutoTooltips' as const;\n\n // --\n // protected props\n protected _grid!: SlickGrid;\n protected _options?: AutoTooltipOption;\n protected _defaults: AutoTooltipOption = {\n enableForCells: true,\n enableForHeaderCells: false,\n maxToolTipLength: undefined,\n replaceExisting: true\n };\n\n /**\n * Constructor of the SlickGrid 3rd party plugin, it can optionally receive options\n * @param {boolean} [options.enableForCells=true] - Enable tooltip for grid cells\n * @param {boolean} [options.enableForHeaderCells=false] - Enable tooltip for header cells\n * @param {number} [options.maxToolTipLength=null] - The maximum length for a tooltip\n * @param {boolean} [options.replaceExisting=null] - Allow preventing custom tooltips from being overwritten by auto tooltips\n */\n constructor(options?: AutoTooltipOption) {\n this._options = Utils.extend(true, {}, this._defaults, options);\n }\n\n /**\n * Initialize plugin.\n */\n init(grid: SlickGrid) {\n this._grid = grid;\n if (this._options?.enableForCells) {\n this._grid.onMouseEnter.subscribe(this.handleMouseEnter.bind(this));\n }\n if (this._options?.enableForHeaderCells) {\n this._grid.onHeaderMouseEnter.subscribe(this.handleHeaderMouseEnter.bind(this));\n }\n }\n\n /**\n * Destroy plugin.\n */\n destroy() {\n if (this._options?.enableForCells) {\n this._grid.onMouseEnter.unsubscribe(this.handleMouseEnter.bind(this));\n }\n if (this._options?.enableForHeaderCells) {\n this._grid.onHeaderMouseEnter.unsubscribe(this.handleHeaderMouseEnter.bind(this));\n }\n }\n\n /**\n * Handle mouse entering grid cell to add/remove tooltip.\n * @param {SlickEventData} event - The event\n */\n protected handleMouseEnter(event: SlickEventData) {\n const cell = this._grid.getCellFromEvent(event);\n if (cell) {\n let node: HTMLElement | null = this._grid.getCellNode(cell.row, cell.cell);\n let text;\n if (this._options && node && (!node.title || this._options?.replaceExisting)) {\n if (node.clientWidth < node.scrollWidth) {\n text = node.textContent?.trim() ?? '';\n if (this._options?.maxToolTipLength && text.length > this._options?.maxToolTipLength) {\n text = text.substring(0, this._options.maxToolTipLength - 3) + '...';\n }\n } else {\n text = '';\n }\n node.title = text;\n }\n node = null;\n }\n }\n\n /**\n * Handle mouse entering header cell to add/remove tooltip.\n * @param {SlickEventData} event - The event\n * @param {object} args.column - The column definition\n */\n protected handleHeaderMouseEnter(event: SlickEventData, args: { column: Column; }) {\n const column = args.column;\n let node: HTMLDivElement | null;\n const targetElm = (event.target as HTMLDivElement);\n\n if (targetElm) {\n node = targetElm.closest('.slick-header-column');\n if (node && !(column?.toolTip)) {\n const titleVal = (targetElm.clientWidth < node.clientWidth) ? column?.name ?? '' : '';\n node.title = Utils.getHtmlStringOutput(titleVal, 'innerHTML');\n }\n }\n node = null;\n }\n}\n\n// extend Slick namespace on window object when building as iife\nif (IIFE_ONLY && window.Slick) {\n Utils.extend(true, window, {\n Slick: {\n AutoTooltips: SlickAutoTooltips\n }\n });\n}\n"], + "sourcesContent": ["import type { AutoTooltipOption, Column, SlickPlugin } from '../models/index.js';\r\nimport { type SlickEventData, Utils as Utils_ } 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 * AutoTooltips plugin to show/hide tooltips when columns are too narrow to fit content.\r\n */\r\nexport class SlickAutoTooltips implements SlickPlugin {\r\n // --\r\n // public API\r\n pluginName = 'AutoTooltips' as const;\r\n\r\n // --\r\n // protected props\r\n protected _grid!: SlickGrid;\r\n protected _options?: AutoTooltipOption;\r\n protected _defaults: AutoTooltipOption = {\r\n enableForCells: true,\r\n enableForHeaderCells: false,\r\n maxToolTipLength: undefined,\r\n replaceExisting: true\r\n };\r\n\r\n /**\r\n * Constructor of the SlickGrid 3rd party plugin, it can optionally receive options\r\n * @param {boolean} [options.enableForCells=true] - Enable tooltip for grid cells\r\n * @param {boolean} [options.enableForHeaderCells=false] - Enable tooltip for header cells\r\n * @param {number} [options.maxToolTipLength=null] - The maximum length for a tooltip\r\n * @param {boolean} [options.replaceExisting=null] - Allow preventing custom tooltips from being overwritten by auto tooltips\r\n */\r\n constructor(options?: AutoTooltipOption) {\r\n this._options = Utils.extend(true, {}, this._defaults, options);\r\n }\r\n\r\n /**\r\n * Initialize plugin.\r\n */\r\n init(grid: SlickGrid) {\r\n this._grid = grid;\r\n if (this._options?.enableForCells) {\r\n this._grid.onMouseEnter.subscribe(this.handleMouseEnter.bind(this));\r\n }\r\n if (this._options?.enableForHeaderCells) {\r\n this._grid.onHeaderMouseEnter.subscribe(this.handleHeaderMouseEnter.bind(this));\r\n }\r\n }\r\n\r\n /**\r\n * Destroy plugin.\r\n */\r\n destroy() {\r\n if (this._options?.enableForCells) {\r\n this._grid.onMouseEnter.unsubscribe(this.handleMouseEnter.bind(this));\r\n }\r\n if (this._options?.enableForHeaderCells) {\r\n this._grid.onHeaderMouseEnter.unsubscribe(this.handleHeaderMouseEnter.bind(this));\r\n }\r\n }\r\n\r\n /**\r\n * Handle mouse entering grid cell to add/remove tooltip.\r\n * @param {SlickEventData} event - The event\r\n */\r\n protected handleMouseEnter(event: SlickEventData) {\r\n const cell = this._grid.getCellFromEvent(event);\r\n if (cell) {\r\n let node: HTMLElement | null = this._grid.getCellNode(cell.row, cell.cell);\r\n let text;\r\n if (this._options && node && (!node.title || this._options?.replaceExisting)) {\r\n if (node.clientWidth < node.scrollWidth) {\r\n text = node.textContent?.trim() ?? '';\r\n if (this._options?.maxToolTipLength && text.length > this._options?.maxToolTipLength) {\r\n text = text.substring(0, this._options.maxToolTipLength - 3) + '...';\r\n }\r\n } else {\r\n text = '';\r\n }\r\n node.title = text;\r\n }\r\n node = null;\r\n }\r\n }\r\n\r\n /**\r\n * Handle mouse entering header cell to add/remove tooltip.\r\n * @param {SlickEventData} event - The event\r\n * @param {object} args.column - The column definition\r\n */\r\n protected handleHeaderMouseEnter(event: SlickEventData, args: { column: Column; }) {\r\n const column = args.column;\r\n let node: HTMLDivElement | null;\r\n const targetElm = (event.target as HTMLDivElement);\r\n\r\n if (targetElm) {\r\n node = targetElm.closest('.slick-header-column');\r\n if (node && !(column?.toolTip)) {\r\n const titleVal = (targetElm.clientWidth < node.clientWidth) ? column?.name ?? '' : '';\r\n node.title = Utils.getHtmlStringOutput(titleVal, 'innerHTML');\r\n }\r\n }\r\n node = null;\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 AutoTooltips: SlickAutoTooltips\r\n }\r\n });\r\n}\r\n"], "mappings": ";;;;;;;AAKA,MAAM,QAAoB,MAAM,OAKnB,oBAAN,MAA+C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAuBpD,YAAY,SAA6B;AApBzC;AAAA;AAAA,wCAAa;AAIb;AAAA;AAAA,0BAAU;AACV,0BAAU;AACV,0BAAU,aAA+B;AAAA,QACvC,gBAAgB;AAAA,QAChB,sBAAsB;AAAA,QACtB,kBAAkB;AAAA,QAClB,iBAAiB;AAAA,MACnB;AAUE,WAAK,WAAW,MAAM,OAAO,IAAM,CAAC,GAAG,KAAK,WAAW,OAAO;AAAA,IAChE;AAAA;AAAA;AAAA;AAAA,IAKA,KAAK,MAAiB;AAxCxB;AAyCI,WAAK,QAAQ,OACT,UAAK,aAAL,WAAe,kBACjB,KAAK,MAAM,aAAa,UAAU,KAAK,iBAAiB,KAAK,IAAI,CAAC,IAEhE,UAAK,aAAL,WAAe,wBACjB,KAAK,MAAM,mBAAmB,UAAU,KAAK,uBAAuB,KAAK,IAAI,CAAC;AAAA,IAElF;AAAA;AAAA;AAAA;AAAA,IAKA,UAAU;AArDZ;AAsDI,OAAI,UAAK,aAAL,WAAe,kBACjB,KAAK,MAAM,aAAa,YAAY,KAAK,iBAAiB,KAAK,IAAI,CAAC,IAElE,UAAK,aAAL,WAAe,wBACjB,KAAK,MAAM,mBAAmB,YAAY,KAAK,uBAAuB,KAAK,IAAI,CAAC;AAAA,IAEpF;AAAA;AAAA;AAAA;AAAA;AAAA,IAMU,iBAAiB,OAAuB;AAlEpD;AAmEI,UAAM,OAAO,KAAK,MAAM,iBAAiB,KAAK;AAC9C,UAAI,MAAM;AACR,YAAI,OAA2B,KAAK,MAAM,YAAY,KAAK,KAAK,KAAK,IAAI,GACrE;AACJ,QAAI,KAAK,YAAY,SAAS,CAAC,KAAK,UAAS,UAAK,aAAL,WAAe,qBACtD,KAAK,cAAc,KAAK,eAC1B,QAAO,gBAAK,gBAAL,mBAAkB,WAAlB,YAA4B,KAC/B,UAAK,aAAL,WAAe,oBAAoB,KAAK,WAAS,UAAK,aAAL,mBAAe,sBAClE,OAAO,KAAK,UAAU,GAAG,KAAK,SAAS,mBAAmB,CAAC,IAAI,UAGjE,OAAO,IAET,KAAK,QAAQ,OAEf,OAAO;AAAA,MACT;AAAA,IACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOU,uBAAuB,OAAuB,MAA2B;AA3FrF;AA4FI,UAAM,SAAS,KAAK,QAChB,MACE,YAAa,MAAM;AAEzB,UAAI,cACF,OAAO,UAAU,QAAwB,sBAAsB,GAC3D,QAAQ,EAAE,yBAAQ,WAAU;AAC9B,YAAM,WAAY,UAAU,cAAc,KAAK,gBAAe,sCAAQ,SAAR,YAAqB;AACnF,aAAK,QAAQ,MAAM,oBAAoB,UAAU,WAAW;AAAA,MAC9D;AAEF,aAAO;AAAA,IACT;AAAA,EACF;AAGA,EAAiB,OAAO,SACtB,MAAM,OAAO,IAAM,QAAQ;AAAA,IACzB,OAAO;AAAA,MACL,cAAc;AAAA,IAChB;AAAA,EACF,CAAC;", "names": [] } diff --git a/dist/browser/plugins/slick.cellcopymanager.js.map b/dist/browser/plugins/slick.cellcopymanager.js.map index b4688ac0..c30adecb 100644 --- a/dist/browser/plugins/slick.cellcopymanager.js.map +++ b/dist/browser/plugins/slick.cellcopymanager.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../../../src/plugins/slick.cellcopymanager.ts"], - "sourcesContent": ["import type { CssStyleHash, SlickPlugin } from '../models/index';\nimport { keyCode as keyCode_, SlickEvent as SlickEvent_, type SlickEventData, Utils as Utils_, type SlickRange } from '../slick.core';\nimport type { SlickGrid } from '../slick.grid';\n\n// for (iife) load Slick methods from global Slick object, or use imports for (esm)\nconst keyCode = IIFE_ONLY ? Slick.keyCode : keyCode_;\nconst SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_;\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\n\n/**\n * This manager enables users to copy/paste cell data\n */\nexport class SlickCellCopyManager implements SlickPlugin {\n // --\n // public API\n pluginName = 'CellCopyManager' as const;\n onCopyCells = new SlickEvent<{ ranges: SlickRange[] | null; }>('onCopyCells');\n onCopyCancelled = new SlickEvent<{ ranges: SlickRange[] | null; }>('onCopyCancelled');\n onPasteCells = new SlickEvent<{ from: SlickRange[] | undefined; to: SlickRange[] | undefined; }>('onPasteCells');\n\n // --\n // protected props\n protected _grid!: SlickGrid;\n protected _copiedRanges?: SlickRange[] | null = null;\n\n init(grid: SlickGrid) {\n this._grid = grid;\n Utils.addSlickEventPubSubWhenDefined(grid.getPubSubService(), this);\n this._grid.onKeyDown.subscribe(this.handleKeyDown.bind(this));\n }\n\n destroy() {\n this._grid.onKeyDown.unsubscribe(this.handleKeyDown.bind(this));\n }\n\n protected handleKeyDown(e: SlickEventData) {\n let ranges: SlickRange[] | undefined;\n if (!this._grid.getEditorLock().isActive()) {\n if (e.which === keyCode.ESCAPE) {\n if (this._copiedRanges) {\n e.preventDefault();\n this.clearCopySelection();\n this.onCopyCancelled.notify({ ranges: this._copiedRanges });\n this._copiedRanges = null;\n }\n }\n\n if (e.which === 67 && (e.ctrlKey || e.metaKey)) {\n ranges = this._grid.getSelectionModel()?.getSelectedRanges() ?? [];\n if (ranges.length !== 0) {\n e.preventDefault();\n this._copiedRanges = ranges;\n this.markCopySelection(ranges);\n this.onCopyCells.notify({ ranges });\n }\n }\n\n if (e.which === 86 && (e.ctrlKey || e.metaKey)) {\n if (this._copiedRanges) {\n e.preventDefault();\n ranges = this._grid.getSelectionModel()?.getSelectedRanges();\n this.onPasteCells.notify({ from: this._copiedRanges, to: ranges });\n if (!this._grid.getOptions().preserveCopiedSelectionOnPaste) {\n this.clearCopySelection();\n this._copiedRanges = null;\n }\n }\n }\n }\n }\n\n protected markCopySelection(ranges: SlickRange[]) {\n const columns = this._grid.getColumns();\n const hash: CssStyleHash = {};\n for (let i = 0; i < ranges.length; i++) {\n for (let j = ranges[i].fromRow; j <= ranges[i].toRow; j++) {\n hash[j] = {};\n for (let k = ranges[i].fromCell; k <= ranges[i].toCell; k++) {\n hash[j][columns[k].id] = 'copied';\n }\n }\n }\n this._grid.setCellCssStyles('copy-manager', hash);\n }\n\n protected clearCopySelection() {\n this._grid.removeCellCssStyles('copy-manager');\n }\n}\n\n// extend Slick namespace on window object when building as iife\nif (IIFE_ONLY && window.Slick) {\n Utils.extend(true, window, {\n Slick: {\n CellCopyManager: SlickCellCopyManager\n }\n });\n}\n"], + "sourcesContent": ["import type { CssStyleHash, SlickPlugin } from '../models/index.js';\r\nimport { keyCode as keyCode_, SlickEvent as SlickEvent_, type SlickEventData, 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 keyCode = IIFE_ONLY ? Slick.keyCode : keyCode_;\r\nconst SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_;\r\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\r\n\r\n/**\r\n * This manager enables users to copy/paste cell data\r\n */\r\nexport class SlickCellCopyManager implements SlickPlugin {\r\n // --\r\n // public API\r\n pluginName = 'CellCopyManager' as const;\r\n onCopyCells = new SlickEvent<{ ranges: SlickRange[] | null; }>('onCopyCells');\r\n onCopyCancelled = new SlickEvent<{ ranges: SlickRange[] | null; }>('onCopyCancelled');\r\n onPasteCells = new SlickEvent<{ from: SlickRange[] | undefined; to: SlickRange[] | undefined; }>('onPasteCells');\r\n\r\n // --\r\n // protected props\r\n protected _grid!: SlickGrid;\r\n protected _copiedRanges?: SlickRange[] | null = null;\r\n\r\n init(grid: SlickGrid) {\r\n this._grid = grid;\r\n Utils.addSlickEventPubSubWhenDefined(grid.getPubSubService(), this);\r\n this._grid.onKeyDown.subscribe(this.handleKeyDown.bind(this));\r\n }\r\n\r\n destroy() {\r\n this._grid.onKeyDown.unsubscribe(this.handleKeyDown.bind(this));\r\n }\r\n\r\n protected handleKeyDown(e: SlickEventData) {\r\n let ranges: SlickRange[] | undefined;\r\n if (!this._grid.getEditorLock().isActive()) {\r\n if (e.which === keyCode.ESCAPE) {\r\n if (this._copiedRanges) {\r\n e.preventDefault();\r\n this.clearCopySelection();\r\n this.onCopyCancelled.notify({ ranges: this._copiedRanges });\r\n this._copiedRanges = null;\r\n }\r\n }\r\n\r\n if (e.which === 67 && (e.ctrlKey || e.metaKey)) {\r\n ranges = this._grid.getSelectionModel()?.getSelectedRanges() ?? [];\r\n if (ranges.length !== 0) {\r\n e.preventDefault();\r\n this._copiedRanges = ranges;\r\n this.markCopySelection(ranges);\r\n this.onCopyCells.notify({ ranges });\r\n }\r\n }\r\n\r\n if (e.which === 86 && (e.ctrlKey || e.metaKey)) {\r\n if (this._copiedRanges) {\r\n e.preventDefault();\r\n ranges = this._grid.getSelectionModel()?.getSelectedRanges();\r\n this.onPasteCells.notify({ from: this._copiedRanges, to: ranges });\r\n if (!this._grid.getOptions().preserveCopiedSelectionOnPaste) {\r\n this.clearCopySelection();\r\n this._copiedRanges = null;\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n protected markCopySelection(ranges: SlickRange[]) {\r\n const columns = this._grid.getColumns();\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 hash[j] = {};\r\n for (let k = ranges[i].fromCell; k <= ranges[i].toCell; k++) {\r\n hash[j][columns[k].id] = 'copied';\r\n }\r\n }\r\n }\r\n this._grid.setCellCssStyles('copy-manager', hash);\r\n }\r\n\r\n protected clearCopySelection() {\r\n this._grid.removeCellCssStyles('copy-manager');\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 CellCopyManager: SlickCellCopyManager\r\n }\r\n });\r\n}\r\n"], "mappings": ";;;;;;;AAKA,MAAM,UAAsB,MAAM,SAC5B,aAAyB,MAAM,OAC/B,QAAoB,MAAM,OAKnB,uBAAN,MAAkD;AAAA,IAAlD;AAGL;AAAA;AAAA,wCAAa;AACb,yCAAc,IAAI,WAA6C,aAAa;AAC5E,6CAAkB,IAAI,WAA6C,iBAAiB;AACpF,0CAAe,IAAI,WAA8E,cAAc;AAI/G;AAAA;AAAA,0BAAU;AACV,0BAAU,iBAAsC;AAAA;AAAA,IAEhD,KAAK,MAAiB;AACpB,WAAK,QAAQ,MACb,MAAM,+BAA+B,KAAK,iBAAiB,GAAG,IAAI,GAClE,KAAK,MAAM,UAAU,UAAU,KAAK,cAAc,KAAK,IAAI,CAAC;AAAA,IAC9D;AAAA,IAEA,UAAU;AACR,WAAK,MAAM,UAAU,YAAY,KAAK,cAAc,KAAK,IAAI,CAAC;AAAA,IAChE;AAAA,IAEU,cAAc,GAAmB;AAnC7C;AAoCI,UAAI;AACJ,MAAK,KAAK,MAAM,cAAc,EAAE,SAAS,MACnC,EAAE,UAAU,QAAQ,UAClB,KAAK,kBACP,EAAE,eAAe,GACjB,KAAK,mBAAmB,GACxB,KAAK,gBAAgB,OAAO,EAAE,QAAQ,KAAK,cAAc,CAAC,GAC1D,KAAK,gBAAgB,OAIrB,EAAE,UAAU,OAAO,EAAE,WAAW,EAAE,aACpC,UAAS,gBAAK,MAAM,kBAAkB,MAA7B,mBAAgC,wBAAhC,YAAuD,CAAC,GAC7D,OAAO,WAAW,MACpB,EAAE,eAAe,GACjB,KAAK,gBAAgB,QACrB,KAAK,kBAAkB,MAAM,GAC7B,KAAK,YAAY,OAAO,EAAE,OAAO,CAAC,KAIlC,EAAE,UAAU,OAAO,EAAE,WAAW,EAAE,YAChC,KAAK,kBACP,EAAE,eAAe,GACjB,UAAS,UAAK,MAAM,kBAAkB,MAA7B,mBAAgC,qBACzC,KAAK,aAAa,OAAO,EAAE,MAAM,KAAK,eAAe,IAAI,OAAO,CAAC,GAC5D,KAAK,MAAM,WAAW,EAAE,mCAC3B,KAAK,mBAAmB,GACxB,KAAK,gBAAgB;AAAA,IAK/B;AAAA,IAEU,kBAAkB,QAAsB;AAChD,UAAM,UAAU,KAAK,MAAM,WAAW,GAChC,OAAqB,CAAC;AAC5B,eAAS,IAAI,GAAG,IAAI,OAAO,QAAQ;AACjC,iBAAS,IAAI,OAAO,CAAC,EAAE,SAAS,KAAK,OAAO,CAAC,EAAE,OAAO,KAAK;AACzD,eAAK,CAAC,IAAI,CAAC;AACX,mBAAS,IAAI,OAAO,CAAC,EAAE,UAAU,KAAK,OAAO,CAAC,EAAE,QAAQ;AACtD,iBAAK,CAAC,EAAE,QAAQ,CAAC,EAAE,EAAE,IAAI;AAAA,QAE7B;AAEF,WAAK,MAAM,iBAAiB,gBAAgB,IAAI;AAAA,IAClD;AAAA,IAEU,qBAAqB;AAC7B,WAAK,MAAM,oBAAoB,cAAc;AAAA,IAC/C;AAAA,EACF;AAGA,EAAiB,OAAO,SACtB,MAAM,OAAO,IAAM,QAAQ;AAAA,IACzB,OAAO;AAAA,MACL,iBAAiB;AAAA,IACnB;AAAA,EACF,CAAC;", "names": [] } diff --git a/dist/browser/plugins/slick.cellexternalcopymanager.js.map b/dist/browser/plugins/slick.cellexternalcopymanager.js.map index d3b20b00..de1b88f6 100644 --- a/dist/browser/plugins/slick.cellexternalcopymanager.js.map +++ b/dist/browser/plugins/slick.cellexternalcopymanager.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../../../src/plugins/slick.cellexternalcopymanager.ts"], - "sourcesContent": ["import type { Column, CssStyleHash, ExcelCopyBufferOption, ExternalCopyClipCommand, SlickPlugin } from '../models/index';\r\nimport type { SlickGrid } from '../slick.grid';\r\nimport { SlickEvent as SlickEvent_, type SlickEventData, SlickRange as SlickRange_, Utils as Utils_ } from '../slick.core';\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 SlickRange = IIFE_ONLY ? Slick.Range : SlickRange_;\r\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\r\n\r\nconst CLEAR_COPY_SELECTION_DELAY = 2000;\r\nconst CLIPBOARD_PASTE_DELAY = 100;\r\n\r\n/***\r\n This manager enables users to copy/paste data from/to an external Spreadsheet application\r\n such as MS-Excel\u00AE or OpenOffice-Spreadsheet.\r\n\r\n Since it is not possible to access directly the clipboard in javascript, the plugin uses\r\n a trick to do it's job. After detecting the keystroke, we dynamically create a textarea\r\n where the browser copies/pastes the serialized data.\r\n\r\n options:\r\n copiedCellStyle : sets the css className used for copied cells. default : \"copied\"\r\n copiedCellStyleLayerKey : sets the layer key for setting css values of copied cells. default : \"copy-manager\"\r\n dataItemColumnValueExtractor : option to specify a custom column value extractor function\r\n dataItemColumnValueSetter : option to specify a custom column value setter function\r\n clipboardCommandHandler : option to specify a custom handler for paste actions\r\n includeHeaderWhenCopying : set to true and the plugin will take the name property from each column (which is usually what appears in your header) and put that as the first row of the text that's copied to the clipboard\r\n bodyElement: option to specify a custom DOM element which to will be added the hidden textbox. It's useful if the grid is inside a modal dialog.\r\n onCopyInit: optional handler to run when copy action initializes\r\n onCopySuccess: optional handler to run when copy action is complete\r\n newRowCreator: function to add rows to table if paste overflows bottom of table, if this function is not provided new rows will be ignored.\r\n readOnlyMode: suppresses paste\r\n headerColumnValueExtractor : option to specify a custom column header value extractor function\r\n*/\r\nexport class SlickCellExternalCopyManager implements SlickPlugin {\r\n // --\r\n // public API\r\n pluginName = 'CellExternalCopyManager' as const;\r\n onCopyCells = new SlickEvent<{ ranges: SlickRange_[]; }>('onCopyCells');\r\n onCopyCancelled = new SlickEvent<{ ranges: SlickRange_[]; }>('onCopyCancelled');\r\n onPasteCells = new SlickEvent<{ ranges: SlickRange_[]; }>('onPasteCells');\r\n\r\n // --\r\n // protected props\r\n protected _grid!: SlickGrid;\r\n protected _bodyElement: HTMLElement;\r\n protected _copiedRanges: SlickRange_[] | null = null;\r\n protected _clearCopyTI?: number;\r\n protected _copiedCellStyle: string;\r\n protected _copiedCellStyleLayerKey: string;\r\n protected _onCopyInit?: () => void;\r\n protected _onCopySuccess?: (rowCount: number) => void;\r\n protected _options: ExcelCopyBufferOption;\r\n\r\n protected keyCodes = {\r\n 'C': 67,\r\n 'V': 86,\r\n 'ESC': 27,\r\n 'INSERT': 45\r\n };\r\n\r\n constructor(options: ExcelCopyBufferOption) {\r\n this._options = options || {};\r\n this._copiedCellStyleLayerKey = this._options.copiedCellStyleLayerKey || 'copy-manager';\r\n this._copiedCellStyle = this._options.copiedCellStyle || 'copied';\r\n this._bodyElement = this._options.bodyElement || document.body;\r\n this._onCopyInit = this._options.onCopyInit || undefined;\r\n this._onCopySuccess = this._options.onCopySuccess || undefined;\r\n }\r\n\r\n init(grid: SlickGrid) {\r\n this._grid = grid;\r\n Utils.addSlickEventPubSubWhenDefined(grid.getPubSubService(), this);\r\n this._grid.onKeyDown.subscribe(this.handleKeyDown.bind(this));\r\n\r\n // we need a cell selection model\r\n const cellSelectionModel = grid.getSelectionModel();\r\n if (!cellSelectionModel) {\r\n throw new Error('Selection model is mandatory for this plugin. Please set a selection model on the grid before adding this plugin: grid.setSelectionModel(new Slick.CellSelectionModel())');\r\n }\r\n // we give focus on the grid when a selection is done on it.\r\n // without this, if the user selects a range of cell without giving focus on a particular cell, the grid doesn't get the focus and key stroke handles (ctrl+c) don't work\r\n cellSelectionModel.onSelectedRangesChanged.subscribe(() => {\r\n if (!this._grid.getEditorLock().isActive()) {\r\n this._grid.focus();\r\n }\r\n });\r\n }\r\n\r\n destroy() {\r\n this._grid.onKeyDown.unsubscribe(this.handleKeyDown.bind(this));\r\n }\r\n\r\n protected getHeaderValueForColumn(columnDef: Column): string {\r\n if (this._options.headerColumnValueExtractor) {\r\n const val = Utils.getHtmlStringOutput(this._options.headerColumnValueExtractor(columnDef));\r\n if (val) {\r\n return val;\r\n }\r\n }\r\n\r\n return Utils.getHtmlStringOutput(columnDef.name || '');\r\n }\r\n\r\n protected getDataItemValueForColumn(item: any, columnDef: Column, event: SlickEventData): string {\r\n if (typeof this._options.dataItemColumnValueExtractor === 'function') {\r\n const val = this._options.dataItemColumnValueExtractor(item, columnDef) as string | null;\r\n if (val) {\r\n return val;\r\n }\r\n }\r\n\r\n let retVal = '';\r\n\r\n // if a custom getter is not defined, we call serializeValue of the editor to serialize\r\n if (columnDef?.editor) {\r\n const tmpP = document.createElement('p');\r\n const editor = new (columnDef.editor as any)({\r\n container: tmpP, // a dummy container\r\n column: columnDef,\r\n event,\r\n position: { top: 0, left: 0 }, // a dummy position required by some editors\r\n grid: this._grid,\r\n });\r\n editor.loadValue(item);\r\n retVal = editor.serializeValue();\r\n editor.destroy();\r\n tmpP.remove();\r\n } else {\r\n retVal = item[columnDef.field || ''];\r\n }\r\n\r\n return retVal;\r\n }\r\n\r\n protected setDataItemValueForColumn(item: any, columnDef: Column, value: string): null | string | void {\r\n if (columnDef.denyPaste) {\r\n return null;\r\n }\r\n\r\n if (this._options.dataItemColumnValueSetter) {\r\n return this._options.dataItemColumnValueSetter(item, columnDef, value) as string;\r\n }\r\n\r\n // if a custom setter is not defined, we call applyValue of the editor to unserialize\r\n if (columnDef.editor) {\r\n const tmpDiv = document.createElement('div');\r\n const editor = new (columnDef.editor as any)({\r\n container: tmpDiv, // a dummy container\r\n column: columnDef,\r\n position: { top: 0, left: 0 }, // a dummy position required by some editors\r\n grid: this._grid\r\n });\r\n editor.loadValue(item);\r\n editor.applyValue(item, value);\r\n editor.destroy();\r\n tmpDiv.remove();\r\n } else {\r\n item[columnDef.field] = value;\r\n }\r\n }\r\n\r\n\r\n protected _createTextBox(innerText: string) {\r\n const scrollPos = document.documentElement.scrollTop || document.body.scrollTop;\r\n const ta = document.createElement('textarea');\r\n ta.style.position = 'absolute';\r\n ta.style.opacity = '0';\r\n ta.value = innerText;\r\n ta.style.top = `${scrollPos}px`;\r\n this._bodyElement.appendChild(ta);\r\n ta.select();\r\n\r\n return ta;\r\n }\r\n\r\n protected _decodeTabularData(grid: SlickGrid, ta: HTMLTextAreaElement) {\r\n const columns = grid.getColumns();\r\n const clipText = ta.value;\r\n const clipRows = clipText.split(/[\\n\\f\\r]/);\r\n // trim trailing CR if present\r\n if (clipRows[clipRows.length - 1] === '') {\r\n clipRows.pop();\r\n }\r\n\r\n let j = 0;\r\n const clippedRange: any[] = [];\r\n\r\n this._bodyElement.removeChild(ta);\r\n for (let i = 0; i < clipRows.length; i++) {\r\n if (clipRows[i] !== '') {\r\n clippedRange[j++] = clipRows[i].split('\\t');\r\n } else {\r\n clippedRange[j++] = [''];\r\n }\r\n }\r\n const selectedCell = grid.getActiveCell();\r\n const ranges = grid.getSelectionModel()?.getSelectedRanges();\r\n const selectedRange = ranges && ranges.length ? ranges[0] : null; // pick only one selection\r\n let activeRow: number;\r\n let activeCell: number;\r\n\r\n if (selectedRange) {\r\n activeRow = selectedRange.fromRow;\r\n activeCell = selectedRange.fromCell;\r\n } else if (selectedCell) {\r\n activeRow = selectedCell.row;\r\n activeCell = selectedCell.cell;\r\n } else {\r\n // we don't know where to paste\r\n return;\r\n }\r\n\r\n let oneCellToMultiple = false;\r\n let destH = clippedRange.length;\r\n let destW = clippedRange.length ? clippedRange[0].length : 0;\r\n if (clippedRange.length === 1 && clippedRange[0].length === 1 && selectedRange) {\r\n oneCellToMultiple = true;\r\n destH = selectedRange.toRow - selectedRange.fromRow + 1;\r\n destW = selectedRange.toCell - selectedRange.fromCell + 1;\r\n }\r\n const availableRows = (grid.getData() as any[]).length - (activeRow || 0);\r\n let addRows = 0;\r\n\r\n // ignore new rows if we don't have a \"newRowCreator\"\r\n if (availableRows < destH && typeof this._options.newRowCreator === 'function') {\r\n const d = grid.getData();\r\n for (addRows = 1; addRows <= destH - availableRows; addRows++) {\r\n d.push({});\r\n }\r\n grid.setData(d);\r\n grid.render();\r\n }\r\n\r\n const overflowsBottomOfGrid = (activeRow || 0) + destH > grid.getDataLength();\r\n if (this._options.newRowCreator && overflowsBottomOfGrid) {\r\n const newRowsNeeded = (activeRow || 0) + destH - grid.getDataLength();\r\n this._options.newRowCreator(newRowsNeeded);\r\n }\r\n\r\n const clipCommand: ExternalCopyClipCommand = {\r\n isClipboardCommand: true,\r\n clippedRange,\r\n oldValues: [],\r\n cellExternalCopyManager: this,\r\n _options: this._options,\r\n setDataItemValueForColumn: this.setDataItemValueForColumn.bind(this),\r\n markCopySelection: this.markCopySelection.bind(this),\r\n oneCellToMultiple,\r\n activeRow,\r\n activeCell,\r\n destH,\r\n destW,\r\n maxDestY: grid.getDataLength(),\r\n maxDestX: grid.getColumns().length,\r\n h: 0,\r\n w: 0,\r\n execute: () => {\r\n clipCommand.h = 0;\r\n for (let y = 0; y < clipCommand.destH; y++) {\r\n clipCommand.oldValues[y] = [];\r\n clipCommand.w = 0;\r\n clipCommand.h++;\r\n let xOffset = 0; // the x offset for hidden col\r\n\r\n for (let x = 0; x < clipCommand.destW; x++) {\r\n const desty = activeRow + y;\r\n const destx = activeCell + x;\r\n const column = columns[destx];\r\n\r\n // paste on hidden column will be skipped, but we need to paste 1 cell further on X axis\r\n // we'll increase our X and increase the offset`\r\n if (column.hidden) {\r\n clipCommand.destW++;\r\n xOffset++;\r\n continue;\r\n }\r\n clipCommand.w++;\r\n\r\n if (desty < clipCommand.maxDestY && destx < clipCommand.maxDestX) {\r\n const dt = grid.getDataItem(desty);\r\n\r\n clipCommand.oldValues[y][x - xOffset] = dt[column['field']];\r\n if (oneCellToMultiple) {\r\n clipCommand.setDataItemValueForColumn(dt, column, clippedRange[0][0]);\r\n } else {\r\n clipCommand.setDataItemValueForColumn(dt, column, clippedRange[y] ? clippedRange[y][x - xOffset] : '');\r\n }\r\n grid.updateCell(desty, destx);\r\n grid.onCellChange.notify({\r\n row: desty,\r\n cell: destx,\r\n item: dt,\r\n grid,\r\n column: {} as Column\r\n });\r\n }\r\n }\r\n }\r\n\r\n const bRange = new SlickRange(\r\n activeRow,\r\n activeCell,\r\n activeRow + clipCommand.h - 1,\r\n activeCell + clipCommand.w - 1\r\n );\r\n\r\n this.markCopySelection([bRange]);\r\n grid.getSelectionModel()?.setSelectedRanges([bRange]);\r\n this.onPasteCells.notify({ ranges: [bRange] });\r\n },\r\n undo: () => {\r\n for (let y = 0; y < clipCommand.destH; y++) {\r\n for (let x = 0; x < clipCommand.destW; x++) {\r\n const desty = activeRow + y;\r\n const destx = activeCell + x;\r\n\r\n if (desty < clipCommand.maxDestY && destx < clipCommand.maxDestX) {\r\n const dt = grid.getDataItem(desty);\r\n if (oneCellToMultiple) {\r\n clipCommand.setDataItemValueForColumn(dt, columns[destx], clipCommand.oldValues[0][0]);\r\n } else {\r\n clipCommand.setDataItemValueForColumn(dt, columns[destx], clipCommand.oldValues[y][x]);\r\n }\r\n grid.updateCell(desty, destx);\r\n grid.onCellChange.notify({\r\n row: desty,\r\n cell: destx,\r\n item: dt,\r\n grid,\r\n column: {} as Column\r\n });\r\n }\r\n }\r\n }\r\n\r\n const bRange = new SlickRange(\r\n activeRow,\r\n activeCell,\r\n activeRow + clipCommand.h - 1,\r\n activeCell + clipCommand.w - 1\r\n );\r\n\r\n this.markCopySelection([bRange]);\r\n grid.getSelectionModel()?.setSelectedRanges([bRange]);\r\n if (typeof this._options.onPasteCells === 'function') {\r\n this.onPasteCells.notify({ ranges: [bRange] });\r\n }\r\n\r\n if (addRows > 1) {\r\n const d = grid.getData();\r\n for (; addRows > 1; addRows--) {\r\n d.splice(d.length - 1, 1);\r\n }\r\n grid.setData(d);\r\n grid.render();\r\n }\r\n }\r\n };\r\n\r\n if (typeof this._options.clipboardCommandHandler === 'function') {\r\n this._options.clipboardCommandHandler(clipCommand);\r\n } else {\r\n clipCommand.execute();\r\n }\r\n }\r\n\r\n protected handleKeyDown(e: SlickEventData): boolean | void {\r\n let ranges: SlickRange_[];\r\n if (!this._grid.getEditorLock().isActive() || this._grid.getOptions().autoEdit) {\r\n if (e.which === this.keyCodes.ESC) {\r\n if (this._copiedRanges) {\r\n e.preventDefault();\r\n this.clearCopySelection();\r\n this.onCopyCancelled.notify({ ranges: this._copiedRanges });\r\n this._copiedRanges = null;\r\n }\r\n }\r\n\r\n if ((e.which === this.keyCodes.C || e.which === this.keyCodes.INSERT) && (e.ctrlKey || e.metaKey) && !e.shiftKey) { // CTRL+C or CTRL+INS\r\n if (typeof this._onCopyInit === 'function') {\r\n this._onCopyInit.call(this);\r\n }\r\n ranges = this._grid.getSelectionModel()?.getSelectedRanges() ?? [];\r\n if (ranges.length !== 0) {\r\n this._copiedRanges = ranges;\r\n this.markCopySelection(ranges);\r\n this.onCopyCells.notify({ ranges });\r\n\r\n const columns = this._grid.getColumns();\r\n let clipText = '';\r\n\r\n for (let rg = 0; rg < ranges.length; rg++) {\r\n const range = ranges[rg];\r\n const clipTextRows: string[] = [];\r\n for (let i = range.fromRow; i < range.toRow + 1; i++) {\r\n const clipTextCells: string[] = [];\r\n const dt = this._grid.getDataItem(i);\r\n\r\n if (clipTextRows.length === 0 && this._options.includeHeaderWhenCopying) {\r\n const clipTextHeaders: string[] = [];\r\n for (let j = range.fromCell; j < range.toCell + 1; j++) {\r\n const colName: string = columns[j].name instanceof HTMLElement\r\n ? (columns[j].name as HTMLElement).innerHTML\r\n : columns[j].name as string;\r\n if (colName.length > 0 && !columns[j].hidden) {\r\n clipTextHeaders.push(this.getHeaderValueForColumn(columns[j]) || '');\r\n }\r\n }\r\n clipTextRows.push(clipTextHeaders.join('\\t'));\r\n }\r\n\r\n for (let j = range.fromCell; j < range.toCell + 1; j++) {\r\n const colName: string = columns[j].name instanceof HTMLElement\r\n ? (columns[j].name as HTMLElement).innerHTML\r\n : columns[j].name as string;\r\n if (colName.length > 0 && !columns[j].hidden) {\r\n clipTextCells.push(this.getDataItemValueForColumn(dt, columns[j], e));\r\n }\r\n }\r\n clipTextRows.push(clipTextCells.join('\\t'));\r\n }\r\n clipText += clipTextRows.join('\\r\\n') + '\\r\\n';\r\n }\r\n\r\n if ((window as any).clipboardData) {\r\n (window as any).clipboardData.setData('Text', clipText);\r\n return true;\r\n }\r\n else {\r\n const focusEl = document.activeElement as HTMLElement;\r\n const ta = this._createTextBox(clipText);\r\n ta.focus();\r\n\r\n window.setTimeout(() => {\r\n this._bodyElement.removeChild(ta);\r\n // restore focus when possible\r\n focusEl\r\n ? focusEl.focus()\r\n : console.log('No element to restore focus to after copy?');\r\n }, this._options?.clipboardPasteDelay ?? CLIPBOARD_PASTE_DELAY);\r\n\r\n if (typeof this._onCopySuccess === 'function') {\r\n let rowCount = 0;\r\n // If it's cell selection, use the toRow/fromRow fields\r\n if (ranges.length === 1) {\r\n rowCount = (ranges[0].toRow + 1) - ranges[0].fromRow;\r\n } else {\r\n rowCount = ranges.length;\r\n }\r\n this._onCopySuccess(rowCount);\r\n }\r\n\r\n return false;\r\n }\r\n }\r\n }\r\n\r\n if (!this._options.readOnlyMode && (\r\n (e.which === this.keyCodes.V && (e.ctrlKey || e.metaKey) && !e.shiftKey)\r\n || (e.which === this.keyCodes.INSERT && e.shiftKey && !e.ctrlKey)\r\n )) { // CTRL+V or Shift+INS\r\n const focusEl = document.activeElement as HTMLElement;\r\n const ta = this._createTextBox('');\r\n window.setTimeout(() => {\r\n this._decodeTabularData(this._grid, ta);\r\n // restore focus when possible\r\n focusEl?.focus();\r\n }, this._options?.clipboardPasteDelay ?? CLIPBOARD_PASTE_DELAY);\r\n return false;\r\n }\r\n }\r\n }\r\n\r\n protected markCopySelection(ranges: SlickRange_[]) {\r\n this.clearCopySelection();\r\n\r\n const columns = this._grid.getColumns();\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 hash[j] = {};\r\n for (let k = ranges[i].fromCell; k <= ranges[i].toCell && k < columns.length; k++) {\r\n hash[j][columns[k].id] = this._copiedCellStyle;\r\n }\r\n }\r\n }\r\n this._grid.setCellCssStyles(this._copiedCellStyleLayerKey, hash);\r\n window.clearTimeout(this._clearCopyTI);\r\n this._clearCopyTI = window.setTimeout(() => {\r\n this.clearCopySelection();\r\n }, this._options?.clearCopySelectionDelay || CLEAR_COPY_SELECTION_DELAY);\r\n }\r\n\r\n clearCopySelection() {\r\n this._grid.removeCellCssStyles(this._copiedCellStyleLayerKey);\r\n }\r\n\r\n setIncludeHeaderWhenCopying(includeHeaderWhenCopying: boolean) {\r\n this._options.includeHeaderWhenCopying = includeHeaderWhenCopying;\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 CellExternalCopyManager: SlickCellExternalCopyManager\r\n }\r\n });\r\n}\r\n"], + "sourcesContent": ["import type { Column, CssStyleHash, ExcelCopyBufferOption, ExternalCopyClipCommand, SlickPlugin } from '../models/index.js';\r\nimport type { SlickGrid } from '../slick.grid.js';\r\nimport { SlickEvent as SlickEvent_, type SlickEventData, SlickRange as SlickRange_, Utils as Utils_ } from '../slick.core.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 SlickRange = IIFE_ONLY ? Slick.Range : SlickRange_;\r\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\r\n\r\nconst CLEAR_COPY_SELECTION_DELAY = 2000;\r\nconst CLIPBOARD_PASTE_DELAY = 100;\r\n\r\n/***\r\n This manager enables users to copy/paste data from/to an external Spreadsheet application\r\n such as MS-Excel\u00AE or OpenOffice-Spreadsheet.\r\n\r\n Since it is not possible to access directly the clipboard in javascript, the plugin uses\r\n a trick to do it's job. After detecting the keystroke, we dynamically create a textarea\r\n where the browser copies/pastes the serialized data.\r\n\r\n options:\r\n copiedCellStyle : sets the css className used for copied cells. default : \"copied\"\r\n copiedCellStyleLayerKey : sets the layer key for setting css values of copied cells. default : \"copy-manager\"\r\n dataItemColumnValueExtractor : option to specify a custom column value extractor function\r\n dataItemColumnValueSetter : option to specify a custom column value setter function\r\n clipboardCommandHandler : option to specify a custom handler for paste actions\r\n includeHeaderWhenCopying : set to true and the plugin will take the name property from each column (which is usually what appears in your header) and put that as the first row of the text that's copied to the clipboard\r\n bodyElement: option to specify a custom DOM element which to will be added the hidden textbox. It's useful if the grid is inside a modal dialog.\r\n onCopyInit: optional handler to run when copy action initializes\r\n onCopySuccess: optional handler to run when copy action is complete\r\n newRowCreator: function to add rows to table if paste overflows bottom of table, if this function is not provided new rows will be ignored.\r\n readOnlyMode: suppresses paste\r\n headerColumnValueExtractor : option to specify a custom column header value extractor function\r\n*/\r\nexport class SlickCellExternalCopyManager implements SlickPlugin {\r\n // --\r\n // public API\r\n pluginName = 'CellExternalCopyManager' as const;\r\n onCopyCells = new SlickEvent<{ ranges: SlickRange_[]; }>('onCopyCells');\r\n onCopyCancelled = new SlickEvent<{ ranges: SlickRange_[]; }>('onCopyCancelled');\r\n onPasteCells = new SlickEvent<{ ranges: SlickRange_[]; }>('onPasteCells');\r\n\r\n // --\r\n // protected props\r\n protected _grid!: SlickGrid;\r\n protected _bodyElement: HTMLElement;\r\n protected _copiedRanges: SlickRange_[] | null = null;\r\n protected _clearCopyTI?: number;\r\n protected _copiedCellStyle: string;\r\n protected _copiedCellStyleLayerKey: string;\r\n protected _onCopyInit?: () => void;\r\n protected _onCopySuccess?: (rowCount: number) => void;\r\n protected _options: ExcelCopyBufferOption;\r\n\r\n protected keyCodes = {\r\n 'C': 67,\r\n 'V': 86,\r\n 'ESC': 27,\r\n 'INSERT': 45\r\n };\r\n\r\n constructor(options: ExcelCopyBufferOption) {\r\n this._options = options || {};\r\n this._copiedCellStyleLayerKey = this._options.copiedCellStyleLayerKey || 'copy-manager';\r\n this._copiedCellStyle = this._options.copiedCellStyle || 'copied';\r\n this._bodyElement = this._options.bodyElement || document.body;\r\n this._onCopyInit = this._options.onCopyInit || undefined;\r\n this._onCopySuccess = this._options.onCopySuccess || undefined;\r\n }\r\n\r\n init(grid: SlickGrid) {\r\n this._grid = grid;\r\n Utils.addSlickEventPubSubWhenDefined(grid.getPubSubService(), this);\r\n this._grid.onKeyDown.subscribe(this.handleKeyDown.bind(this));\r\n\r\n // we need a cell selection model\r\n const cellSelectionModel = grid.getSelectionModel();\r\n if (!cellSelectionModel) {\r\n throw new Error('Selection model is mandatory for this plugin. Please set a selection model on the grid before adding this plugin: grid.setSelectionModel(new Slick.CellSelectionModel())');\r\n }\r\n // we give focus on the grid when a selection is done on it.\r\n // without this, if the user selects a range of cell without giving focus on a particular cell, the grid doesn't get the focus and key stroke handles (ctrl+c) don't work\r\n cellSelectionModel.onSelectedRangesChanged.subscribe(() => {\r\n if (!this._grid.getEditorLock().isActive()) {\r\n this._grid.focus();\r\n }\r\n });\r\n }\r\n\r\n destroy() {\r\n this._grid.onKeyDown.unsubscribe(this.handleKeyDown.bind(this));\r\n }\r\n\r\n protected getHeaderValueForColumn(columnDef: Column): string {\r\n if (this._options.headerColumnValueExtractor) {\r\n const val = Utils.getHtmlStringOutput(this._options.headerColumnValueExtractor(columnDef));\r\n if (val) {\r\n return val;\r\n }\r\n }\r\n\r\n return Utils.getHtmlStringOutput(columnDef.name || '');\r\n }\r\n\r\n protected getDataItemValueForColumn(item: any, columnDef: Column, event: SlickEventData): string {\r\n if (typeof this._options.dataItemColumnValueExtractor === 'function') {\r\n const val = this._options.dataItemColumnValueExtractor(item, columnDef) as string | null;\r\n if (val) {\r\n return val;\r\n }\r\n }\r\n\r\n let retVal = '';\r\n\r\n // if a custom getter is not defined, we call serializeValue of the editor to serialize\r\n if (columnDef?.editor) {\r\n const tmpP = document.createElement('p');\r\n const editor = new (columnDef.editor as any)({\r\n container: tmpP, // a dummy container\r\n column: columnDef,\r\n event,\r\n position: { top: 0, left: 0 }, // a dummy position required by some editors\r\n grid: this._grid,\r\n });\r\n editor.loadValue(item);\r\n retVal = editor.serializeValue();\r\n editor.destroy();\r\n tmpP.remove();\r\n } else {\r\n retVal = item[columnDef.field || ''];\r\n }\r\n\r\n return retVal;\r\n }\r\n\r\n protected setDataItemValueForColumn(item: any, columnDef: Column, value: string): null | string | void {\r\n if (columnDef.denyPaste) {\r\n return null;\r\n }\r\n\r\n if (this._options.dataItemColumnValueSetter) {\r\n return this._options.dataItemColumnValueSetter(item, columnDef, value) as string;\r\n }\r\n\r\n // if a custom setter is not defined, we call applyValue of the editor to unserialize\r\n if (columnDef.editor) {\r\n const tmpDiv = document.createElement('div');\r\n const editor = new (columnDef.editor as any)({\r\n container: tmpDiv, // a dummy container\r\n column: columnDef,\r\n position: { top: 0, left: 0 }, // a dummy position required by some editors\r\n grid: this._grid\r\n });\r\n editor.loadValue(item);\r\n editor.applyValue(item, value);\r\n editor.destroy();\r\n tmpDiv.remove();\r\n } else {\r\n item[columnDef.field] = value;\r\n }\r\n }\r\n\r\n\r\n protected _createTextBox(innerText: string) {\r\n const scrollPos = document.documentElement.scrollTop || document.body.scrollTop;\r\n const ta = document.createElement('textarea');\r\n ta.style.position = 'absolute';\r\n ta.style.opacity = '0';\r\n ta.value = innerText;\r\n ta.style.top = `${scrollPos}px`;\r\n this._bodyElement.appendChild(ta);\r\n ta.select();\r\n\r\n return ta;\r\n }\r\n\r\n protected _decodeTabularData(grid: SlickGrid, ta: HTMLTextAreaElement) {\r\n const columns = grid.getColumns();\r\n const clipText = ta.value;\r\n const clipRows = clipText.split(/[\\n\\f\\r]/);\r\n // trim trailing CR if present\r\n if (clipRows[clipRows.length - 1] === '') {\r\n clipRows.pop();\r\n }\r\n\r\n let j = 0;\r\n const clippedRange: any[] = [];\r\n\r\n this._bodyElement.removeChild(ta);\r\n for (let i = 0; i < clipRows.length; i++) {\r\n if (clipRows[i] !== '') {\r\n clippedRange[j++] = clipRows[i].split('\\t');\r\n } else {\r\n clippedRange[j++] = [''];\r\n }\r\n }\r\n const selectedCell = grid.getActiveCell();\r\n const ranges = grid.getSelectionModel()?.getSelectedRanges();\r\n const selectedRange = ranges && ranges.length ? ranges[0] : null; // pick only one selection\r\n let activeRow: number;\r\n let activeCell: number;\r\n\r\n if (selectedRange) {\r\n activeRow = selectedRange.fromRow;\r\n activeCell = selectedRange.fromCell;\r\n } else if (selectedCell) {\r\n activeRow = selectedCell.row;\r\n activeCell = selectedCell.cell;\r\n } else {\r\n // we don't know where to paste\r\n return;\r\n }\r\n\r\n let oneCellToMultiple = false;\r\n let destH = clippedRange.length;\r\n let destW = clippedRange.length ? clippedRange[0].length : 0;\r\n if (clippedRange.length === 1 && clippedRange[0].length === 1 && selectedRange) {\r\n oneCellToMultiple = true;\r\n destH = selectedRange.toRow - selectedRange.fromRow + 1;\r\n destW = selectedRange.toCell - selectedRange.fromCell + 1;\r\n }\r\n const availableRows = (grid.getData() as any[]).length - (activeRow || 0);\r\n let addRows = 0;\r\n\r\n // ignore new rows if we don't have a \"newRowCreator\"\r\n if (availableRows < destH && typeof this._options.newRowCreator === 'function') {\r\n const d = grid.getData();\r\n for (addRows = 1; addRows <= destH - availableRows; addRows++) {\r\n d.push({});\r\n }\r\n grid.setData(d);\r\n grid.render();\r\n }\r\n\r\n const overflowsBottomOfGrid = (activeRow || 0) + destH > grid.getDataLength();\r\n if (this._options.newRowCreator && overflowsBottomOfGrid) {\r\n const newRowsNeeded = (activeRow || 0) + destH - grid.getDataLength();\r\n this._options.newRowCreator(newRowsNeeded);\r\n }\r\n\r\n const clipCommand: ExternalCopyClipCommand = {\r\n isClipboardCommand: true,\r\n clippedRange,\r\n oldValues: [],\r\n cellExternalCopyManager: this,\r\n _options: this._options,\r\n setDataItemValueForColumn: this.setDataItemValueForColumn.bind(this),\r\n markCopySelection: this.markCopySelection.bind(this),\r\n oneCellToMultiple,\r\n activeRow,\r\n activeCell,\r\n destH,\r\n destW,\r\n maxDestY: grid.getDataLength(),\r\n maxDestX: grid.getColumns().length,\r\n h: 0,\r\n w: 0,\r\n execute: () => {\r\n clipCommand.h = 0;\r\n for (let y = 0; y < clipCommand.destH; y++) {\r\n clipCommand.oldValues[y] = [];\r\n clipCommand.w = 0;\r\n clipCommand.h++;\r\n let xOffset = 0; // the x offset for hidden col\r\n\r\n for (let x = 0; x < clipCommand.destW; x++) {\r\n const desty = activeRow + y;\r\n const destx = activeCell + x;\r\n const column = columns[destx];\r\n\r\n // paste on hidden column will be skipped, but we need to paste 1 cell further on X axis\r\n // we'll increase our X and increase the offset`\r\n if (column.hidden) {\r\n clipCommand.destW++;\r\n xOffset++;\r\n continue;\r\n }\r\n clipCommand.w++;\r\n\r\n if (desty < clipCommand.maxDestY && destx < clipCommand.maxDestX) {\r\n const dt = grid.getDataItem(desty);\r\n\r\n clipCommand.oldValues[y][x - xOffset] = dt[column['field']];\r\n if (oneCellToMultiple) {\r\n clipCommand.setDataItemValueForColumn(dt, column, clippedRange[0][0]);\r\n } else {\r\n clipCommand.setDataItemValueForColumn(dt, column, clippedRange[y] ? clippedRange[y][x - xOffset] : '');\r\n }\r\n grid.updateCell(desty, destx);\r\n grid.onCellChange.notify({\r\n row: desty,\r\n cell: destx,\r\n item: dt,\r\n grid,\r\n column: {} as Column\r\n });\r\n }\r\n }\r\n }\r\n\r\n const bRange = new SlickRange(\r\n activeRow,\r\n activeCell,\r\n activeRow + clipCommand.h - 1,\r\n activeCell + clipCommand.w - 1\r\n );\r\n\r\n this.markCopySelection([bRange]);\r\n grid.getSelectionModel()?.setSelectedRanges([bRange]);\r\n this.onPasteCells.notify({ ranges: [bRange] });\r\n },\r\n undo: () => {\r\n for (let y = 0; y < clipCommand.destH; y++) {\r\n for (let x = 0; x < clipCommand.destW; x++) {\r\n const desty = activeRow + y;\r\n const destx = activeCell + x;\r\n\r\n if (desty < clipCommand.maxDestY && destx < clipCommand.maxDestX) {\r\n const dt = grid.getDataItem(desty);\r\n if (oneCellToMultiple) {\r\n clipCommand.setDataItemValueForColumn(dt, columns[destx], clipCommand.oldValues[0][0]);\r\n } else {\r\n clipCommand.setDataItemValueForColumn(dt, columns[destx], clipCommand.oldValues[y][x]);\r\n }\r\n grid.updateCell(desty, destx);\r\n grid.onCellChange.notify({\r\n row: desty,\r\n cell: destx,\r\n item: dt,\r\n grid,\r\n column: {} as Column\r\n });\r\n }\r\n }\r\n }\r\n\r\n const bRange = new SlickRange(\r\n activeRow,\r\n activeCell,\r\n activeRow + clipCommand.h - 1,\r\n activeCell + clipCommand.w - 1\r\n );\r\n\r\n this.markCopySelection([bRange]);\r\n grid.getSelectionModel()?.setSelectedRanges([bRange]);\r\n if (typeof this._options.onPasteCells === 'function') {\r\n this.onPasteCells.notify({ ranges: [bRange] });\r\n }\r\n\r\n if (addRows > 1) {\r\n const d = grid.getData();\r\n for (; addRows > 1; addRows--) {\r\n d.splice(d.length - 1, 1);\r\n }\r\n grid.setData(d);\r\n grid.render();\r\n }\r\n }\r\n };\r\n\r\n if (typeof this._options.clipboardCommandHandler === 'function') {\r\n this._options.clipboardCommandHandler(clipCommand);\r\n } else {\r\n clipCommand.execute();\r\n }\r\n }\r\n\r\n protected handleKeyDown(e: SlickEventData): boolean | void {\r\n let ranges: SlickRange_[];\r\n if (!this._grid.getEditorLock().isActive() || this._grid.getOptions().autoEdit) {\r\n if (e.which === this.keyCodes.ESC) {\r\n if (this._copiedRanges) {\r\n e.preventDefault();\r\n this.clearCopySelection();\r\n this.onCopyCancelled.notify({ ranges: this._copiedRanges });\r\n this._copiedRanges = null;\r\n }\r\n }\r\n\r\n if ((e.which === this.keyCodes.C || e.which === this.keyCodes.INSERT) && (e.ctrlKey || e.metaKey) && !e.shiftKey) { // CTRL+C or CTRL+INS\r\n if (typeof this._onCopyInit === 'function') {\r\n this._onCopyInit.call(this);\r\n }\r\n ranges = this._grid.getSelectionModel()?.getSelectedRanges() ?? [];\r\n if (ranges.length !== 0) {\r\n this._copiedRanges = ranges;\r\n this.markCopySelection(ranges);\r\n this.onCopyCells.notify({ ranges });\r\n\r\n const columns = this._grid.getColumns();\r\n let clipText = '';\r\n\r\n for (let rg = 0; rg < ranges.length; rg++) {\r\n const range = ranges[rg];\r\n const clipTextRows: string[] = [];\r\n for (let i = range.fromRow; i < range.toRow + 1; i++) {\r\n const clipTextCells: string[] = [];\r\n const dt = this._grid.getDataItem(i);\r\n\r\n if (clipTextRows.length === 0 && this._options.includeHeaderWhenCopying) {\r\n const clipTextHeaders: string[] = [];\r\n for (let j = range.fromCell; j < range.toCell + 1; j++) {\r\n const colName: string = columns[j].name instanceof HTMLElement\r\n ? (columns[j].name as HTMLElement).innerHTML\r\n : columns[j].name as string;\r\n if (colName.length > 0 && !columns[j].hidden) {\r\n clipTextHeaders.push(this.getHeaderValueForColumn(columns[j]) || '');\r\n }\r\n }\r\n clipTextRows.push(clipTextHeaders.join('\\t'));\r\n }\r\n\r\n for (let j = range.fromCell; j < range.toCell + 1; j++) {\r\n const colName: string = columns[j].name instanceof HTMLElement\r\n ? (columns[j].name as HTMLElement).innerHTML\r\n : columns[j].name as string;\r\n if (colName.length > 0 && !columns[j].hidden) {\r\n clipTextCells.push(this.getDataItemValueForColumn(dt, columns[j], e));\r\n }\r\n }\r\n clipTextRows.push(clipTextCells.join('\\t'));\r\n }\r\n clipText += clipTextRows.join('\\r\\n') + '\\r\\n';\r\n }\r\n\r\n if ((window as any).clipboardData) {\r\n (window as any).clipboardData.setData('Text', clipText);\r\n return true;\r\n }\r\n else {\r\n const focusEl = document.activeElement as HTMLElement;\r\n const ta = this._createTextBox(clipText);\r\n ta.focus();\r\n\r\n window.setTimeout(() => {\r\n this._bodyElement.removeChild(ta);\r\n // restore focus when possible\r\n focusEl\r\n ? focusEl.focus()\r\n : console.log('No element to restore focus to after copy?');\r\n }, this._options?.clipboardPasteDelay ?? CLIPBOARD_PASTE_DELAY);\r\n\r\n if (typeof this._onCopySuccess === 'function') {\r\n let rowCount = 0;\r\n // If it's cell selection, use the toRow/fromRow fields\r\n if (ranges.length === 1) {\r\n rowCount = (ranges[0].toRow + 1) - ranges[0].fromRow;\r\n } else {\r\n rowCount = ranges.length;\r\n }\r\n this._onCopySuccess(rowCount);\r\n }\r\n\r\n return false;\r\n }\r\n }\r\n }\r\n\r\n if (!this._options.readOnlyMode && (\r\n (e.which === this.keyCodes.V && (e.ctrlKey || e.metaKey) && !e.shiftKey)\r\n || (e.which === this.keyCodes.INSERT && e.shiftKey && !e.ctrlKey)\r\n )) { // CTRL+V or Shift+INS\r\n const focusEl = document.activeElement as HTMLElement;\r\n const ta = this._createTextBox('');\r\n window.setTimeout(() => {\r\n this._decodeTabularData(this._grid, ta);\r\n // restore focus when possible\r\n focusEl?.focus();\r\n }, this._options?.clipboardPasteDelay ?? CLIPBOARD_PASTE_DELAY);\r\n return false;\r\n }\r\n }\r\n }\r\n\r\n protected markCopySelection(ranges: SlickRange_[]) {\r\n this.clearCopySelection();\r\n\r\n const columns = this._grid.getColumns();\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 hash[j] = {};\r\n for (let k = ranges[i].fromCell; k <= ranges[i].toCell && k < columns.length; k++) {\r\n hash[j][columns[k].id] = this._copiedCellStyle;\r\n }\r\n }\r\n }\r\n this._grid.setCellCssStyles(this._copiedCellStyleLayerKey, hash);\r\n window.clearTimeout(this._clearCopyTI);\r\n this._clearCopyTI = window.setTimeout(() => {\r\n this.clearCopySelection();\r\n }, this._options?.clearCopySelectionDelay || CLEAR_COPY_SELECTION_DELAY);\r\n }\r\n\r\n clearCopySelection() {\r\n this._grid.removeCellCssStyles(this._copiedCellStyleLayerKey);\r\n }\r\n\r\n setIncludeHeaderWhenCopying(includeHeaderWhenCopying: boolean) {\r\n this._options.includeHeaderWhenCopying = includeHeaderWhenCopying;\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 CellExternalCopyManager: SlickCellExternalCopyManager\r\n }\r\n });\r\n}\r\n"], "mappings": "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", "names": ["_a"] } diff --git a/dist/browser/plugins/slick.cellmenu.js.map b/dist/browser/plugins/slick.cellmenu.js.map index edef348c..2ceca125 100644 --- a/dist/browser/plugins/slick.cellmenu.js.map +++ b/dist/browser/plugins/slick.cellmenu.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../../../src/plugins/slick.cellmenu.ts"], - "sourcesContent": ["import {\n BindingEventService as BindingEventService_,\n SlickEvent as SlickEvent_,\n SlickEventData as SlickEventData_,\n SlickEventHandler as SlickEventHandler_,\n Utils as Utils_\n} from '../slick.core';\nimport type {\n CellMenuOption,\n Column,\n DOMMouseOrTouchEvent,\n GridOption,\n MenuCommandItem,\n MenuCommandItemCallbackArgs,\n MenuFromCellCallbackArgs,\n MenuOptionItem,\n MenuOptionItemCallbackArgs,\n MenuType,\n SlickPlugin\n} from '../models/index';\nimport type { SlickGrid } from '../slick.grid';\n\n// for (iife) load Slick methods from global Slick object, or use imports for (esm)\nconst BindingEventService = IIFE_ONLY ? Slick.BindingEventService : BindingEventService_;\nconst SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_;\nconst SlickEventData = IIFE_ONLY ? Slick.EventData : SlickEventData_;\nconst EventHandler = IIFE_ONLY ? Slick.EventHandler : SlickEventHandler_;\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\n\n/**\n * A plugin to add Menu on a Cell click (click on the cell that has the cellMenu object defined)\n * The \"cellMenu\" is defined in a Column Definition object\n * Similar to the ContextMenu plugin (could be used in combo),\n * except that it subscribes to the cell \"onClick\" event (regular mouse click or touch).\n *\n * A general use of this plugin is for an Action Dropdown Menu to do certain things on the row that was clicked\n * You can use it to change the cell data property through a list of Options AND/OR through a list of Commands.\n *\n * USAGE:\n *\n * Add the slick.cellMenu.(js|css) files and register it with the grid.\n *\n * To specify a menu in a column header, extend the column definition like so:\n * var cellMenuPlugin = new Slick.Plugins.CellMenu(columns, grid, options);\n *\n * Available cellMenu options, by defining a cellMenu object:\n *\n * var columns = [\n * {\n * id: \"action\", name: \"Action\", field: \"action\", formatter: fakeButtonFormatter,\n * cellMenu: {\n * optionTitle: \"Change Effort Driven\",\n * optionItems: [\n * { option: true, title: \"True\", iconCssClass: 'checkmark' },\n * { option: false, title: \"False\" }\n * ],\n * commandTitle: \"Commands\",\n * commandItems: [\n * { command: \"delete-row\", title: \"Delete Row\", iconCssClass: \"sgi sgi-close\", cssClass: 'bold', textCssClass: \"red\" },\n * { divider: true },\n * \"divider\" // you can pass \"divider\" as a string or an object\n * { command: \"help\", title: \"Help\", iconCssClass: \"icon-help\" },\n * { command: \"help\", title: \"Disabled Command\", disabled: true },\n * ],\n * }\n * }\n * ];\n *\n *\n * Available cellMenu properties:\n * commandTitle: Title of the Command section (optional)\n * commandItems: Array of Command item objects (command/title pair)\n * optionTitle: Title of the Option section (optional)\n * optionItems: Array of Options item objects (option/title pair)\n * hideCloseButton: Hide the Close button on top right (defaults to false)\n * hideCommandSection: Hide the Commands section even when the commandItems array is filled (defaults to false)\n * hideMenuOnScroll: Do we want to hide the Cell Menu when a scrolling event occurs (defaults to true)?\n * hideOptionSection: Hide the Options section even when the optionItems array is filled (defaults to false)\n * maxHeight: Maximum height that the drop menu will have, can be a number (250) or text (\"none\")\n * width: Width that the drop menu will have, can be a number (250) or text (defaults to \"auto\")\n * autoAdjustDrop: Auto-align dropup or dropdown menu to the left or right depending on grid viewport available space (defaults to true)\n * autoAdjustDropOffset: Optionally add an offset to the auto-align of the drop menu (defaults to 0)\n * autoAlignSide: Auto-align drop menu to the left or right depending on grid viewport available space (defaults to true)\n * autoAlignSideOffset: Optionally add an offset to the left/right side auto-align (defaults to 0)\n * menuUsabilityOverride: Callback method that user can override the default behavior of enabling/disabling the menu from being usable (must be combined with a custom formatter)\n * subItemChevronClass: CSS class that can be added on the right side of a sub-item parent (typically a chevron-right icon)\n * subMenuOpenByEvent: defaults to \"mouseover\", what event type shoud we use to open sub-menu(s), 2 options are available: \"mouseover\" or \"click\"\n *\n *\n * Available menu Command/Option item properties:\n * action: Optionally define a callback function that gets executed when item is chosen (and/or use the onCommand event)\n * command: A command identifier to be passed to the onCommand event handlers (when using \"commandItems\").\n * option: An option to be passed to the onOptionSelected event handlers (when using \"optionItems\").\n * title: Menu item text label.\n * divider: Boolean which tells if the current item is a divider, not an actual command. You could also pass \"divider\" instead of an object\n * disabled: Whether the item/command is disabled.\n * hidden: Whether the item/command is hidden.\n * subMenuTitle: Optional sub-menu title that will shows up when sub-menu commmands/options list is opened\n * subMenuTitleCssClass: Optional sub-menu title CSS class to use with `subMenuTitle`\n * tooltip: Item tooltip.\n * cssClass: A CSS class to be added to the menu item container.\n * iconCssClass: A CSS class to be added to the menu item icon.\n * textCssClass: A CSS class to be added to the menu item text.\n * iconImage: A url to the icon image.\n * itemVisibilityOverride: Callback method that user can override the default behavior of showing/hiding an item from the list\n * itemUsabilityOverride: Callback method that user can override the default behavior of enabling/disabling an item from the list\n *\n *\n * The plugin exposes the following events:\n *\n * onAfterMenuShow: Fired after the menu is shown. You can customize the menu or dismiss it by returning false.\n * Event args:\n * cell: Cell or column index\n * row: Row index\n * grid: Reference to the grid.\n *\n * onBeforeMenuShow: Fired before the menu is shown. You can customize the menu or dismiss it by returning false.\n * Event args:\n * cell: Cell or column index\n * row: Row index\n * grid: Reference to the grid.\n *\n * onBeforeMenuClose: Fired when the menu is closing.\n * Event args:\n * cell: Cell or column index\n * row: Row index\n * grid: Reference to the grid.\n * menu: Menu DOM element\n *\n * onCommand: Fired on menu option clicked from the Command items list\n * Event args:\n * cell: Cell or column index\n * row: Row index\n * grid: Reference to the grid.\n * command: Menu command identified.\n * item: Menu item selected\n * column: Cell Column definition\n * dataContext: Cell Data Context (data object)\n *\n * onOptionSelected: Fired on menu option clicked from the Option items list\n * Event args:\n * cell: Cell or column index\n * row: Row index\n * grid: Reference to the grid.\n * option: Menu option selected.\n * item: Menu item selected\n * column: Cell Column definition\n * dataContext: Cell Data Context (data object)\n *\n *\n * @param options {Object} Cell Menu Options\n * @class Slick.Plugins.CellMenu\n */\nexport class SlickCellMenu implements SlickPlugin {\n // --\n // public API\n pluginName = 'CellMenu' as const;\n onAfterMenuShow = new SlickEvent('onAfterMenuShow');\n onBeforeMenuShow = new SlickEvent('onBeforeMenuShow');\n onBeforeMenuClose = new SlickEvent('onBeforeMenuClose');\n onCommand = new SlickEvent('onCommand');\n onOptionSelected = new SlickEvent('onOptionSelected');\n\n // --\n // protected props\n protected _bindingEventService = new BindingEventService();\n protected _cellMenuProperties: CellMenuOption;\n protected _currentCell = -1;\n protected _currentRow = -1;\n protected _grid!: SlickGrid;\n protected _gridOptions!: GridOption;\n protected _gridUid = '';\n protected _handler = new EventHandler();\n protected _commandTitleElm?: HTMLSpanElement;\n protected _optionTitleElm?: HTMLSpanElement;\n protected _lastMenuTypeClicked = '';\n protected _menuElm?: HTMLDivElement | null;\n protected _subMenuParentId = '';\n protected _defaults: CellMenuOption = {\n autoAdjustDrop: true, // dropup/dropdown\n autoAlignSide: true, // left/right\n autoAdjustDropOffset: 0,\n autoAlignSideOffset: 0,\n hideMenuOnScroll: true,\n maxHeight: 'none',\n width: 'auto',\n subMenuOpenByEvent: 'mouseover',\n };\n\n constructor(optionProperties: Partial) {\n this._cellMenuProperties = Utils.extend({}, this._defaults, optionProperties);\n }\n\n init(grid: SlickGrid) {\n this._grid = grid;\n this._gridOptions = grid.getOptions();\n Utils.addSlickEventPubSubWhenDefined(grid.getPubSubService(), this);\n this._gridUid = grid?.getUID() || '';\n this._handler.subscribe(this._grid.onClick as any, this.handleCellClick.bind(this));\n if (this._cellMenuProperties.hideMenuOnScroll) {\n this._handler.subscribe(this._grid.onScroll, this.closeMenu.bind(this));\n }\n }\n\n setOptions(newOptions: Partial) {\n this._cellMenuProperties = Utils.extend({}, this._cellMenuProperties, newOptions);\n }\n\n destroy() {\n this.onAfterMenuShow.unsubscribe();\n this.onBeforeMenuShow.unsubscribe();\n this.onBeforeMenuClose.unsubscribe();\n this.onCommand.unsubscribe();\n this.onOptionSelected.unsubscribe();\n this._handler.unsubscribeAll();\n this._bindingEventService.unbindAll();\n this._menuElm?.remove();\n this._commandTitleElm = null as any;\n this._optionTitleElm = null as any;\n this._menuElm = null as any;\n }\n\n protected createParentMenu(e: DOMMouseOrTouchEvent) {\n const cell = this._grid.getCellFromEvent(e);\n this._currentCell = cell?.cell ?? 0;\n this._currentRow = cell?.row ?? 0;\n const columnDef = this._grid.getColumns()[this._currentCell];\n\n const commandItems = this._cellMenuProperties.commandItems || [];\n const optionItems = this._cellMenuProperties.optionItems || [];\n\n // make sure there's at least something to show before creating the Cell Menu\n if (!columnDef || !columnDef.cellMenu || (!commandItems.length && !optionItems.length)) {\n return;\n }\n\n // delete any prior Cell Menu\n this.closeMenu();\n\n // Let the user modify the menu or cancel altogether,\n // or provide alternative menu implementation.\n if (this.onBeforeMenuShow.notify({\n cell: this._currentCell,\n row: this._currentRow,\n grid: this._grid\n }, e, this).getReturnValue() === false) {\n return;\n }\n\n // create 1st parent menu container & reposition it\n this._menuElm = this.createMenu(commandItems, optionItems);\n this._menuElm.style.top = `${e.pageY + 5}px`;\n this._menuElm.style.left = `${e.pageX}px`;\n this._menuElm.style.display = 'block';\n document.body.appendChild(this._menuElm);\n\n if (this.onAfterMenuShow.notify({\n cell: this._currentCell,\n row: this._currentRow,\n grid: this._grid\n }, e, this).getReturnValue() === false) {\n return;\n }\n\n return this._menuElm;\n }\n\n /**\n * Create parent menu or sub-menu(s), a parent menu will start at level 0 while sub-menu(s) will be incremented\n * @param commandItems - array of optional commands or dividers\n * @param optionItems - array of optional options or dividers\n * @param level - menu level\n * @param item - command, option or divider\n * @returns menu DOM element\n */\n protected createMenu(commandItems: Array, optionItems: Array, level = 0, item?: MenuCommandItem | MenuOptionItem | 'divider') {\n const columnDef = this._grid.getColumns()[this._currentCell];\n const dataContext = this._grid.getDataItem(this._currentRow);\n\n // create a new cell menu\n const maxHeight = isNaN(this._cellMenuProperties.maxHeight as number) ? this._cellMenuProperties.maxHeight : `${this._cellMenuProperties.maxHeight ?? 0}px`;\n const width = isNaN(this._cellMenuProperties.width as number) ? this._cellMenuProperties.width : `${this._cellMenuProperties.maxWidth ?? 0}px`;\n\n // to avoid having multiple sub-menu trees opened,\n // we need to somehow keep trace of which parent menu the tree belongs to\n // and we should keep ref of only the first sub-menu parent, we can use the command name (remove any whitespaces though)\n const subMenuCommand = (item as MenuCommandItem)?.command;\n let subMenuId = (level === 1 && subMenuCommand) ? subMenuCommand.replaceAll(' ', '') : '';\n if (subMenuId) {\n this._subMenuParentId = subMenuId;\n }\n if (level > 1) {\n subMenuId = this._subMenuParentId;\n }\n\n const menuClasses = `slick-cell-menu slick-menu-level-${level} ${this._gridUid}`;\n const bodyMenuElm = document.body.querySelector(`.slick-cell-menu.slick-menu-level-${level}${this.getGridUidSelector()}`);\n\n // return menu/sub-menu if it's already opened unless we are on different sub-menu tree if so close them all\n if (bodyMenuElm) {\n if (bodyMenuElm.dataset.subMenuParent === subMenuId) {\n return bodyMenuElm;\n }\n this.destroySubMenus();\n }\n\n const menuElm = document.createElement('div');\n menuElm.className = menuClasses;\n if (level > 0) {\n menuElm.classList.add('slick-submenu');\n if (subMenuId) {\n menuElm.dataset.subMenuParent = subMenuId;\n }\n }\n menuElm.ariaLabel = level > 1 ? 'SubMenu' : 'Cell Menu';\n menuElm.role = 'menu';\n if (width) {\n menuElm.style.width = width as string;\n }\n if (maxHeight) {\n menuElm.style.maxHeight = maxHeight as string;\n }\n\n menuElm.style.display = 'none';\n\n let closeButtonElm: HTMLButtonElement | null = null;\n if (level === 0) {\n closeButtonElm = document.createElement('button');\n closeButtonElm.type = 'button';\n closeButtonElm.className = 'close';\n closeButtonElm.dataset.dismiss = 'slick-cell-menu';\n closeButtonElm.ariaLabel = 'Close';\n\n const spanCloseElm = document.createElement('span');\n spanCloseElm.className = 'close';\n spanCloseElm.ariaHidden = 'true';\n spanCloseElm.textContent = '\u00D7';\n closeButtonElm.appendChild(spanCloseElm);\n }\n\n // -- Option List section\n if (!this._cellMenuProperties.hideOptionSection && optionItems.length > 0) {\n const optionMenuElm = document.createElement('div');\n optionMenuElm.className = 'slick-cell-menu-option-list';\n optionMenuElm.role = 'menu';\n\n // when creating sub-menu add its sub-menu title when exists\n if (item && level > 0) {\n this.addSubMenuTitleWhenExists(item, optionMenuElm); // add sub-menu title when exists\n }\n\n if (closeButtonElm && !this._cellMenuProperties.hideCloseButton) {\n this._bindingEventService.bind(closeButtonElm, 'click', this.handleCloseButtonClicked.bind(this) as EventListener);\n menuElm.appendChild(closeButtonElm);\n }\n menuElm.appendChild(optionMenuElm);\n\n this.populateCommandOrOptionItems(\n 'option',\n this._cellMenuProperties,\n optionMenuElm,\n optionItems,\n { cell: this._currentCell, row: this._currentRow, column: columnDef, dataContext, grid: this._grid, level }\n );\n }\n\n // -- Command List section\n if (!this._cellMenuProperties.hideCommandSection && commandItems.length > 0) {\n const commandMenuElm = document.createElement('div');\n commandMenuElm.className = 'slick-cell-menu-command-list';\n commandMenuElm.role = 'menu';\n\n // when creating sub-menu add its sub-menu title when exists\n if (item && level > 0) {\n this.addSubMenuTitleWhenExists(item, commandMenuElm); // add sub-menu title when exists\n }\n\n if (closeButtonElm && !this._cellMenuProperties.hideCloseButton && (optionItems.length === 0 || this._cellMenuProperties.hideOptionSection)) {\n this._bindingEventService.bind(closeButtonElm, 'click', this.handleCloseButtonClicked.bind(this) as EventListener);\n menuElm.appendChild(closeButtonElm);\n }\n menuElm.appendChild(commandMenuElm);\n\n this.populateCommandOrOptionItems(\n 'command',\n this._cellMenuProperties,\n commandMenuElm,\n commandItems,\n { cell: this._currentCell, row: this._currentRow, column: columnDef, dataContext, grid: this._grid, level }\n );\n }\n\n // increment level for possible next sub-menus if exists\n level++;\n\n return menuElm;\n }\n\n protected addSubMenuTitleWhenExists(item: MenuCommandItem | MenuOptionItem | 'divider', commandOrOptionMenu: HTMLDivElement) {\n if (item !== 'divider' && item?.subMenuTitle) {\n const subMenuTitleElm = document.createElement('div');\n subMenuTitleElm.className = 'slick-menu-title';\n subMenuTitleElm.textContent = item.subMenuTitle as string;\n const subMenuTitleClass = item.subMenuTitleCssClass as string;\n if (subMenuTitleClass) {\n subMenuTitleElm.classList.add(...Utils.classNameToList(subMenuTitleClass));\n }\n\n commandOrOptionMenu.appendChild(subMenuTitleElm);\n }\n }\n\n protected handleCloseButtonClicked(e: DOMMouseOrTouchEvent) {\n if (!e.defaultPrevented) {\n this.closeMenu(e);\n }\n }\n\n /** Close and destroy Cell Menu */\n closeMenu(e?: DOMMouseOrTouchEvent | SlickEventData_, args?: MenuFromCellCallbackArgs) {\n if (this._menuElm) {\n if (this.onBeforeMenuClose.notify({\n cell: args?.cell ?? 0,\n row: args?.row ?? 0,\n grid: this._grid,\n }, e, this).getReturnValue() === false) {\n return;\n }\n this._menuElm.remove();\n this._menuElm = null;\n }\n this.destroySubMenus();\n }\n\n /** Destroy all parent menus and any sub-menus */\n destroyAllMenus() {\n this.destroySubMenus();\n\n // remove all parent menu listeners before removing them from the DOM\n this._bindingEventService.unbindAll('parent-menu');\n document.querySelectorAll(`.slick-cell-menu${this.getGridUidSelector()}`)\n .forEach(subElm => subElm.remove());\n }\n\n /** Close and destroy all previously opened sub-menus */\n destroySubMenus() {\n this._bindingEventService.unbindAll('sub-menu');\n document.querySelectorAll(`.slick-cell-menu.slick-submenu${this.getGridUidSelector()}`)\n .forEach(subElm => subElm.remove());\n }\n\n protected repositionSubMenu(item: MenuCommandItem | MenuOptionItem | 'divider', type: MenuType, level: number, e: DOMMouseOrTouchEvent) {\n // when we're clicking a grid cell OR our last menu type (command/option) differs then we know that we need to start fresh and close any sub-menus that might still be open\n if (e.target.classList.contains('slick-cell') || this._lastMenuTypeClicked !== type) {\n this.destroySubMenus();\n }\n\n // creating sub-menu, we'll also pass level & the item object since we might have \"subMenuTitle\" to show\n const subMenuElm = this.createMenu((item as MenuCommandItem)?.commandItems || [], (item as MenuOptionItem)?.optionItems || [], level + 1, item);\n subMenuElm.style.display = 'block';\n document.body.appendChild(subMenuElm);\n this.repositionMenu(e, subMenuElm);\n }\n\n /**\n * Reposition the menu drop (up/down) and the side (left/right)\n * @param {*} event\n */\n repositionMenu(e: DOMMouseOrTouchEvent, menuElm: HTMLElement) {\n const isSubMenu = menuElm.classList.contains('slick-submenu');\n const parentElm = isSubMenu\n ? e.target.closest('.slick-cell-menu-item') as HTMLDivElement\n : e.target.closest('.slick-cell') as HTMLDivElement;\n\n if (menuElm && parentElm) {\n const parentOffset = Utils.offset(parentElm);\n let menuOffsetLeft = parentElm ? parentOffset?.left ?? 0 : e?.pageX ?? 0;\n let menuOffsetTop = parentElm ? parentOffset?.top ?? 0 : e?.pageY ?? 0;\n const parentCellWidth = parentElm?.offsetWidth || 0;\n const menuHeight = menuElm?.offsetHeight ?? 0;\n const menuWidth = Number(menuElm?.offsetWidth ?? this._cellMenuProperties.width ?? 0);\n const rowHeight = this._gridOptions.rowHeight;\n const dropOffset = Number(this._cellMenuProperties.autoAdjustDropOffset || 0);\n const sideOffset = Number(this._cellMenuProperties.autoAlignSideOffset || 0);\n\n // if autoAdjustDrop is enable, we first need to see what position the drop will be located (defaults to bottom)\n // without necessary toggling it's position just yet, we just want to know the future position for calculation\n if (this._cellMenuProperties.autoAdjustDrop) {\n // since we reposition menu below slick cell, we need to take it in consideration and do our calculation from that element\n const spaceBottom = Utils.calculateAvailableSpace(parentElm).bottom;\n const spaceTop = Utils.calculateAvailableSpace(parentElm).top;\n const spaceBottomRemaining = spaceBottom + dropOffset - rowHeight!;\n const spaceTopRemaining = spaceTop - dropOffset + rowHeight!;\n const dropPosition = (spaceBottomRemaining < menuHeight && spaceTopRemaining > spaceBottomRemaining) ? 'top' : 'bottom';\n if (dropPosition === 'top') {\n menuElm.classList.remove('dropdown');\n menuElm.classList.add('dropup');\n if (isSubMenu) {\n menuOffsetTop -= (menuHeight - dropOffset - parentElm.clientHeight);\n } else {\n menuOffsetTop -= menuHeight - dropOffset;\n }\n } else {\n menuElm.classList.remove('dropup');\n menuElm.classList.add('dropdown');\n if (isSubMenu) {\n menuOffsetTop += dropOffset;\n } else {\n menuOffsetTop += rowHeight! + dropOffset;\n }\n }\n }\n\n // when auto-align is set, it will calculate whether it has enough space in the viewport to show the drop menu on the right (default)\n // if there isn't enough space on the right, it will automatically align the drop menu to the left (defaults to the right)\n // to simulate an align left, we actually need to know the width of the drop menu\n if (this._cellMenuProperties.autoAlignSide) {\n const gridPos = this._grid.getGridPosition();\n let subMenuPosCalc = menuOffsetLeft + Number(menuWidth); // calculate coordinate at caller element far right\n if (isSubMenu) {\n subMenuPosCalc += parentElm.clientWidth;\n }\n const browserWidth = document.documentElement.clientWidth;\n const dropSide = (subMenuPosCalc >= gridPos.width || subMenuPosCalc >= browserWidth) ? 'left' : 'right';\n if (dropSide === 'left') {\n menuElm.classList.remove('dropright');\n menuElm.classList.add('dropleft');\n if (isSubMenu) {\n menuOffsetLeft -= menuWidth - sideOffset;\n } else {\n menuOffsetLeft -= menuWidth - parentCellWidth - sideOffset;\n }\n } else {\n menuElm.classList.remove('dropleft');\n menuElm.classList.add('dropright');\n if (isSubMenu) {\n menuOffsetLeft += sideOffset + parentElm.offsetWidth;\n } else {\n menuOffsetLeft += sideOffset;\n }\n }\n }\n\n // ready to reposition the menu\n menuElm.style.top = `${menuOffsetTop}px`;\n menuElm.style.left = `${menuOffsetLeft}px`;\n }\n }\n\n protected getGridUidSelector() {\n const gridUid = this._grid.getUID() || '';\n return gridUid ? `.${gridUid}` : '';\n }\n\n protected handleCellClick(evt: SlickEventData_ | DOMMouseOrTouchEvent, args: MenuCommandItemCallbackArgs) {\n this.destroyAllMenus(); // make there's only 1 parent menu opened at a time\n const e = (evt instanceof SlickEventData) ? evt.getNativeEvent>() : evt;\n const cell = this._grid.getCellFromEvent(e);\n\n if (cell) {\n const dataContext = this._grid.getDataItem(cell.row);\n const columnDef = this._grid.getColumns()[cell.cell];\n\n // prevent event from bubbling but only on column that has a cell menu defined\n if (columnDef?.cellMenu) {\n e.preventDefault();\n }\n\n // merge the cellMenu of the column definition with the default properties\n this._cellMenuProperties = Utils.extend({}, this._cellMenuProperties, columnDef.cellMenu);\n\n // run the override function (when defined), if the result is false it won't go further\n args = args || {};\n args.column = columnDef;\n args.dataContext = dataContext;\n args.grid = this._grid;\n if (!this.runOverrideFunctionWhenExists(this._cellMenuProperties.menuUsabilityOverride, args)) {\n return;\n }\n\n // create the DOM element\n this._menuElm = this.createParentMenu(e);\n\n // reposition the menu to where the user clicked\n if (this._menuElm) {\n this.repositionMenu(e, this._menuElm);\n this._menuElm.setAttribute('aria-expanded', 'true');\n this._menuElm.style.display = 'block';\n }\n\n // Hide the menu on outside click.\n this._bindingEventService.bind(document.body, 'mousedown', this.handleBodyMouseDown.bind(this) as EventListener);\n }\n }\n\n /** When users click outside the Cell Menu, we will typically close the Cell Menu (and any sub-menus) */\n protected handleBodyMouseDown(e: DOMMouseOrTouchEvent) {\n // did we click inside the menu or any of its sub-menu(s)\n let isMenuClicked = false;\n if (this._menuElm?.contains(e.target)) {\n isMenuClicked = true;\n }\n if (!isMenuClicked) {\n document\n .querySelectorAll(`.slick-cell-menu.slick-submenu${this.getGridUidSelector()}`)\n .forEach(subElm => {\n if (subElm.contains(e.target)) {\n isMenuClicked = true;\n }\n });\n }\n\n if (this._menuElm !== e.target && !isMenuClicked && !e.defaultPrevented) {\n this.closeMenu(e, { cell: this._currentCell, row: this._currentRow, grid: this._grid });\n }\n }\n\n /** Build the Command Items section. */\n protected populateCommandOrOptionItems(\n itemType: MenuType,\n cellMenu: CellMenuOption,\n commandOrOptionMenuElm: HTMLElement,\n commandOrOptionItems: Array | Array,\n args: { cell: number, row: number, column: Column, dataContext: any, grid: SlickGrid, level: number }\n ) {\n if (!args || !commandOrOptionItems || !cellMenu) {\n return;\n }\n\n // user could pass a title on top of the Commands/Options section\n const level = args?.level || 0;\n const isSubMenu = level > 0;\n if (cellMenu?.[`${itemType}Title`] && !isSubMenu) {\n this[`_${itemType}TitleElm`] = document.createElement('div');\n this[`_${itemType}TitleElm`]!.className = 'slick-menu-title';\n this[`_${itemType}TitleElm`]!.textContent = cellMenu[`${itemType}Title`] as string;\n commandOrOptionMenuElm.appendChild(this[`_${itemType}TitleElm`]!);\n }\n\n for (let i = 0, ln = commandOrOptionItems.length; i < ln; i++) {\n let addClickListener = true;\n const item = commandOrOptionItems[i];\n\n // run each override functions to know if the item is visible and usable\n const isItemVisible = this.runOverrideFunctionWhenExists((item as MenuCommandItem | MenuOptionItem).itemVisibilityOverride, args);\n const isItemUsable = this.runOverrideFunctionWhenExists((item as MenuCommandItem | MenuOptionItem).itemUsabilityOverride, args);\n\n // if the result is not visible then there's no need to go further\n if (!isItemVisible) {\n continue;\n }\n\n // when the override is defined, we need to use its result to update the disabled property\n // so that \"handleMenuItemClick\" has the correct flag and won't trigger a command/option clicked event\n if (Object.prototype.hasOwnProperty.call(item, 'itemUsabilityOverride')) {\n (item as MenuCommandItem | MenuOptionItem).disabled = isItemUsable ? false : true;\n }\n\n const liElm = document.createElement('div');\n liElm.className = 'slick-cell-menu-item';\n liElm.role = 'menuitem';\n\n if ((item as MenuCommandItem | MenuOptionItem).divider || item === 'divider') {\n liElm.classList.add('slick-cell-menu-item-divider');\n addClickListener = false;\n }\n\n // if the item is disabled then add the disabled css class\n if ((item as MenuCommandItem | MenuOptionItem).disabled || !isItemUsable) {\n liElm.classList.add('slick-cell-menu-item-disabled');\n }\n\n // if the item is hidden then add the hidden css class\n if ((item as MenuCommandItem | MenuOptionItem).hidden) {\n liElm.classList.add('slick-cell-menu-item-hidden');\n }\n\n if ((item as MenuCommandItem | MenuOptionItem).cssClass) {\n liElm.classList.add(...Utils.classNameToList((item as MenuCommandItem | MenuOptionItem).cssClass));\n }\n\n if ((item as MenuCommandItem | MenuOptionItem).tooltip) {\n liElm.title = (item as MenuCommandItem | MenuOptionItem).tooltip || '';\n }\n\n const iconElm = document.createElement('div');\n iconElm.className = 'slick-cell-menu-icon';\n\n liElm.appendChild(iconElm);\n\n if ((item as MenuCommandItem | MenuOptionItem).iconCssClass) {\n iconElm.classList.add(...Utils.classNameToList((item as MenuCommandItem | MenuOptionItem).iconCssClass));\n }\n\n if ((item as MenuCommandItem | MenuOptionItem).iconImage) {\n iconElm.style.backgroundImage = `url(${(item as MenuCommandItem | MenuOptionItem).iconImage})`;\n }\n\n const textElm = document.createElement('span');\n textElm.className = 'slick-cell-menu-content';\n textElm.textContent = (item as MenuCommandItem | MenuOptionItem).title || '';\n\n liElm.appendChild(textElm);\n\n if ((item as MenuCommandItem | MenuOptionItem).textCssClass) {\n textElm.classList.add(...Utils.classNameToList((item as MenuCommandItem | MenuOptionItem).textCssClass));\n }\n\n commandOrOptionMenuElm.appendChild(liElm);\n\n if (addClickListener) {\n const eventGroup = isSubMenu ? 'sub-menu' : 'parent-menu';\n this._bindingEventService.bind(liElm, 'click', this.handleMenuItemClick.bind(this, item, itemType, level) as EventListener, undefined, eventGroup);\n }\n\n // optionally open sub-menu(s) by mouseover\n if (this._cellMenuProperties.subMenuOpenByEvent === 'mouseover') {\n this._bindingEventService.bind(liElm, 'mouseover', ((e: DOMMouseOrTouchEvent) => {\n if ((item as MenuCommandItem).commandItems || (item as MenuOptionItem).optionItems) {\n this.repositionSubMenu(item, itemType, level, e);\n this._lastMenuTypeClicked = itemType;\n } else if (!isSubMenu) {\n this.destroySubMenus();\n }\n }) as EventListener);\n }\n\n // the option/command item could be a sub-menu if it has another list of commands/options\n if ((item as MenuCommandItem).commandItems || (item as MenuOptionItem).optionItems) {\n const chevronElm = document.createElement('span');\n chevronElm.className = 'sub-item-chevron';\n if (this._cellMenuProperties.subItemChevronClass) {\n chevronElm.classList.add(...Utils.classNameToList(this._cellMenuProperties.subItemChevronClass));\n } else {\n chevronElm.textContent = '\u2B9E'; // \u2B9E or \u25B8\n }\n\n liElm.classList.add('slick-submenu-item');\n liElm.appendChild(chevronElm);\n continue;\n }\n }\n }\n\n protected handleMenuItemClick(item: MenuCommandItem | MenuOptionItem | 'divider', type: MenuType, level = 0, e: DOMMouseOrTouchEvent) {\n if ((item as never)?.[type] !== undefined && item !== 'divider' && !item.disabled && !(item as MenuCommandItem | MenuOptionItem).divider && this._currentCell !== undefined && this._currentRow !== undefined) {\n if (type === 'option' && !this._grid.getEditorLock().commitCurrentEdit()) {\n return;\n }\n const optionOrCommand = (item as any)[type] !== undefined ? (item as any)[type] : '';\n const row = this._currentRow;\n const cell = this._currentCell;\n const columnDef = this._grid.getColumns()[cell];\n const dataContext = this._grid.getDataItem(row);\n\n if (optionOrCommand !== undefined && !(item as any)[`${type}Items`]) {\n // user could execute a callback through 2 ways\n // via the onCommand/onOptionSelected event and/or an action callback\n const callbackArgs = {\n cell,\n row,\n grid: this._grid,\n [type]: optionOrCommand,\n item,\n column: columnDef,\n dataContext,\n };\n const eventType = type === 'command' ? 'onCommand' : 'onOptionSelected';\n this[eventType].notify(callbackArgs as any, e, this);\n\n // execute action callback when defined\n if (typeof item.action === 'function') {\n (item as any).action.call(this, e, callbackArgs);\n }\n\n // unless prevented, close the menu\n if (!e.defaultPrevented) {\n this.closeMenu(e, { cell, row, grid: this._grid });\n }\n } else if ((item as MenuCommandItem).commandItems || (item as MenuOptionItem).optionItems) {\n this.repositionSubMenu(item, type, level, e);\n } else {\n this.destroySubMenus();\n }\n this._lastMenuTypeClicked = type;\n }\n }\n\n /**\n * Method that user can pass to override the default behavior.\n * In order word, user can choose or an item is (usable/visible/enable) by providing his own logic.\n * @param overrideFn: override function callback\n * @param args: multiple arguments provided to the override (cell, row, columnDef, dataContext, grid)\n */\n protected runOverrideFunctionWhenExists(overrideFn: ((args: any) => boolean) | undefined, args: T): boolean {\n if (typeof overrideFn === 'function') {\n return overrideFn.call(this, args);\n }\n return true;\n }\n}\n\n// extend Slick namespace on window object when building as iife\nif (IIFE_ONLY && window.Slick) {\n Utils.extend(true, window, {\n Slick: {\n Plugins: {\n CellMenu: SlickCellMenu\n }\n }\n });\n}\n"], + "sourcesContent": ["import {\r\n BindingEventService as BindingEventService_,\r\n SlickEvent as SlickEvent_,\r\n SlickEventData as SlickEventData_,\r\n SlickEventHandler as SlickEventHandler_,\r\n Utils as Utils_\r\n} from '../slick.core.js';\r\nimport type {\r\n CellMenuOption,\r\n Column,\r\n DOMMouseOrTouchEvent,\r\n GridOption,\r\n MenuCommandItem,\r\n MenuCommandItemCallbackArgs,\r\n MenuFromCellCallbackArgs,\r\n MenuOptionItem,\r\n MenuOptionItemCallbackArgs,\r\n MenuType,\r\n SlickPlugin\r\n} 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 BindingEventService = IIFE_ONLY ? Slick.BindingEventService : BindingEventService_;\r\nconst SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_;\r\nconst SlickEventData = IIFE_ONLY ? Slick.EventData : SlickEventData_;\r\nconst EventHandler = IIFE_ONLY ? Slick.EventHandler : SlickEventHandler_;\r\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\r\n\r\n/**\r\n * A plugin to add Menu on a Cell click (click on the cell that has the cellMenu object defined)\r\n * The \"cellMenu\" is defined in a Column Definition object\r\n * Similar to the ContextMenu plugin (could be used in combo),\r\n * except that it subscribes to the cell \"onClick\" event (regular mouse click or touch).\r\n *\r\n * A general use of this plugin is for an Action Dropdown Menu to do certain things on the row that was clicked\r\n * You can use it to change the cell data property through a list of Options AND/OR through a list of Commands.\r\n *\r\n * USAGE:\r\n *\r\n * Add the slick.cellMenu.(js|css) files and register it with the grid.\r\n *\r\n * To specify a menu in a column header, extend the column definition like so:\r\n * var cellMenuPlugin = new Slick.Plugins.CellMenu(columns, grid, options);\r\n *\r\n * Available cellMenu options, by defining a cellMenu object:\r\n *\r\n * var columns = [\r\n * {\r\n * id: \"action\", name: \"Action\", field: \"action\", formatter: fakeButtonFormatter,\r\n * cellMenu: {\r\n * optionTitle: \"Change Effort Driven\",\r\n * optionItems: [\r\n * { option: true, title: \"True\", iconCssClass: 'checkmark' },\r\n * { option: false, title: \"False\" }\r\n * ],\r\n * commandTitle: \"Commands\",\r\n * commandItems: [\r\n * { command: \"delete-row\", title: \"Delete Row\", iconCssClass: \"sgi sgi-close\", cssClass: 'bold', textCssClass: \"red\" },\r\n * { divider: true },\r\n * \"divider\" // you can pass \"divider\" as a string or an object\r\n * { command: \"help\", title: \"Help\", iconCssClass: \"icon-help\" },\r\n * { command: \"help\", title: \"Disabled Command\", disabled: true },\r\n * ],\r\n * }\r\n * }\r\n * ];\r\n *\r\n *\r\n * Available cellMenu properties:\r\n * commandTitle: Title of the Command section (optional)\r\n * commandItems: Array of Command item objects (command/title pair)\r\n * optionTitle: Title of the Option section (optional)\r\n * optionItems: Array of Options item objects (option/title pair)\r\n * hideCloseButton: Hide the Close button on top right (defaults to false)\r\n * hideCommandSection: Hide the Commands section even when the commandItems array is filled (defaults to false)\r\n * hideMenuOnScroll: Do we want to hide the Cell Menu when a scrolling event occurs (defaults to true)?\r\n * hideOptionSection: Hide the Options section even when the optionItems array is filled (defaults to false)\r\n * maxHeight: Maximum height that the drop menu will have, can be a number (250) or text (\"none\")\r\n * width: Width that the drop menu will have, can be a number (250) or text (defaults to \"auto\")\r\n * autoAdjustDrop: Auto-align dropup or dropdown menu to the left or right depending on grid viewport available space (defaults to true)\r\n * autoAdjustDropOffset: Optionally add an offset to the auto-align of the drop menu (defaults to 0)\r\n * autoAlignSide: Auto-align drop menu to the left or right depending on grid viewport available space (defaults to true)\r\n * autoAlignSideOffset: Optionally add an offset to the left/right side auto-align (defaults to 0)\r\n * menuUsabilityOverride: Callback method that user can override the default behavior of enabling/disabling the menu from being usable (must be combined with a custom formatter)\r\n * subItemChevronClass: CSS class that can be added on the right side of a sub-item parent (typically a chevron-right icon)\r\n * subMenuOpenByEvent: defaults to \"mouseover\", what event type shoud we use to open sub-menu(s), 2 options are available: \"mouseover\" or \"click\"\r\n *\r\n *\r\n * Available menu Command/Option item properties:\r\n * action: Optionally define a callback function that gets executed when item is chosen (and/or use the onCommand event)\r\n * command: A command identifier to be passed to the onCommand event handlers (when using \"commandItems\").\r\n * option: An option to be passed to the onOptionSelected event handlers (when using \"optionItems\").\r\n * title: Menu item text label.\r\n * divider: Boolean which tells if the current item is a divider, not an actual command. You could also pass \"divider\" instead of an object\r\n * disabled: Whether the item/command is disabled.\r\n * hidden: Whether the item/command is hidden.\r\n * subMenuTitle: Optional sub-menu title that will shows up when sub-menu commmands/options list is opened\r\n * subMenuTitleCssClass: Optional sub-menu title CSS class to use with `subMenuTitle`\r\n * tooltip: Item tooltip.\r\n * cssClass: A CSS class to be added to the menu item container.\r\n * iconCssClass: A CSS class to be added to the menu item icon.\r\n * textCssClass: A CSS class to be added to the menu item text.\r\n * iconImage: A url to the icon image.\r\n * itemVisibilityOverride: Callback method that user can override the default behavior of showing/hiding an item from the list\r\n * itemUsabilityOverride: Callback method that user can override the default behavior of enabling/disabling an item from the list\r\n *\r\n *\r\n * The plugin exposes the following events:\r\n *\r\n * onAfterMenuShow: Fired after the menu is shown. You can customize the menu or dismiss it by returning false.\r\n * Event args:\r\n * cell: Cell or column index\r\n * row: Row index\r\n * grid: Reference to the grid.\r\n *\r\n * onBeforeMenuShow: Fired before the menu is shown. You can customize the menu or dismiss it by returning false.\r\n * Event args:\r\n * cell: Cell or column index\r\n * row: Row index\r\n * grid: Reference to the grid.\r\n *\r\n * onBeforeMenuClose: Fired when the menu is closing.\r\n * Event args:\r\n * cell: Cell or column index\r\n * row: Row index\r\n * grid: Reference to the grid.\r\n * menu: Menu DOM element\r\n *\r\n * onCommand: Fired on menu option clicked from the Command items list\r\n * Event args:\r\n * cell: Cell or column index\r\n * row: Row index\r\n * grid: Reference to the grid.\r\n * command: Menu command identified.\r\n * item: Menu item selected\r\n * column: Cell Column definition\r\n * dataContext: Cell Data Context (data object)\r\n *\r\n * onOptionSelected: Fired on menu option clicked from the Option items list\r\n * Event args:\r\n * cell: Cell or column index\r\n * row: Row index\r\n * grid: Reference to the grid.\r\n * option: Menu option selected.\r\n * item: Menu item selected\r\n * column: Cell Column definition\r\n * dataContext: Cell Data Context (data object)\r\n *\r\n *\r\n * @param options {Object} Cell Menu Options\r\n * @class Slick.Plugins.CellMenu\r\n */\r\nexport class SlickCellMenu implements SlickPlugin {\r\n // --\r\n // public API\r\n pluginName = 'CellMenu' as const;\r\n onAfterMenuShow = new SlickEvent('onAfterMenuShow');\r\n onBeforeMenuShow = new SlickEvent('onBeforeMenuShow');\r\n onBeforeMenuClose = new SlickEvent('onBeforeMenuClose');\r\n onCommand = new SlickEvent('onCommand');\r\n onOptionSelected = new SlickEvent('onOptionSelected');\r\n\r\n // --\r\n // protected props\r\n protected _bindingEventService = new BindingEventService();\r\n protected _cellMenuProperties: CellMenuOption;\r\n protected _currentCell = -1;\r\n protected _currentRow = -1;\r\n protected _grid!: SlickGrid;\r\n protected _gridOptions!: GridOption;\r\n protected _gridUid = '';\r\n protected _handler = new EventHandler();\r\n protected _commandTitleElm?: HTMLSpanElement;\r\n protected _optionTitleElm?: HTMLSpanElement;\r\n protected _lastMenuTypeClicked = '';\r\n protected _menuElm?: HTMLDivElement | null;\r\n protected _subMenuParentId = '';\r\n protected _defaults: CellMenuOption = {\r\n autoAdjustDrop: true, // dropup/dropdown\r\n autoAlignSide: true, // left/right\r\n autoAdjustDropOffset: 0,\r\n autoAlignSideOffset: 0,\r\n hideMenuOnScroll: true,\r\n maxHeight: 'none',\r\n width: 'auto',\r\n subMenuOpenByEvent: 'mouseover',\r\n };\r\n\r\n constructor(optionProperties: Partial) {\r\n this._cellMenuProperties = Utils.extend({}, this._defaults, optionProperties);\r\n }\r\n\r\n init(grid: SlickGrid) {\r\n this._grid = grid;\r\n this._gridOptions = grid.getOptions();\r\n Utils.addSlickEventPubSubWhenDefined(grid.getPubSubService(), this);\r\n this._gridUid = grid?.getUID() || '';\r\n this._handler.subscribe(this._grid.onClick as any, this.handleCellClick.bind(this));\r\n if (this._cellMenuProperties.hideMenuOnScroll) {\r\n this._handler.subscribe(this._grid.onScroll, this.closeMenu.bind(this));\r\n }\r\n }\r\n\r\n setOptions(newOptions: Partial) {\r\n this._cellMenuProperties = Utils.extend({}, this._cellMenuProperties, newOptions);\r\n }\r\n\r\n destroy() {\r\n this.onAfterMenuShow.unsubscribe();\r\n this.onBeforeMenuShow.unsubscribe();\r\n this.onBeforeMenuClose.unsubscribe();\r\n this.onCommand.unsubscribe();\r\n this.onOptionSelected.unsubscribe();\r\n this._handler.unsubscribeAll();\r\n this._bindingEventService.unbindAll();\r\n this._menuElm?.remove();\r\n this._commandTitleElm = null as any;\r\n this._optionTitleElm = null as any;\r\n this._menuElm = null as any;\r\n }\r\n\r\n protected createParentMenu(e: DOMMouseOrTouchEvent) {\r\n const cell = this._grid.getCellFromEvent(e);\r\n this._currentCell = cell?.cell ?? 0;\r\n this._currentRow = cell?.row ?? 0;\r\n const columnDef = this._grid.getColumns()[this._currentCell];\r\n\r\n const commandItems = this._cellMenuProperties.commandItems || [];\r\n const optionItems = this._cellMenuProperties.optionItems || [];\r\n\r\n // make sure there's at least something to show before creating the Cell Menu\r\n if (!columnDef || !columnDef.cellMenu || (!commandItems.length && !optionItems.length)) {\r\n return;\r\n }\r\n\r\n // delete any prior Cell Menu\r\n this.closeMenu();\r\n\r\n // Let the user modify the menu or cancel altogether,\r\n // or provide alternative menu implementation.\r\n if (this.onBeforeMenuShow.notify({\r\n cell: this._currentCell,\r\n row: this._currentRow,\r\n grid: this._grid\r\n }, e, this).getReturnValue() === false) {\r\n return;\r\n }\r\n\r\n // create 1st parent menu container & reposition it\r\n this._menuElm = this.createMenu(commandItems, optionItems);\r\n this._menuElm.style.top = `${e.pageY + 5}px`;\r\n this._menuElm.style.left = `${e.pageX}px`;\r\n this._menuElm.style.display = 'block';\r\n document.body.appendChild(this._menuElm);\r\n\r\n if (this.onAfterMenuShow.notify({\r\n cell: this._currentCell,\r\n row: this._currentRow,\r\n grid: this._grid\r\n }, e, this).getReturnValue() === false) {\r\n return;\r\n }\r\n\r\n return this._menuElm;\r\n }\r\n\r\n /**\r\n * Create parent menu or sub-menu(s), a parent menu will start at level 0 while sub-menu(s) will be incremented\r\n * @param commandItems - array of optional commands or dividers\r\n * @param optionItems - array of optional options or dividers\r\n * @param level - menu level\r\n * @param item - command, option or divider\r\n * @returns menu DOM element\r\n */\r\n protected createMenu(commandItems: Array, optionItems: Array, level = 0, item?: MenuCommandItem | MenuOptionItem | 'divider') {\r\n const columnDef = this._grid.getColumns()[this._currentCell];\r\n const dataContext = this._grid.getDataItem(this._currentRow);\r\n\r\n // create a new cell menu\r\n const maxHeight = isNaN(this._cellMenuProperties.maxHeight as number) ? this._cellMenuProperties.maxHeight : `${this._cellMenuProperties.maxHeight ?? 0}px`;\r\n const width = isNaN(this._cellMenuProperties.width as number) ? this._cellMenuProperties.width : `${this._cellMenuProperties.maxWidth ?? 0}px`;\r\n\r\n // to avoid having multiple sub-menu trees opened,\r\n // we need to somehow keep trace of which parent menu the tree belongs to\r\n // and we should keep ref of only the first sub-menu parent, we can use the command name (remove any whitespaces though)\r\n const subMenuCommand = (item as MenuCommandItem)?.command;\r\n let subMenuId = (level === 1 && subMenuCommand) ? subMenuCommand.replaceAll(' ', '') : '';\r\n if (subMenuId) {\r\n this._subMenuParentId = subMenuId;\r\n }\r\n if (level > 1) {\r\n subMenuId = this._subMenuParentId;\r\n }\r\n\r\n const menuClasses = `slick-cell-menu slick-menu-level-${level} ${this._gridUid}`;\r\n const bodyMenuElm = document.body.querySelector(`.slick-cell-menu.slick-menu-level-${level}${this.getGridUidSelector()}`);\r\n\r\n // return menu/sub-menu if it's already opened unless we are on different sub-menu tree if so close them all\r\n if (bodyMenuElm) {\r\n if (bodyMenuElm.dataset.subMenuParent === subMenuId) {\r\n return bodyMenuElm;\r\n }\r\n this.destroySubMenus();\r\n }\r\n\r\n const menuElm = document.createElement('div');\r\n menuElm.className = menuClasses;\r\n if (level > 0) {\r\n menuElm.classList.add('slick-submenu');\r\n if (subMenuId) {\r\n menuElm.dataset.subMenuParent = subMenuId;\r\n }\r\n }\r\n menuElm.ariaLabel = level > 1 ? 'SubMenu' : 'Cell Menu';\r\n menuElm.role = 'menu';\r\n if (width) {\r\n menuElm.style.width = width as string;\r\n }\r\n if (maxHeight) {\r\n menuElm.style.maxHeight = maxHeight as string;\r\n }\r\n\r\n menuElm.style.display = 'none';\r\n\r\n let closeButtonElm: HTMLButtonElement | null = null;\r\n if (level === 0) {\r\n closeButtonElm = document.createElement('button');\r\n closeButtonElm.type = 'button';\r\n closeButtonElm.className = 'close';\r\n closeButtonElm.dataset.dismiss = 'slick-cell-menu';\r\n closeButtonElm.ariaLabel = 'Close';\r\n\r\n const spanCloseElm = document.createElement('span');\r\n spanCloseElm.className = 'close';\r\n spanCloseElm.ariaHidden = 'true';\r\n spanCloseElm.textContent = '\u00D7';\r\n closeButtonElm.appendChild(spanCloseElm);\r\n }\r\n\r\n // -- Option List section\r\n if (!this._cellMenuProperties.hideOptionSection && optionItems.length > 0) {\r\n const optionMenuElm = document.createElement('div');\r\n optionMenuElm.className = 'slick-cell-menu-option-list';\r\n optionMenuElm.role = 'menu';\r\n\r\n // when creating sub-menu add its sub-menu title when exists\r\n if (item && level > 0) {\r\n this.addSubMenuTitleWhenExists(item, optionMenuElm); // add sub-menu title when exists\r\n }\r\n\r\n if (closeButtonElm && !this._cellMenuProperties.hideCloseButton) {\r\n this._bindingEventService.bind(closeButtonElm, 'click', this.handleCloseButtonClicked.bind(this) as EventListener);\r\n menuElm.appendChild(closeButtonElm);\r\n }\r\n menuElm.appendChild(optionMenuElm);\r\n\r\n this.populateCommandOrOptionItems(\r\n 'option',\r\n this._cellMenuProperties,\r\n optionMenuElm,\r\n optionItems,\r\n { cell: this._currentCell, row: this._currentRow, column: columnDef, dataContext, grid: this._grid, level }\r\n );\r\n }\r\n\r\n // -- Command List section\r\n if (!this._cellMenuProperties.hideCommandSection && commandItems.length > 0) {\r\n const commandMenuElm = document.createElement('div');\r\n commandMenuElm.className = 'slick-cell-menu-command-list';\r\n commandMenuElm.role = 'menu';\r\n\r\n // when creating sub-menu add its sub-menu title when exists\r\n if (item && level > 0) {\r\n this.addSubMenuTitleWhenExists(item, commandMenuElm); // add sub-menu title when exists\r\n }\r\n\r\n if (closeButtonElm && !this._cellMenuProperties.hideCloseButton && (optionItems.length === 0 || this._cellMenuProperties.hideOptionSection)) {\r\n this._bindingEventService.bind(closeButtonElm, 'click', this.handleCloseButtonClicked.bind(this) as EventListener);\r\n menuElm.appendChild(closeButtonElm);\r\n }\r\n menuElm.appendChild(commandMenuElm);\r\n\r\n this.populateCommandOrOptionItems(\r\n 'command',\r\n this._cellMenuProperties,\r\n commandMenuElm,\r\n commandItems,\r\n { cell: this._currentCell, row: this._currentRow, column: columnDef, dataContext, grid: this._grid, level }\r\n );\r\n }\r\n\r\n // increment level for possible next sub-menus if exists\r\n level++;\r\n\r\n return menuElm;\r\n }\r\n\r\n protected addSubMenuTitleWhenExists(item: MenuCommandItem | MenuOptionItem | 'divider', commandOrOptionMenu: HTMLDivElement) {\r\n if (item !== 'divider' && item?.subMenuTitle) {\r\n const subMenuTitleElm = document.createElement('div');\r\n subMenuTitleElm.className = 'slick-menu-title';\r\n subMenuTitleElm.textContent = item.subMenuTitle as string;\r\n const subMenuTitleClass = item.subMenuTitleCssClass as string;\r\n if (subMenuTitleClass) {\r\n subMenuTitleElm.classList.add(...Utils.classNameToList(subMenuTitleClass));\r\n }\r\n\r\n commandOrOptionMenu.appendChild(subMenuTitleElm);\r\n }\r\n }\r\n\r\n protected handleCloseButtonClicked(e: DOMMouseOrTouchEvent) {\r\n if (!e.defaultPrevented) {\r\n this.closeMenu(e);\r\n }\r\n }\r\n\r\n /** Close and destroy Cell Menu */\r\n closeMenu(e?: DOMMouseOrTouchEvent | SlickEventData_, args?: MenuFromCellCallbackArgs) {\r\n if (this._menuElm) {\r\n if (this.onBeforeMenuClose.notify({\r\n cell: args?.cell ?? 0,\r\n row: args?.row ?? 0,\r\n grid: this._grid,\r\n }, e, this).getReturnValue() === false) {\r\n return;\r\n }\r\n this._menuElm.remove();\r\n this._menuElm = null;\r\n }\r\n this.destroySubMenus();\r\n }\r\n\r\n /** Destroy all parent menus and any sub-menus */\r\n destroyAllMenus() {\r\n this.destroySubMenus();\r\n\r\n // remove all parent menu listeners before removing them from the DOM\r\n this._bindingEventService.unbindAll('parent-menu');\r\n document.querySelectorAll(`.slick-cell-menu${this.getGridUidSelector()}`)\r\n .forEach(subElm => subElm.remove());\r\n }\r\n\r\n /** Close and destroy all previously opened sub-menus */\r\n destroySubMenus() {\r\n this._bindingEventService.unbindAll('sub-menu');\r\n document.querySelectorAll(`.slick-cell-menu.slick-submenu${this.getGridUidSelector()}`)\r\n .forEach(subElm => subElm.remove());\r\n }\r\n\r\n protected repositionSubMenu(item: MenuCommandItem | MenuOptionItem | 'divider', type: MenuType, level: number, e: DOMMouseOrTouchEvent) {\r\n // when we're clicking a grid cell OR our last menu type (command/option) differs then we know that we need to start fresh and close any sub-menus that might still be open\r\n if (e.target.classList.contains('slick-cell') || this._lastMenuTypeClicked !== type) {\r\n this.destroySubMenus();\r\n }\r\n\r\n // creating sub-menu, we'll also pass level & the item object since we might have \"subMenuTitle\" to show\r\n const subMenuElm = this.createMenu((item as MenuCommandItem)?.commandItems || [], (item as MenuOptionItem)?.optionItems || [], level + 1, item);\r\n subMenuElm.style.display = 'block';\r\n document.body.appendChild(subMenuElm);\r\n this.repositionMenu(e, subMenuElm);\r\n }\r\n\r\n /**\r\n * Reposition the menu drop (up/down) and the side (left/right)\r\n * @param {*} event\r\n */\r\n repositionMenu(e: DOMMouseOrTouchEvent, menuElm: HTMLElement) {\r\n const isSubMenu = menuElm.classList.contains('slick-submenu');\r\n const parentElm = isSubMenu\r\n ? e.target.closest('.slick-cell-menu-item') as HTMLDivElement\r\n : e.target.closest('.slick-cell') as HTMLDivElement;\r\n\r\n if (menuElm && parentElm) {\r\n const parentOffset = Utils.offset(parentElm);\r\n let menuOffsetLeft = parentElm ? parentOffset?.left ?? 0 : e?.pageX ?? 0;\r\n let menuOffsetTop = parentElm ? parentOffset?.top ?? 0 : e?.pageY ?? 0;\r\n const parentCellWidth = parentElm?.offsetWidth || 0;\r\n const menuHeight = menuElm?.offsetHeight ?? 0;\r\n const menuWidth = Number(menuElm?.offsetWidth ?? this._cellMenuProperties.width ?? 0);\r\n const rowHeight = this._gridOptions.rowHeight;\r\n const dropOffset = Number(this._cellMenuProperties.autoAdjustDropOffset || 0);\r\n const sideOffset = Number(this._cellMenuProperties.autoAlignSideOffset || 0);\r\n\r\n // if autoAdjustDrop is enable, we first need to see what position the drop will be located (defaults to bottom)\r\n // without necessary toggling it's position just yet, we just want to know the future position for calculation\r\n if (this._cellMenuProperties.autoAdjustDrop) {\r\n // since we reposition menu below slick cell, we need to take it in consideration and do our calculation from that element\r\n const spaceBottom = Utils.calculateAvailableSpace(parentElm).bottom;\r\n const spaceTop = Utils.calculateAvailableSpace(parentElm).top;\r\n const spaceBottomRemaining = spaceBottom + dropOffset - rowHeight!;\r\n const spaceTopRemaining = spaceTop - dropOffset + rowHeight!;\r\n const dropPosition = (spaceBottomRemaining < menuHeight && spaceTopRemaining > spaceBottomRemaining) ? 'top' : 'bottom';\r\n if (dropPosition === 'top') {\r\n menuElm.classList.remove('dropdown');\r\n menuElm.classList.add('dropup');\r\n if (isSubMenu) {\r\n menuOffsetTop -= (menuHeight - dropOffset - parentElm.clientHeight);\r\n } else {\r\n menuOffsetTop -= menuHeight - dropOffset;\r\n }\r\n } else {\r\n menuElm.classList.remove('dropup');\r\n menuElm.classList.add('dropdown');\r\n if (isSubMenu) {\r\n menuOffsetTop += dropOffset;\r\n } else {\r\n menuOffsetTop += rowHeight! + dropOffset;\r\n }\r\n }\r\n }\r\n\r\n // when auto-align is set, it will calculate whether it has enough space in the viewport to show the drop menu on the right (default)\r\n // if there isn't enough space on the right, it will automatically align the drop menu to the left (defaults to the right)\r\n // to simulate an align left, we actually need to know the width of the drop menu\r\n if (this._cellMenuProperties.autoAlignSide) {\r\n const gridPos = this._grid.getGridPosition();\r\n let subMenuPosCalc = menuOffsetLeft + Number(menuWidth); // calculate coordinate at caller element far right\r\n if (isSubMenu) {\r\n subMenuPosCalc += parentElm.clientWidth;\r\n }\r\n const browserWidth = document.documentElement.clientWidth;\r\n const dropSide = (subMenuPosCalc >= gridPos.width || subMenuPosCalc >= browserWidth) ? 'left' : 'right';\r\n if (dropSide === 'left') {\r\n menuElm.classList.remove('dropright');\r\n menuElm.classList.add('dropleft');\r\n if (isSubMenu) {\r\n menuOffsetLeft -= menuWidth - sideOffset;\r\n } else {\r\n menuOffsetLeft -= menuWidth - parentCellWidth - sideOffset;\r\n }\r\n } else {\r\n menuElm.classList.remove('dropleft');\r\n menuElm.classList.add('dropright');\r\n if (isSubMenu) {\r\n menuOffsetLeft += sideOffset + parentElm.offsetWidth;\r\n } else {\r\n menuOffsetLeft += sideOffset;\r\n }\r\n }\r\n }\r\n\r\n // ready to reposition the menu\r\n menuElm.style.top = `${menuOffsetTop}px`;\r\n menuElm.style.left = `${menuOffsetLeft}px`;\r\n }\r\n }\r\n\r\n protected getGridUidSelector() {\r\n const gridUid = this._grid.getUID() || '';\r\n return gridUid ? `.${gridUid}` : '';\r\n }\r\n\r\n protected handleCellClick(evt: SlickEventData_ | DOMMouseOrTouchEvent, args: MenuCommandItemCallbackArgs) {\r\n this.destroyAllMenus(); // make there's only 1 parent menu opened at a time\r\n const e = (evt instanceof SlickEventData) ? evt.getNativeEvent>() : evt;\r\n const cell = this._grid.getCellFromEvent(e);\r\n\r\n if (cell) {\r\n const dataContext = this._grid.getDataItem(cell.row);\r\n const columnDef = this._grid.getColumns()[cell.cell];\r\n\r\n // prevent event from bubbling but only on column that has a cell menu defined\r\n if (columnDef?.cellMenu) {\r\n e.preventDefault();\r\n }\r\n\r\n // merge the cellMenu of the column definition with the default properties\r\n this._cellMenuProperties = Utils.extend({}, this._cellMenuProperties, columnDef.cellMenu);\r\n\r\n // run the override function (when defined), if the result is false it won't go further\r\n args = args || {};\r\n args.column = columnDef;\r\n args.dataContext = dataContext;\r\n args.grid = this._grid;\r\n if (!this.runOverrideFunctionWhenExists(this._cellMenuProperties.menuUsabilityOverride, args)) {\r\n return;\r\n }\r\n\r\n // create the DOM element\r\n this._menuElm = this.createParentMenu(e);\r\n\r\n // reposition the menu to where the user clicked\r\n if (this._menuElm) {\r\n this.repositionMenu(e, this._menuElm);\r\n this._menuElm.setAttribute('aria-expanded', 'true');\r\n this._menuElm.style.display = 'block';\r\n }\r\n\r\n // Hide the menu on outside click.\r\n this._bindingEventService.bind(document.body, 'mousedown', this.handleBodyMouseDown.bind(this) as EventListener);\r\n }\r\n }\r\n\r\n /** When users click outside the Cell Menu, we will typically close the Cell Menu (and any sub-menus) */\r\n protected handleBodyMouseDown(e: DOMMouseOrTouchEvent) {\r\n // did we click inside the menu or any of its sub-menu(s)\r\n let isMenuClicked = false;\r\n if (this._menuElm?.contains(e.target)) {\r\n isMenuClicked = true;\r\n }\r\n if (!isMenuClicked) {\r\n document\r\n .querySelectorAll(`.slick-cell-menu.slick-submenu${this.getGridUidSelector()}`)\r\n .forEach(subElm => {\r\n if (subElm.contains(e.target)) {\r\n isMenuClicked = true;\r\n }\r\n });\r\n }\r\n\r\n if (this._menuElm !== e.target && !isMenuClicked && !e.defaultPrevented) {\r\n this.closeMenu(e, { cell: this._currentCell, row: this._currentRow, grid: this._grid });\r\n }\r\n }\r\n\r\n /** Build the Command Items section. */\r\n protected populateCommandOrOptionItems(\r\n itemType: MenuType,\r\n cellMenu: CellMenuOption,\r\n commandOrOptionMenuElm: HTMLElement,\r\n commandOrOptionItems: Array | Array,\r\n args: { cell: number, row: number, column: Column, dataContext: any, grid: SlickGrid, level: number }\r\n ) {\r\n if (!args || !commandOrOptionItems || !cellMenu) {\r\n return;\r\n }\r\n\r\n // user could pass a title on top of the Commands/Options section\r\n const level = args?.level || 0;\r\n const isSubMenu = level > 0;\r\n if (cellMenu?.[`${itemType}Title`] && !isSubMenu) {\r\n this[`_${itemType}TitleElm`] = document.createElement('div');\r\n this[`_${itemType}TitleElm`]!.className = 'slick-menu-title';\r\n this[`_${itemType}TitleElm`]!.textContent = cellMenu[`${itemType}Title`] as string;\r\n commandOrOptionMenuElm.appendChild(this[`_${itemType}TitleElm`]!);\r\n }\r\n\r\n for (let i = 0, ln = commandOrOptionItems.length; i < ln; i++) {\r\n let addClickListener = true;\r\n const item = commandOrOptionItems[i];\r\n\r\n // run each override functions to know if the item is visible and usable\r\n const isItemVisible = this.runOverrideFunctionWhenExists((item as MenuCommandItem | MenuOptionItem).itemVisibilityOverride, args);\r\n const isItemUsable = this.runOverrideFunctionWhenExists((item as MenuCommandItem | MenuOptionItem).itemUsabilityOverride, args);\r\n\r\n // if the result is not visible then there's no need to go further\r\n if (!isItemVisible) {\r\n continue;\r\n }\r\n\r\n // when the override is defined, we need to use its result to update the disabled property\r\n // so that \"handleMenuItemClick\" has the correct flag and won't trigger a command/option clicked event\r\n if (Object.prototype.hasOwnProperty.call(item, 'itemUsabilityOverride')) {\r\n (item as MenuCommandItem | MenuOptionItem).disabled = isItemUsable ? false : true;\r\n }\r\n\r\n const liElm = document.createElement('div');\r\n liElm.className = 'slick-cell-menu-item';\r\n liElm.role = 'menuitem';\r\n\r\n if ((item as MenuCommandItem | MenuOptionItem).divider || item === 'divider') {\r\n liElm.classList.add('slick-cell-menu-item-divider');\r\n addClickListener = false;\r\n }\r\n\r\n // if the item is disabled then add the disabled css class\r\n if ((item as MenuCommandItem | MenuOptionItem).disabled || !isItemUsable) {\r\n liElm.classList.add('slick-cell-menu-item-disabled');\r\n }\r\n\r\n // if the item is hidden then add the hidden css class\r\n if ((item as MenuCommandItem | MenuOptionItem).hidden) {\r\n liElm.classList.add('slick-cell-menu-item-hidden');\r\n }\r\n\r\n if ((item as MenuCommandItem | MenuOptionItem).cssClass) {\r\n liElm.classList.add(...Utils.classNameToList((item as MenuCommandItem | MenuOptionItem).cssClass));\r\n }\r\n\r\n if ((item as MenuCommandItem | MenuOptionItem).tooltip) {\r\n liElm.title = (item as MenuCommandItem | MenuOptionItem).tooltip || '';\r\n }\r\n\r\n const iconElm = document.createElement('div');\r\n iconElm.className = 'slick-cell-menu-icon';\r\n\r\n liElm.appendChild(iconElm);\r\n\r\n if ((item as MenuCommandItem | MenuOptionItem).iconCssClass) {\r\n iconElm.classList.add(...Utils.classNameToList((item as MenuCommandItem | MenuOptionItem).iconCssClass));\r\n }\r\n\r\n if ((item as MenuCommandItem | MenuOptionItem).iconImage) {\r\n iconElm.style.backgroundImage = `url(${(item as MenuCommandItem | MenuOptionItem).iconImage})`;\r\n }\r\n\r\n const textElm = document.createElement('span');\r\n textElm.className = 'slick-cell-menu-content';\r\n textElm.textContent = (item as MenuCommandItem | MenuOptionItem).title || '';\r\n\r\n liElm.appendChild(textElm);\r\n\r\n if ((item as MenuCommandItem | MenuOptionItem).textCssClass) {\r\n textElm.classList.add(...Utils.classNameToList((item as MenuCommandItem | MenuOptionItem).textCssClass));\r\n }\r\n\r\n commandOrOptionMenuElm.appendChild(liElm);\r\n\r\n if (addClickListener) {\r\n const eventGroup = isSubMenu ? 'sub-menu' : 'parent-menu';\r\n this._bindingEventService.bind(liElm, 'click', this.handleMenuItemClick.bind(this, item, itemType, level) as EventListener, undefined, eventGroup);\r\n }\r\n\r\n // optionally open sub-menu(s) by mouseover\r\n if (this._cellMenuProperties.subMenuOpenByEvent === 'mouseover') {\r\n this._bindingEventService.bind(liElm, 'mouseover', ((e: DOMMouseOrTouchEvent) => {\r\n if ((item as MenuCommandItem).commandItems || (item as MenuOptionItem).optionItems) {\r\n this.repositionSubMenu(item, itemType, level, e);\r\n this._lastMenuTypeClicked = itemType;\r\n } else if (!isSubMenu) {\r\n this.destroySubMenus();\r\n }\r\n }) as EventListener);\r\n }\r\n\r\n // the option/command item could be a sub-menu if it has another list of commands/options\r\n if ((item as MenuCommandItem).commandItems || (item as MenuOptionItem).optionItems) {\r\n const chevronElm = document.createElement('span');\r\n chevronElm.className = 'sub-item-chevron';\r\n if (this._cellMenuProperties.subItemChevronClass) {\r\n chevronElm.classList.add(...Utils.classNameToList(this._cellMenuProperties.subItemChevronClass));\r\n } else {\r\n chevronElm.textContent = '\u2B9E'; // \u2B9E or \u25B8\r\n }\r\n\r\n liElm.classList.add('slick-submenu-item');\r\n liElm.appendChild(chevronElm);\r\n continue;\r\n }\r\n }\r\n }\r\n\r\n protected handleMenuItemClick(item: MenuCommandItem | MenuOptionItem | 'divider', type: MenuType, level = 0, e: DOMMouseOrTouchEvent) {\r\n if ((item as never)?.[type] !== undefined && item !== 'divider' && !item.disabled && !(item as MenuCommandItem | MenuOptionItem).divider && this._currentCell !== undefined && this._currentRow !== undefined) {\r\n if (type === 'option' && !this._grid.getEditorLock().commitCurrentEdit()) {\r\n return;\r\n }\r\n const optionOrCommand = (item as any)[type] !== undefined ? (item as any)[type] : '';\r\n const row = this._currentRow;\r\n const cell = this._currentCell;\r\n const columnDef = this._grid.getColumns()[cell];\r\n const dataContext = this._grid.getDataItem(row);\r\n\r\n if (optionOrCommand !== undefined && !(item as any)[`${type}Items`]) {\r\n // user could execute a callback through 2 ways\r\n // via the onCommand/onOptionSelected event and/or an action callback\r\n const callbackArgs = {\r\n cell,\r\n row,\r\n grid: this._grid,\r\n [type]: optionOrCommand,\r\n item,\r\n column: columnDef,\r\n dataContext,\r\n };\r\n const eventType = type === 'command' ? 'onCommand' : 'onOptionSelected';\r\n this[eventType].notify(callbackArgs as any, e, this);\r\n\r\n // execute action callback when defined\r\n if (typeof item.action === 'function') {\r\n (item as any).action.call(this, e, callbackArgs);\r\n }\r\n\r\n // unless prevented, close the menu\r\n if (!e.defaultPrevented) {\r\n this.closeMenu(e, { cell, row, grid: this._grid });\r\n }\r\n } else if ((item as MenuCommandItem).commandItems || (item as MenuOptionItem).optionItems) {\r\n this.repositionSubMenu(item, type, level, e);\r\n } else {\r\n this.destroySubMenus();\r\n }\r\n this._lastMenuTypeClicked = type;\r\n }\r\n }\r\n\r\n /**\r\n * Method that user can pass to override the default behavior.\r\n * In order word, user can choose or an item is (usable/visible/enable) by providing his own logic.\r\n * @param overrideFn: override function callback\r\n * @param args: multiple arguments provided to the override (cell, row, columnDef, dataContext, grid)\r\n */\r\n protected runOverrideFunctionWhenExists(overrideFn: ((args: any) => boolean) | undefined, args: T): boolean {\r\n if (typeof overrideFn === 'function') {\r\n return overrideFn.call(this, args);\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 Plugins: {\r\n CellMenu: SlickCellMenu\r\n }\r\n }\r\n });\r\n}\r\n"], "mappings": "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", "names": [] } diff --git a/dist/browser/plugins/slick.cellrangedecorator.js.map b/dist/browser/plugins/slick.cellrangedecorator.js.map index f5dc82c8..7da7f458 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';\nimport { Utils as Utils_, type SlickRange } from '../slick.core';\nimport type { SlickGrid } from '../slick.grid';\n\n// for (iife) load Slick methods from global Slick object, or use imports for (esm)\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\n\n/***\n * Displays an overlay on top of a given cell range.\n *\n * TODO:\n * Currently, it blocks mouse events to DOM nodes behind it.\n * Use FF and WebKit-specific \"pointer-events\" CSS style, or some kind of event forwarding.\n * Could also construct the borders separately using 4 individual DIVs.\n *\n * @param {Grid} grid\n * @param {Object} options\n */\nexport class SlickCellRangeDecorator implements SlickPlugin {\n // --\n // public API\n pluginName = 'CellRangeDecorator' as const;\n\n // --\n // protected props\n protected _options: CellRangeDecoratorOption;\n protected _elem?: HTMLDivElement | null;\n protected _defaults = {\n selectionCssClass: 'slick-range-decorator',\n selectionCss: {\n zIndex: '9999',\n border: '2px dashed red'\n },\n offset: { top: -1, left: -1, height: -2, width: -2 }\n } as CellRangeDecoratorOption;\n\n constructor(protected readonly grid: SlickGrid, options?: Partial) {\n this._options = Utils.extend(true, {}, this._defaults, options);\n }\n\n destroy() {\n this.hide();\n }\n\n init() { }\n\n hide() {\n this._elem?.remove();\n this._elem = null;\n }\n\n show(range: SlickRange) {\n if (!this._elem) {\n this._elem = document.createElement('div');\n this._elem.className = this._options.selectionCssClass;\n Object.keys(this._options.selectionCss as CSSStyleDeclaration).forEach((cssStyleKey) => {\n this._elem!.style[cssStyleKey as CSSStyleDeclarationWritable] = this._options.selectionCss[cssStyleKey as CSSStyleDeclarationWritable];\n });\n this._elem.style.position = 'absolute';\n const canvasNode = this.grid.getActiveCanvasNode();\n if (canvasNode) {\n canvasNode.appendChild(this._elem);\n }\n }\n\n const from = this.grid.getCellNodeBox(range.fromRow, range.fromCell);\n const to = this.grid.getCellNodeBox(range.toRow, range.toCell);\n\n if (from && to && this._options?.offset) {\n this._elem.style.top = `${from.top + this._options.offset.top}px`;\n this._elem.style.left = `${from.left + this._options.offset.left}px`;\n this._elem.style.height = `${to.bottom - from.top + this._options.offset.height}px`;\n this._elem.style.width = `${to.right - from.left + this._options.offset.width}px`;\n }\n\n return this._elem;\n }\n}\n\n// extend Slick namespace on window object when building as iife\nif (IIFE_ONLY && window.Slick) {\n Utils.extend(true, window, {\n Slick: {\n CellRangeDecorator: SlickCellRangeDecorator\n }\n });\n}\n"], + "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;", "names": [] } diff --git a/dist/browser/plugins/slick.cellrangeselector.js.map b/dist/browser/plugins/slick.cellrangeselector.js.map index 74aee243..683575af 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';\r\nimport { Draggable as Draggable_ } from '../slick.interactions';\r\nimport { SlickCellRangeDecorator as SlickCellRangeDecorator_ } from './slick.cellrangedecorator';\r\nimport type { CellRangeSelectorOption, DragPosition, DragRange, DragRowMove, GridOption, MouseOffsetViewport, OnScrollEventArgs, SlickPlugin } from '../models/index';\r\nimport type { SlickGrid } from '../slick.grid';\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"], + "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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", "names": [] } diff --git a/dist/browser/plugins/slick.cellselectionmodel.js.map b/dist/browser/plugins/slick.cellselectionmodel.js.map index d1251994..f29125c1 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';\nimport { SlickCellRangeSelector as SlickCellRangeSelector_ } from './slick.cellrangeselector';\nimport type { CustomDataView, OnActiveCellChangedEventArgs } from '../models/index';\nimport type { SlickDataView } from '../slick.dataview';\nimport type { SlickGrid } from '../slick.grid';\n\n// for (iife) load Slick methods from global Slick object, or use imports for (esm)\nconst SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_;\nconst SlickEventData = IIFE_ONLY ? Slick.EventData : SlickEventData_;\nconst SlickRange = IIFE_ONLY ? Slick.Range : SlickRange_;\nconst SlickCellRangeSelector = IIFE_ONLY ? Slick.CellRangeSelector : SlickCellRangeSelector_;\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\n\nexport interface CellSelectionModelOption {\n selectActiveCell: boolean;\n cellRangeSelector?: SlickCellRangeSelector_;\n}\n\nexport class SlickCellSelectionModel {\n // --\n // public API\n pluginName = 'CellSelectionModel' as const;\n onSelectedRangesChanged = new SlickEvent('onSelectedRangesChanged');\n\n // --\n // protected props\n protected _cachedPageRowCount = 0;\n protected _dataView?: CustomDataView | SlickDataView;\n protected _grid!: SlickGrid;\n protected _prevSelectedRow?: number;\n protected _prevKeyDown = '';\n protected _ranges: SlickRange_[] = [];\n protected _selector: SlickCellRangeSelector_;\n protected _options?: CellSelectionModelOption;\n protected _defaults: CellSelectionModelOption = {\n selectActiveCell: true\n };\n\n constructor(options?: { selectActiveCell: boolean; cellRangeSelector: SlickCellRangeSelector_; }) {\n if (options === undefined || options.cellRangeSelector === undefined) {\n this._selector = new SlickCellRangeSelector({ selectionCss: { border: '2px solid black' } as CSSStyleDeclaration });\n } else {\n this._selector = options.cellRangeSelector;\n }\n }\n\n init(grid: SlickGrid) {\n this._options = Utils.extend(true, {}, this._defaults, this._options);\n this._grid = grid;\n if (grid.hasDataView()) {\n this._dataView = grid.getData();\n }\n this._grid.onActiveCellChanged.subscribe(this.handleActiveCellChange.bind(this));\n this._grid.onKeyDown.subscribe(this.handleKeyDown.bind(this));\n grid.registerPlugin(this._selector);\n this._selector.onCellRangeSelected.subscribe(this.handleCellRangeSelected.bind(this));\n this._selector.onBeforeCellRangeSelected.subscribe(this.handleBeforeCellRangeSelected.bind(this));\n }\n\n destroy() {\n this._grid.onActiveCellChanged.unsubscribe(this.handleActiveCellChange.bind(this));\n this._grid.onKeyDown.unsubscribe(this.handleKeyDown.bind(this));\n this._selector.onCellRangeSelected.unsubscribe(this.handleCellRangeSelected.bind(this));\n this._selector.onBeforeCellRangeSelected.unsubscribe(this.handleBeforeCellRangeSelected.bind(this));\n this._grid.unregisterPlugin(this._selector);\n this._selector?.destroy();\n }\n\n protected removeInvalidRanges(ranges: SlickRange_[]) {\n const result: SlickRange_[] = [];\n\n for (let i = 0; i < ranges.length; i++) {\n const r = ranges[i];\n if (this._grid.canCellBeSelected(r.fromRow, r.fromCell) && this._grid.canCellBeSelected(r.toRow, r.toCell)) {\n result.push(r);\n }\n }\n\n return result;\n }\n\n protected rangesAreEqual(range1: SlickRange_[], range2: SlickRange_[]) {\n let areDifferent = (range1.length !== range2.length);\n if (!areDifferent) {\n for (let i = 0; i < range1.length; i++) {\n if (\n range1[i].fromCell !== range2[i].fromCell\n || range1[i].fromRow !== range2[i].fromRow\n || range1[i].toCell !== range2[i].toCell\n || range1[i].toRow !== range2[i].toRow\n ) {\n areDifferent = true;\n break;\n }\n }\n }\n return !areDifferent;\n }\n\n /** Provide a way to force a recalculation of page row count (for example on grid resize) */\n resetPageRowCount() {\n this._cachedPageRowCount = 0;\n }\n\n setSelectedRanges(ranges: SlickRange_[], caller = 'SlickCellSelectionModel.setSelectedRanges') {\n // simple check for: empty selection didn't change, prevent firing onSelectedRangesChanged\n if ((!this._ranges || this._ranges.length === 0) && (!ranges || ranges.length === 0)) { return; }\n\n // if range has not changed, don't fire onSelectedRangesChanged\n const rangeHasChanged = !this.rangesAreEqual(this._ranges, ranges);\n\n this._ranges = this.removeInvalidRanges(ranges);\n if (rangeHasChanged) {\n // provide extra \"caller\" argument through SlickEventData event to avoid breaking the previous pubsub event structure\n // that only accepts an array of selected range `SlickRange[]`, the SlickEventData args will be merged and used later by `onSelectedRowsChanged`\n const eventData = new SlickEventData(new CustomEvent('click', { detail: { caller } }), this._ranges);\n this.onSelectedRangesChanged.notify(this._ranges, eventData);\n }\n }\n\n getSelectedRanges() {\n return this._ranges;\n }\n\n refreshSelections() {\n this.setSelectedRanges(this.getSelectedRanges());\n }\n\n protected handleBeforeCellRangeSelected(e: SlickEventData_): boolean | void {\n if (this._grid.getEditorLock().isActive()) {\n e.stopPropagation();\n return false;\n }\n }\n\n protected handleCellRangeSelected(_e: SlickEventData_, args: { range: SlickRange_; }) {\n this._grid.setActiveCell(args.range.fromRow, args.range.fromCell, false, false, true);\n this.setSelectedRanges([args.range]);\n }\n\n protected handleActiveCellChange(_e: SlickEventData_, args: OnActiveCellChangedEventArgs) {\n this._prevSelectedRow = undefined;\n const isCellDefined = Utils.isDefined(args.cell);\n const isRowDefined = Utils.isDefined(args.row);\n\n if (this._options?.selectActiveCell && isRowDefined && isCellDefined) {\n this.setSelectedRanges([new SlickRange(args.row, args.cell)]);\n } else if (!this._options?.selectActiveCell || (!isRowDefined && !isCellDefined)) {\n // clear the previous selection once the cell changes\n this.setSelectedRanges([]);\n }\n }\n\n protected isKeyAllowed(key: string) {\n return ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'PageDown', 'PageUp', 'Home', 'End'].some(k => k === key);\n }\n\n protected handleKeyDown(e: SlickEventData_) {\n let ranges: SlickRange_[], last: SlickRange_;\n const colLn = this._grid.getColumns().length;\n const active = this._grid.getActiveCell();\n let dataLn = 0;\n if (this._dataView && 'getPagingInfo' in this._dataView) {\n dataLn = this._dataView?.getPagingInfo().pageSize || this._dataView.getLength();\n } else {\n dataLn = this._grid.getDataLength();\n }\n\n if (active && (e.shiftKey || e.ctrlKey) && !e.altKey && this.isKeyAllowed(e.key as string)) {\n ranges = this.getSelectedRanges().slice();\n if (!ranges.length) {\n ranges.push(new SlickRange(active.row, active.cell));\n }\n // keyboard can work with last range only\n last = ranges.pop() as SlickRange_;\n\n // can't handle selection out of active cell\n if (!last.contains(active.row, active.cell)) {\n last = new SlickRange(active.row, active.cell);\n }\n\n let dRow = last.toRow - last.fromRow;\n let dCell = last.toCell - last.fromCell;\n\n // walking direction\n const dirRow = active.row === last.fromRow ? 1 : -1;\n const dirCell = active.cell === last.fromCell ? 1 : -1;\n const isSingleKeyMove = e.key!.startsWith('Arrow');\n let toCell: undefined | number = undefined;\n let toRow = 0;\n\n if (isSingleKeyMove && !e.ctrlKey) {\n // single cell move: (Arrow{Up/ArrowDown/ArrowLeft/ArrowRight})\n if (e.key === 'ArrowLeft') {\n dCell -= dirCell;\n } else if (e.key === 'ArrowRight') {\n dCell += dirCell;\n } else if (e.key === 'ArrowUp') {\n dRow -= dirRow;\n } else if (e.key === 'ArrowDown') {\n dRow += dirRow;\n }\n toRow = active.row + dirRow * dRow;\n } else {\n // multiple cell moves: (Home, End, Page{Up/Down})\n if (this._cachedPageRowCount < 1) {\n this._cachedPageRowCount = this._grid.getViewportRowCount();\n }\n if (this._prevSelectedRow === undefined) {\n this._prevSelectedRow = active.row;\n }\n\n if (e.shiftKey && !e.ctrlKey && e.key === 'Home') {\n toCell = 0;\n toRow = active.row;\n } else if (e.shiftKey && !e.ctrlKey && e.key === 'End') {\n toCell = colLn - 1;\n toRow = active.row;\n } else if (e.ctrlKey && e.shiftKey && e.key === 'Home') {\n toCell = 0;\n toRow = 0;\n } else if (e.ctrlKey && e.shiftKey && e.key === 'End') {\n toCell = colLn - 1;\n toRow = dataLn - 1;\n } else if (e.key === 'PageUp') {\n if (this._prevSelectedRow >= 0) {\n toRow = this._prevSelectedRow - this._cachedPageRowCount;\n }\n if (toRow < 0) {\n toRow = 0;\n }\n } else if (e.key === 'PageDown') {\n if (this._prevSelectedRow <= dataLn - 1) {\n toRow = this._prevSelectedRow + this._cachedPageRowCount;\n }\n if (toRow > dataLn - 1) {\n toRow = dataLn - 1;\n }\n }\n this._prevSelectedRow = toRow;\n }\n\n // define new selection range\n toCell ??= active.cell + dirCell * dCell;\n const new_last = new SlickRange(active.row, active.cell, toRow, toCell);\n if (this.removeInvalidRanges([new_last]).length) {\n ranges.push(new_last);\n const viewRow = dirRow > 0 ? new_last.toRow : new_last.fromRow;\n const viewCell = dirCell > 0 ? new_last.toCell : new_last.fromCell;\n\n if (isSingleKeyMove) {\n this._grid.scrollRowIntoView(viewRow);\n this._grid.scrollCellIntoView(viewRow, viewCell);\n } else {\n this._grid.scrollRowIntoView(toRow);\n this._grid.scrollCellIntoView(toRow, viewCell);\n }\n } else {\n ranges.push(last);\n }\n\n this.setSelectedRanges(ranges);\n\n e.preventDefault();\n e.stopPropagation();\n this._prevKeyDown = e.key as string;\n }\n }\n}\n\n// extend Slick namespace on window object when building as iife\nif (IIFE_ONLY && window.Slick) {\n Utils.extend(true, window, {\n Slick: {\n CellSelectionModel: SlickCellSelectionModel\n }\n });\n}\n"], + "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) {\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 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);\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.checkboxselectcolumn.js.map b/dist/browser/plugins/slick.checkboxselectcolumn.js.map index 1e832bba..1cc09789 100644 --- a/dist/browser/plugins/slick.checkboxselectcolumn.js.map +++ b/dist/browser/plugins/slick.checkboxselectcolumn.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../../../src/plugins/slick.checkboxselectcolumn.ts"], - "sourcesContent": ["import type { CheckboxSelectorOption, Column, DOMEvent, SlickPlugin, SelectableOverrideCallback, OnHeaderClickEventArgs } from '../models/index';\nimport { BindingEventService as BindingEventService_, type SlickEventData, SlickEventHandler as SlickEventHandler_, Utils as Utils_ } from '../slick.core';\nimport type { SlickDataView } from '../slick.dataview';\nimport type { SlickGrid } from '../slick.grid';\n\n// for (iife) load Slick methods from global Slick object, or use imports for (esm)\nconst BindingEventService = IIFE_ONLY ? Slick.BindingEventService : BindingEventService_;\nconst SlickEventHandler = IIFE_ONLY ? Slick.EventHandler : SlickEventHandler_;\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\n\nexport class SlickCheckboxSelectColumn implements SlickPlugin {\n // --\n // public API\n pluginName = 'CheckboxSelectColumn' as const;\n\n // --\n // protected props\n protected _dataView!: SlickDataView;\n protected _grid!: SlickGrid;\n protected _isUsingDataView = false;\n protected _selectableOverride: SelectableOverrideCallback | null = null;\n protected _headerRowNode?: HTMLElement;\n protected _selectAll_UID: number;\n protected _handler = new SlickEventHandler();\n protected _selectedRowsLookup: any = {};\n protected _checkboxColumnCellIndex: number | null = null;\n protected _options: CheckboxSelectorOption;\n protected _defaults: CheckboxSelectorOption = {\n columnId: '_checkbox_selector',\n cssClass: undefined,\n hideSelectAllCheckbox: false,\n name: '',\n toolTip: 'Select/Deselect All',\n width: 30,\n reorderable: false,\n applySelectOnAllPages: false, // defaults to false, when that is enabled the \"Select All\" will be applied to all pages (when using Pagination)\n hideInColumnTitleRow: false,\n hideInFilterHeaderRow: true\n };\n protected _isSelectAllChecked = false;\n protected _bindingEventService: BindingEventService_;\n\n constructor(options?: Partial) {\n this._bindingEventService = new BindingEventService();\n this._options = Utils.extend(true, {}, this._defaults, options);\n this._selectAll_UID = this.createUID();\n\n // user could override the checkbox icon logic from within the options or after instantiating the plugin\n if (typeof this._options.selectableOverride === 'function') {\n this.selectableOverride(this._options.selectableOverride);\n }\n }\n\n init(grid: SlickGrid) {\n this._grid = grid;\n this._isUsingDataView = !Array.isArray(grid.getData());\n if (this._isUsingDataView) {\n this._dataView = grid.getData();\n }\n this._handler\n .subscribe(this._grid.onSelectedRowsChanged, this.handleSelectedRowsChanged.bind(this))\n .subscribe(this._grid.onClick, this.handleClick.bind(this))\n .subscribe(this._grid.onKeyDown, this.handleKeyDown.bind(this));\n\n if (this._isUsingDataView && this._dataView && this._options.applySelectOnAllPages) {\n this._handler\n .subscribe(this._dataView.onSelectedRowIdsChanged, this.handleDataViewSelectedIdsChanged.bind(this))\n .subscribe(this._dataView.onPagingInfoChanged, this.handleDataViewSelectedIdsChanged.bind(this));\n }\n\n if (!this._options.hideInFilterHeaderRow) {\n this.addCheckboxToFilterHeaderRow(grid);\n }\n if (!this._options.hideInColumnTitleRow) {\n this._handler.subscribe(this._grid.onHeaderClick, this.handleHeaderClick.bind(this));\n }\n }\n\n destroy() {\n this._handler.unsubscribeAll();\n this._bindingEventService.unbindAll();\n }\n\n getOptions() {\n return this._options;\n }\n\n setOptions(options: Partial) {\n this._options = Utils.extend(true, {}, this._options, options);\n\n if (this._options.hideSelectAllCheckbox) {\n this.hideSelectAllFromColumnHeaderTitleRow();\n this.hideSelectAllFromColumnHeaderFilterRow();\n } else {\n if (!this._options.hideInColumnTitleRow) {\n this.renderSelectAllCheckbox(this._isSelectAllChecked);\n this._handler.subscribe(this._grid.onHeaderClick, this.handleHeaderClick.bind(this));\n } else {\n this.hideSelectAllFromColumnHeaderTitleRow();\n if (this._options.name) {\n this._grid.updateColumnHeader(this._options.columnId || '', this._options.name, '');\n }\n }\n\n if (!this._options.hideInFilterHeaderRow) {\n const selectAllContainerElm = this._headerRowNode?.querySelector('#filter-checkbox-selectall-container');\n if (selectAllContainerElm) {\n selectAllContainerElm.style.display = 'flex';\n const selectAllInputElm = selectAllContainerElm.querySelector('input[type=\"checkbox\"]');\n if (selectAllInputElm) {\n selectAllInputElm.checked = this._isSelectAllChecked;\n }\n }\n } else {\n this.hideSelectAllFromColumnHeaderFilterRow();\n }\n }\n }\n\n protected hideSelectAllFromColumnHeaderTitleRow() {\n this._grid.updateColumnHeader(this._options.columnId || '', this._options.name || '', '');\n }\n\n protected hideSelectAllFromColumnHeaderFilterRow() {\n const selectAllContainerElm = this._headerRowNode?.querySelector('#filter-checkbox-selectall-container');\n if (selectAllContainerElm) {\n selectAllContainerElm.style.display = 'none';\n }\n }\n\n protected handleSelectedRowsChanged() {\n const selectedRows = this._grid.getSelectedRows();\n const lookup: any = {};\n let row = 0, i = 0, k = 0;\n let disabledCount = 0;\n if (typeof this._selectableOverride === 'function') {\n for (k = 0; k < this._grid.getDataLength(); k++) {\n // If we are allowed to select the row\n const dataItem = this._grid.getDataItem(k);\n if (!this.checkSelectableOverride(i, dataItem, this._grid)) {\n disabledCount++;\n }\n }\n }\n\n const removeList: number[] = [];\n for (i = 0; i < selectedRows.length; i++) {\n row = selectedRows[i];\n\n // If we are allowed to select the row\n const rowItem = this._grid.getDataItem(row);\n if (this.checkSelectableOverride(i, rowItem, this._grid)) {\n lookup[row] = true;\n if (lookup[row] !== this._selectedRowsLookup[row]) {\n this._grid.invalidateRow(row);\n delete this._selectedRowsLookup[row];\n }\n }\n else {\n removeList.push(row);\n }\n }\n if (typeof this._selectedRowsLookup === 'object') {\n Object.keys(this._selectedRowsLookup).forEach(selectedRow => {\n if (selectedRow !== undefined) {\n this._grid.invalidateRow(+selectedRow);\n }\n });\n }\n this._selectedRowsLookup = lookup;\n this._grid.render();\n this._isSelectAllChecked = (selectedRows?.length ?? 0) + disabledCount >= this._grid.getDataLength();\n\n if (!this._isUsingDataView || !this._options.applySelectOnAllPages) {\n if (!this._options.hideInColumnTitleRow && !this._options.hideSelectAllCheckbox) {\n this.renderSelectAllCheckbox(this._isSelectAllChecked);\n }\n if (!this._options.hideInFilterHeaderRow) {\n const selectAllElm = this._headerRowNode?.querySelector(`#header-filter-selector${this._selectAll_UID}`);\n if (selectAllElm) {\n selectAllElm.checked = this._isSelectAllChecked;\n }\n }\n }\n\n // Remove items that shouln't of been selected in the first place (Got here Ctrl + click)\n if (removeList.length > 0) {\n for (i = 0; i < removeList.length; i++) {\n const remIdx = selectedRows.indexOf(removeList[i]);\n selectedRows.splice(remIdx, 1);\n }\n this._grid.setSelectedRows(selectedRows, 'click.cleanup');\n }\n }\n\n protected handleDataViewSelectedIdsChanged() {\n const selectedIds = this._dataView.getAllSelectedFilteredIds();\n const filteredItems = this._dataView.getFilteredItems();\n let disabledCount = 0;\n\n if (typeof this._selectableOverride === 'function' && selectedIds.length > 0) {\n for (let k = 0; k < this._dataView.getItemCount(); k++) {\n // If we are allowed to select the row\n const dataItem: T = this._dataView.getItemByIdx(k);\n const idProperty = this._dataView.getIdPropertyName();\n const dataItemId = dataItem[idProperty as keyof T];\n const foundItemIdx = filteredItems.findIndex(function (item) {\n return item[idProperty as keyof T] === dataItemId;\n });\n if (foundItemIdx >= 0 && !this.checkSelectableOverride(k, dataItem, this._grid)) {\n disabledCount++;\n }\n }\n }\n this._isSelectAllChecked = (selectedIds && selectedIds.length) + disabledCount >= filteredItems.length;\n\n if (!this._options.hideInColumnTitleRow && !this._options.hideSelectAllCheckbox) {\n this.renderSelectAllCheckbox(this._isSelectAllChecked);\n }\n if (!this._options.hideInFilterHeaderRow) {\n const selectAllElm = this._headerRowNode?.querySelector(`#header-filter-selector${this._selectAll_UID}`);\n if (selectAllElm) {\n selectAllElm.checked = this._isSelectAllChecked;\n }\n }\n }\n\n protected handleKeyDown(e: SlickEventData, args: any) {\n if (e.which === 32) {\n if (this._grid.getColumns()[args.cell].id === this._options.columnId) {\n // if editing, try to commit\n if (!this._grid.getEditorLock().isActive() || this._grid.getEditorLock().commitCurrentEdit()) {\n this.toggleRowSelection(args.row);\n }\n e.preventDefault();\n e.stopImmediatePropagation();\n }\n }\n }\n\n protected handleClick(e: SlickEventData, args: { row: number; cell: number; }) {\n // clicking on a row select checkbox\n if (this._grid.getColumns()[args.cell].id === this._options.columnId && (e.target as HTMLInputElement).type === 'checkbox') {\n // if editing, try to commit\n if (this._grid.getEditorLock().isActive() && !this._grid.getEditorLock().commitCurrentEdit()) {\n e.preventDefault();\n e.stopImmediatePropagation();\n return;\n }\n\n this.toggleRowSelection(args.row);\n e.stopPropagation();\n e.stopImmediatePropagation();\n }\n }\n\n protected toggleRowSelection(row: number) {\n const dataContext = this._grid.getDataItem(row);\n if (!this.checkSelectableOverride(row, dataContext, this._grid)) {\n return;\n }\n\n if (this._selectedRowsLookup[row]) {\n const newSelectedRows = this._grid.getSelectedRows().filter((n) => n !== row);\n this._grid.setSelectedRows(newSelectedRows, 'click.toggle');\n } else {\n this._grid.setSelectedRows(this._grid.getSelectedRows().concat(row), 'click.toggle');\n }\n this._grid.setActiveCell(row, this.getCheckboxColumnCellIndex());\n }\n\n selectRows(rowArray: number[]) {\n const addRows: number[] = [];\n for (let i = 0, l = rowArray.length; i < l; i++) {\n if (!this._selectedRowsLookup[rowArray[i]]) {\n addRows[addRows.length] = rowArray[i];\n }\n }\n this._grid.setSelectedRows(this._grid.getSelectedRows().concat(addRows), 'SlickCheckboxSelectColumn.selectRows');\n }\n\n deSelectRows(rowArray: number[]) {\n const removeRows: number[] = [];\n for (let i = 0, l = rowArray.length; i < l; i++) {\n if (this._selectedRowsLookup[rowArray[i]]) {\n removeRows[removeRows.length] = rowArray[i];\n }\n }\n\n this._grid.setSelectedRows(this._grid.getSelectedRows().filter((n) => removeRows.indexOf(n) < 0), 'SlickCheckboxSelectColumn.deSelectRows');\n }\n\n protected handleHeaderClick(e: DOMEvent | SlickEventData, args: OnHeaderClickEventArgs) {\n if (args.column.id === this._options.columnId && (e.target as HTMLInputElement).type === 'checkbox') {\n // if editing, try to commit\n if (this._grid.getEditorLock().isActive() && !this._grid.getEditorLock().commitCurrentEdit()) {\n e.preventDefault();\n e.stopImmediatePropagation();\n return;\n }\n\n let isAllSelected = (e.target as HTMLInputElement).checked;\n const caller = isAllSelected ? 'click.selectAll' : 'click.unselectAll';\n const rows: number[] = [];\n\n if (isAllSelected) {\n for (let i = 0; i < this._grid.getDataLength(); i++) {\n // Get the row and check it's a selectable row before pushing it onto the stack\n const rowItem = this._grid.getDataItem(i);\n if (!rowItem.__group && !rowItem.__groupTotals && this.checkSelectableOverride(i, rowItem, this._grid)) {\n rows.push(i);\n }\n }\n isAllSelected = true;\n }\n if (this._isUsingDataView && this._dataView && this._options.applySelectOnAllPages) {\n const ids: Array = [];\n const filteredItems = this._dataView.getFilteredItems();\n for (let j = 0; j < filteredItems.length; j++) {\n // Get the row and check it's a selectable ID (it could be in a different page) before pushing it onto the stack\n const dataviewRowItem: T = filteredItems[j];\n if (this.checkSelectableOverride(j, dataviewRowItem, this._grid)) {\n ids.push(dataviewRowItem[this._dataView.getIdPropertyName() as keyof T] as number | string);\n }\n }\n this._dataView.setSelectedIds(ids, { isRowBeingAdded: isAllSelected });\n }\n this._grid.setSelectedRows(rows, caller);\n e.stopPropagation();\n e.stopImmediatePropagation();\n }\n }\n\n protected getCheckboxColumnCellIndex() {\n if (this._checkboxColumnCellIndex === null) {\n this._checkboxColumnCellIndex = 0;\n const colArr = this._grid.getColumns();\n for (let i = 0; i < colArr.length; i++) {\n if (colArr[i].id === this._options.columnId) {\n this._checkboxColumnCellIndex = i;\n }\n }\n }\n return this._checkboxColumnCellIndex;\n }\n\n /**\n * use a DocumentFragment to return a fragment including an then a