From 0037c7d831314706cc238340406e250f2d26ee64 Mon Sep 17 00:00:00 2001 From: Ghislain B Date: Mon, 17 Jul 2023 23:30:16 -0400 Subject: [PATCH] feat: migrate Draggable Grouping Plugins to TypeScript (#814) --- examples/example-draggable-grouping.html | 2 +- src/global.d.ts | 2 + src/models/column.interface.ts | 5 +- .../draggableGroupingOption.interface.ts | 49 ++ src/models/index.ts | 1 + src/plugins/slick.autotooltips.ts | 2 +- src/plugins/slick.cellcopymanager.ts | 6 +- src/plugins/slick.draggablegrouping.js | 469 ----------------- src/plugins/slick.draggablegrouping.ts | 493 ++++++++++++++++++ src/slick.compositeeditor.ts | 2 +- src/slick.core.ts | 24 +- src/slick.dataview.ts | 12 +- src/slick.editors.ts | 4 +- src/slick.formatters.ts | 2 +- src/slick.grid.ts | 40 +- src/slick.groupitemmetadataprovider.ts | 6 +- src/slick.interactions.ts | 2 +- src/styles/slick-alpine-theme.scss | 2 - 18 files changed, 601 insertions(+), 522 deletions(-) create mode 100644 src/models/draggableGroupingOption.interface.ts delete mode 100644 src/plugins/slick.draggablegrouping.js create mode 100644 src/plugins/slick.draggablegrouping.ts diff --git a/examples/example-draggable-grouping.html b/examples/example-draggable-grouping.html index f9acb8f4..7a0f154c 100644 --- a/examples/example-draggable-grouping.html +++ b/examples/example-draggable-grouping.html @@ -409,7 +409,7 @@

View Source:

var options = { enableCellNavigation: true, editable: true, - enableColumnReorder: draggableGrouping.getSetupColumnReorder, + enableColumnReorder: draggableGrouping.getSetupColumnReorder.bind(draggableGrouping), createPreHeaderPanel: true, showPreHeaderPanel: true, preHeaderPanelHeight: 28, diff --git a/src/global.d.ts b/src/global.d.ts index dac5788f..8abfc336 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -33,6 +33,7 @@ import type { SlickCellExternalCopyManager } from './plugins/slick.cellexternalc import type { SlickCellRangeDecorator } from './plugins/slick.cellrangedecorator'; import type { SlickCellRangeSelector } from './plugins/slick.cellrangeselector'; import type { SlickCellSelectionModel } from './plugins/slick.cellselectionmodel'; +import type { SlickDraggableGrouping } from './plugins/slick.draggablegrouping'; import type { SlickRowSelectionModel } from './plugins/slick.rowselectionmodel'; import type { SlickState } from './plugins/slick.state'; import type { SlickGroupItemMetadataProvider } from './slick.groupitemmetadataprovider'; @@ -68,6 +69,7 @@ declare global { DataView: typeof SlickDataView, GroupItemMetadataProvider: typeof SlickGroupItemMetadataProvider }, + DraggableGrouping: typeof SlickDraggableGrouping, Editors: typeof Editors, Event: typeof SlickEvent, EventData: typeof SlickEventData, diff --git a/src/models/column.interface.ts b/src/models/column.interface.ts index fe15af21..e718d26b 100644 --- a/src/models/column.interface.ts +++ b/src/models/column.interface.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/indent */ -import type { CellMenuOption, CustomTooltipOption, Editor, EditorValidator, Formatter, FormatterResultObject, GroupTotalsFormatter, HeaderButtonsOrMenu } from './index'; +import type { CellMenuOption, CustomTooltipOption, Editor, EditorValidator, Formatter, FormatterResultObject, GroupTotalsFormatter, Grouping, HeaderButtonsOrMenu } from './index'; import type { SlickGrid } from '../slick.grid'; type PathsToStringProps = T extends string | number | boolean | Date ? [] : { @@ -130,6 +130,9 @@ export interface Column { /** Formatter override function */ formatterOverride?: { ReturnsTextOnly: boolean; } | FormatterOverrideCallback; + /** Grouping option used by a Draggable Grouping Column */ + grouping?: Grouping; + /** Group Totals Formatter function that can be used to add grouping totals in the grid */ groupTotalsFormatter?: GroupTotalsFormatter; diff --git a/src/models/draggableGroupingOption.interface.ts b/src/models/draggableGroupingOption.interface.ts new file mode 100644 index 00000000..00b91a65 --- /dev/null +++ b/src/models/draggableGroupingOption.interface.ts @@ -0,0 +1,49 @@ +import type { ColumnReorderFunction, GroupingGetterFunction } from './index'; + +export interface DraggableGroupingOption { + /** an extra CSS class to add to the delete button (default undefined), if deleteIconCssClass is undefined then slick-groupby-remove-icon class will be added */ + deleteIconCssClass?: string; + + /** a url to the delete button image (default undefined) */ + deleteIconImage?: string; + + /** option to specify set own placeholder note text */ + dropPlaceHolderText?: string; + + /** an extra CSS class to add to the grouping field hint (default undefined) */ + groupIconCssClass?: string; + + /** a url to the grouping field hint image (default undefined) */ + groupIconImage?: string; + + /** Defaults to False, should we display a toggle all button (typically aligned on the left before any of the column group) */ + hideToggleAllButton?: boolean; + + /** Defaults to False, should we show the Sorting icons on each group by element? */ + hideGroupSortIcons?: boolean; + + /** an extra CSS class to add to the sort ascending icon (default undefined), if sortAscIconCssClass is undefined then slick-groupby-sort-asc-icon class will be added */ + sortAscIconCssClass?: string; + + /** an extra CSS class to add to the sort descending icon (default undefined), if sortDescIconCssClass is undefined then slick-groupby-sort-desc-icon class will be added */ + sortDescIconCssClass?: string; + + /** Defaults to "Toggle all Groups", placeholder of the Toggle All button that can optionally show up in the pre-header row. */ + toggleAllPlaceholderText?: string; + + /** Defaults to empty string, text to show in the Toggle All button that can optionally show up in the pre-header row. */ + toggleAllButtonText?: string; + + // + // Methods + // --------- + + /** provide option to clear grouping */ + clearDroppedGroups?: () => void; + + /** its function to setup draggable feature agains Header Column, should be passed on grid option. Also possible to pass custom function */ + getSetupColumnReorder?: ColumnReorderFunction; + + /** provide option to set default grouping on loading */ + setDroppedGroups?: (groupingInfo: Array | string) => void; +} diff --git a/src/models/index.ts b/src/models/index.ts index c29a53fe..ea13ba30 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -14,6 +14,7 @@ export * from './customTooltipOption.interface'; export * from './dataViewEvents.interface'; export * from './domEvent.interface'; export * from './drag.interface'; +export * from './draggableGroupingOption.interface'; export * from './editCommand.interface'; export * from './editor.interface'; export * from './editorArguments.interface'; diff --git a/src/plugins/slick.autotooltips.ts b/src/plugins/slick.autotooltips.ts index 5329cdc3..30bd15c6 100644 --- a/src/plugins/slick.autotooltips.ts +++ b/src/plugins/slick.autotooltips.ts @@ -3,7 +3,7 @@ import { Utils as Utils_ } from '../slick.core'; import type { SlickGrid } from '../slick.grid'; // for (iife) load Slick methods from global Slick object, or use imports for (cjs/esm) -const Utils = (IIFE_ONLY ? Slick.Utils : Utils_) as typeof Utils_; +const Utils = IIFE_ONLY ? Slick.Utils : Utils_; /** * AutoTooltips plugin to show/hide tooltips when columns are too narrow to fit content. diff --git a/src/plugins/slick.cellcopymanager.ts b/src/plugins/slick.cellcopymanager.ts index 04c9cf75..d924dca9 100644 --- a/src/plugins/slick.cellcopymanager.ts +++ b/src/plugins/slick.cellcopymanager.ts @@ -3,9 +3,9 @@ import { SlickEvent as SlickEvent_, keyCode as keyCode_, Utils as Utils_, SlickR import type { SlickGrid } from '../slick.grid'; // for (iife) load Slick methods from global Slick object, or use imports for (cjs/esm) -const keyCode = (IIFE_ONLY ? Slick.keyCode : keyCode_); -const SlickEvent = (IIFE_ONLY ? Slick.Event : SlickEvent_); -const Utils = (IIFE_ONLY ? Slick.Utils : Utils_) as typeof Utils_; +const keyCode = IIFE_ONLY ? Slick.keyCode : keyCode_; +const SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_; +const Utils = IIFE_ONLY ? Slick.Utils : Utils_; /** * This manager enables users to copy/paste cell data diff --git a/src/plugins/slick.draggablegrouping.js b/src/plugins/slick.draggablegrouping.js deleted file mode 100644 index 0a63345f..00000000 --- a/src/plugins/slick.draggablegrouping.js +++ /dev/null @@ -1,469 +0,0 @@ -import { BindingEventService as BindingEventService_, Event as SlickEvent_, EventHandler as EventHandler_, Utils as Utils_ } from '../slick.core'; - -// for (iife) load Slick methods from global Slick object, or use imports for (cjs/esm) -const BindingEventService = IIFE_ONLY ? Slick.BindingEventService : BindingEventService_; -const SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_; -const EventHandler = IIFE_ONLY ? Slick.EventHandler : EventHandler_; -const Utils = IIFE_ONLY ? Slick.Utils : Utils_; - -/** - * - * Draggable Grouping contributed by: Muthukumar Selvarasu - * muthukumar{dot}se{at}gmail{dot}com - * github.com/muthukumarse/Slickgrid - * - * NOTES: - * This plugin provides the Draggable Grouping feature - * A plugin to add Draggable Grouping feature. - * - * USAGE: - * - * Add the plugin .js & .css files and register it with the grid. - * - * - * The plugin expose the following methods: - * destroy: used to destroy the plugin - * setDroppedGroups: provide option to set default grouping on loading - * clearDroppedGroups: provide option to clear grouping - * getSetupColumnReorder: its function to setup draggable feature agains Header Column, should be passed on grid option. Also possible to pass custom function - * - * - * The plugin expose the following event(s): - * onGroupChanged: pass the grouped columns to who subscribed. - * - * @param options {Object} Options: - * deleteIconCssClass: an extra CSS class to add to the delete button (default undefined), if deleteIconCssClass && deleteIconImage undefined then slick-groupby-remove-image class will be added - * deleteIconImage: a url to the delete button image (default undefined) - * groupIconCssClass: an extra CSS class to add to the grouping field hint (default undefined) - * groupIconImage: a url to the grouping field hint image (default undefined) - * dropPlaceHolderText: option to specify set own placeholder note text - * - */ - -export function DraggableGrouping(options) { - var _grid; - var _gridUid; - var _gridColumns; - var _dataView; - var _dropzoneElm; - var _droppableInstance; - var dropzonePlaceholder; - var groupToggler; - var _defaults = { - }; - var onGroupChanged = new SlickEvent(); - var _bindingEventService = new BindingEventService(); - var _handler = new EventHandler(); - var _sortableLeftInstance; - var _sortableRightInstance; - - /** - * Initialize plugin. - */ - function init(grid) { - options = Utils.extend(true, {}, _defaults, options); - _grid = grid; - _gridUid = _grid.getUID(); - _gridColumns = _grid.getColumns(); - _dataView = _grid.getData(); - _dropzoneElm = grid.getPreHeaderPanel(); - _dropzoneElm.classList.add('slick-dropzone'); - - var dropPlaceHolderText = options.dropPlaceHolderText || 'Drop a column header here to group by the column'; - - dropzonePlaceholder = document.createElement('div') - dropzonePlaceholder.className = 'slick-placeholder'; - dropzonePlaceholder.textContent = dropPlaceHolderText; - - groupToggler = document.createElement('div'); - groupToggler.className = 'slick-group-toggle-all expanded'; - groupToggler.style.display = 'none'; - - _dropzoneElm.appendChild(dropzonePlaceholder); - _dropzoneElm.appendChild(groupToggler); - - setupColumnDropbox(); - - - _handler.subscribe(_grid.onHeaderCellRendered, function (e, args) { - var column = args.column; - var node = args.node; - if (!Utils.isEmptyObject(column.grouping) && node) { - node.style.cursor = 'pointer'; // add the pointer cursor on each column title - - // also optionally add an icon beside each column title that can be dragged - if (options.groupIconCssClass || options.groupIconImage) { - const groupableIconElm = document.createElement('span'); - groupableIconElm.className = 'slick-column-groupable'; - if (options.groupIconCssClass) { - groupableIconElm.classList.add(...options.groupIconCssClass.split(' ')); - } - if (options.groupIconImage) { - groupableIconElm.style.background = "url(" + options.groupIconImage + ") no-repeat center center"; - } - node.appendChild(groupableIconElm); - } - } - }); - - for (var i = 0; i < _gridColumns.length; i++) { - var columnId = _gridColumns[i].field; - _grid.updateColumnHeader(columnId); - } - - } - - function setupColumnReorder(grid, headers, _headerColumnWidthDiff, setColumns, setupColumnResize, _columns, getColumnIndex, _uid, trigger) { - const dropzoneElm = grid.getPreHeaderPanel(); - - var sortableOptions = { - animation: 50, - // chosenClass: 'slick-header-column-active', - ghostClass: "slick-sortable-placeholder", - draggable: '.slick-header-column', - dataIdAttr: 'data-id', - group: { - name: 'shared', - pull: 'clone', - put: false, - }, - revertClone: true, - // filter: function (_e, target) { - // // block column from being able to be dragged if it's already a grouped column - // // NOTE: need to disable for now since it also blocks the column reordering - // return columnsGroupBy.some(c => c.id === target.getAttribute('data-id')); - // }, - onStart: function () { - dropzoneElm.classList.add('slick-dropzone-hover'); - dropzoneElm.classList.add('slick-dropzone-placeholder-hover'); - const draggablePlaceholderElm = dropzoneElm.querySelector('.slick-placeholder'); - - draggablePlaceholderElm.style.display = 'inline-block'; - groupToggler.style.display = 'none'; - - const droppedGroupingElms = dropzoneElm.querySelectorAll('.slick-dropped-grouping'); - droppedGroupingElms.forEach(droppedGroupingElm => droppedGroupingElm.style.display = 'none'); - }, - onEnd: function (e) { - const draggablePlaceholderElm = dropzoneElm.querySelector('.slick-placeholder'); - dropzoneElm.classList.remove('slick-dropzone-hover'); - draggablePlaceholderElm.classList.remove('slick-dropzone-placeholder-hover'); - - - if (dropzonePlaceholder) { - dropzonePlaceholder.style.display = 'none'; - } - if (draggablePlaceholderElm) { - draggablePlaceholderElm.parentElement && draggablePlaceholderElm.parentElement.classList.remove('slick-dropzone-placeholder-hover'); - } - - const droppedGroupingElms = dropzoneElm.querySelectorAll('.slick-dropped-grouping'); - droppedGroupingElms.forEach(droppedGroupingElm => droppedGroupingElm.style.display = 'inline-flex'); - - if (droppedGroupingElms.length) { - if (draggablePlaceholderElm) { - draggablePlaceholderElm.style.display = 'none'; - } - groupToggler.style.display = 'inline-block'; - } - - if (!grid.getEditorLock().commitCurrentEdit()) { - return; - } - - const reorderedIds = _sortableLeftInstance && _sortableLeftInstance.toArray() || []; - - // when frozen columns are used, headers has more than one entry and we need the ids from all of them. - // though there is only really a left and right header, this will work even if that should change. - if (headers.length > 1) { - const ids = _sortableRightInstance && _sortableRightInstance.toArray() || []; - - // Note: the loop below could be simplified with: - // reorderedIds.push.apply(reorderedIds,ids); - // However, the loop is more in keeping with way-backward compatibility - for (const id of ids) { - reorderedIds.push(id); - } - } - - const finalReorderedColumns = []; - const reorderedColumns = grid.getColumns(); - for (const reorderedId of reorderedIds) { - finalReorderedColumns.push(reorderedColumns[getColumnIndex(reorderedId)]); - } - setColumns(finalReorderedColumns); - trigger(grid.onColumnsReordered, { grid }); - e.stopPropagation(); - setupColumnResize(); - } - } - - _sortableLeftInstance = Sortable.create(document.querySelector(`.${grid.getUID()} .slick-header-columns.slick-header-columns-left`), sortableOptions); - _sortableRightInstance = Sortable.create(document.querySelector(`.${grid.getUID()} .slick-header-columns.slick-header-columns-right`), sortableOptions); - - return { - sortableLeftInstance: _sortableLeftInstance, - sortableRightInstance: _sortableRightInstance - }; - } - - /** - * Destroy plugin. - */ - function destroy() { - if (_sortableLeftInstance && _sortableLeftInstance.el) { - _sortableLeftInstance.destroy(); - } - if (_sortableRightInstance && _sortableRightInstance.el) { - _sortableRightInstance.destroy(); - } - onGroupChanged.unsubscribe(); - _handler.unsubscribeAll(); - _bindingEventService.unbindAll(); - Utils.emptyElement(document.querySelector(`.${_gridUid} .slick-preheader-panel`)); - } - - function addDragOverDropzoneListeners() { - const draggablePlaceholderElm = _dropzoneElm.querySelector('.slick-placeholder'); - - if (draggablePlaceholderElm) { - _bindingEventService.bind(draggablePlaceholderElm, 'dragover', (e) => e.preventDefault); - _bindingEventService.bind(draggablePlaceholderElm, 'dragenter', () => _dropzoneElm.classList.add('slick-dropzone-hover')); - _bindingEventService.bind(draggablePlaceholderElm, 'dragleave', () => _dropzoneElm.classList.remove('slick-dropzone-hover')); - } - } - - function setupColumnDropbox() { - const dropzoneElm = _dropzoneElm; - - _droppableInstance = Sortable.create(dropzoneElm, { - group: 'shared', - // chosenClass: 'slick-header-column-active', - ghostClass: 'slick-droppable-sortitem-hover', - draggable: '.slick-dropped-grouping', - dragoverBubble: true, - onAdd: (evt) => { - const el = evt.item; - const elId = el.getAttribute('id'); - if (elId && elId.replace(_gridUid, '')) { - handleGroupByDrop(dropzoneElm, (Sortable.utils).clone(evt.item)); - } - evt.clone.style.opacity = '.5'; - el.parentNode && el.parentNode.removeChild(el); - }, - onUpdate: () => { - const sortArray = _droppableInstance && _droppableInstance.toArray() || []; - let newGroupingOrder = []; - for (var i = 0, l = sortArray.length; i < l; i++) { - for (var a = 0, b = columnsGroupBy.length; a < b; a++) { - if (columnsGroupBy[a].id == sortArray[i]) { - newGroupingOrder.push(columnsGroupBy[a]); - break; - } - } - } - columnsGroupBy = newGroupingOrder; - updateGroupBy("sort-group"); - }, - }); - - // Sortable doesn't have onOver, we need to implement it ourselves - addDragOverDropzoneListeners(); - - if (groupToggler) { - _bindingEventService.bind(groupToggler, 'click', ((event) => { - const target = event.target; - toggleGroupToggler(target, target && target.classList.contains('expanded')); - })); - } - } - - - var columnsGroupBy = []; - - function handleGroupByDrop(containerElm, headerColumnElm) { - const headerColDataId = headerColumnElm.getAttribute('data-id'); - const columnId = headerColDataId && headerColDataId.replace(_gridUid, ''); - let columnAllowed = true; - for (const colGroupBy of columnsGroupBy) { - if (colGroupBy.id === columnId) { - columnAllowed = false; - } - } - - if (columnAllowed) { - for (const col of _gridColumns) { - if (col.id === columnId) { - if (col.grouping && !Utils.isEmptyObject(col.grouping)) { - const columnNameElm = headerColumnElm.querySelector('.slick-column-name'); - const entryElm = document.createElement('div'); - entryElm.id = `${_gridUid}_${col.id}_entry`; - entryElm.className = 'slick-dropped-grouping'; - entryElm.dataset.id = `${col.id}`; - - const groupTextElm = document.createElement('div'); - groupTextElm.className = 'slick-dropped-grouping-title'; - groupTextElm.style.display = 'inline-flex'; - groupTextElm.textContent = columnNameElm ? columnNameElm.textContent : headerColumnElm.textContent; - entryElm.appendChild(groupTextElm); - - // delete icon - const groupRemoveIconElm = document.createElement('div'); - groupRemoveIconElm.className = 'slick-groupby-remove'; - if (options.deleteIconCssClass) { - groupRemoveIconElm.classList.add(...options.deleteIconCssClass.split(' ')); - } - if (options.deleteIconImage) { - groupRemoveIconElm.classList.add(...options.deleteIconImage.split(' ')); - } - if (!options.deleteIconCssClass) { - groupRemoveIconElm.classList.add('slick-groupby-remove-icon'); - } - if (!options.deleteIconCssClass && !options.deleteIconImage) { - groupRemoveIconElm.classList.add('slick-groupby-remove-image'); - } - - // sorting icons when enabled - if (options && options.hideGroupSortIcons !== true && col.sortable) { - if (col.grouping && col.grouping.sortAsc === undefined) { - col.grouping.sortAsc = true; - } - } - - entryElm.appendChild(groupRemoveIconElm); - entryElm.appendChild(document.createElement('div')); - containerElm.appendChild(entryElm); - - addColumnGroupBy(col); - addGroupByRemoveClickHandler(col.id, groupRemoveIconElm, headerColumnElm, entryElm); - } - } - } - groupToggler.style.display = 'inline-block'; - } - } - - function addColumnGroupBy(column) { - columnsGroupBy.push(column); - updateGroupBy("add-group"); - } - - function addGroupByRemoveClickHandler(id, groupRemoveIconElm, headerColumnElm, entry) { - _bindingEventService.bind(groupRemoveIconElm, 'click', () => { - const boundedElms = _bindingEventService.boundedEvents.filter(boundedEvent => boundedEvent.element === groupRemoveIconElm); - for (const boundedEvent of boundedElms) { - _bindingEventService.unbind(boundedEvent.element, 'click', boundedEvent.listener); - } - removeGroupBy(id, headerColumnElm, entry); - }); - } - - function setDroppedGroups(groupingInfo) { - const groupingInfos = Array.isArray(groupingInfo) ? groupingInfo : [groupingInfo]; - dropzonePlaceholder.style.display = 'none'; - for (const groupInfo of groupingInfos) { - const columnElm = _grid.getHeaderColumn(groupInfo); - handleGroupByDrop(_dropzoneElm, columnElm); - } - } - - function clearDroppedGroups() { - columnsGroupBy = []; - updateGroupBy('clear-all'); - const allDroppedGroupingElms = _dropzoneElm.querySelectorAll('.slick-dropped-grouping'); - groupToggler.style.display = 'none'; - - for (const groupElm of Array.from(allDroppedGroupingElms)) { - const groupRemoveBtnElm = _dropzoneElm.querySelector('.slick-groupby-remove'); - groupRemoveBtnElm && groupRemoveBtnElm.remove(); - groupElm && groupElm.remove(); - } - - // show placeholder text & hide the "Toggle All" when that later feature is enabled - dropzonePlaceholder.style.display = 'inline-block'; - } - - function removeFromArray(arr) { - var what, a = arguments, - L = a.length, - ax; - while (L > 1 && arr.length) { - what = a[--L]; - while ((ax = arr.indexOf(what)) != -1) { - arr.splice(ax, 1); - } - } - return arr; - } - - function removeGroupBy(id, _column, entry) { - entry.remove(); - var groupby = []; - _gridColumns.forEach(function (e) { - groupby[e.id] = e; - }); - removeFromArray(columnsGroupBy, groupby[id]); - if (columnsGroupBy.length === 0) { - dropzonePlaceholder.style = 'block'; - groupToggler.style.display = 'none'; - } - updateGroupBy("remove-group"); - } - - function toggleGroupToggler(targetElm, collapsing = true, shouldExecuteDataViewCommand = true) { - if (targetElm) { - if (collapsing === true) { - targetElm.classList.add('collapsed'); - targetElm.classList.remove('expanded'); - if (shouldExecuteDataViewCommand) { - _dataView.collapseAllGroups(); - } - } else { - targetElm.classList.remove('collapsed'); - targetElm.classList.add('expanded'); - if (shouldExecuteDataViewCommand) { - _dataView.expandAllGroups(); - } - } - } - } - - function updateGroupBy(originator) { - if (columnsGroupBy.length === 0) { - _dataView.setGrouping([]); - onGroupChanged.notify({ caller: originator, groupColumns: [] }); - return; - } - var groupingArray = []; - columnsGroupBy.forEach(function (element) { - groupingArray.push(element.grouping); - }); - _dataView.setGrouping(groupingArray); - /* - collapseAllGroups(); - */ - onGroupChanged.notify({ caller: originator, groupColumns: groupingArray }); - } - - // Public API - Utils.extend(this, { - "init": init, - "destroy": destroy, - "pluginName": "DraggableGrouping", - - "onGroupChanged": onGroupChanged, - "setDroppedGroups": setDroppedGroups, - "clearDroppedGroups": clearDroppedGroups, - "getSetupColumnReorder": setupColumnReorder, - }); -} - -// extend Slick namespace on window object when building as iife -if (IIFE_ONLY && window.Slick) { - Utils.extend(true, window, { - Slick: { - DraggableGrouping - } - }); -} - diff --git a/src/plugins/slick.draggablegrouping.ts b/src/plugins/slick.draggablegrouping.ts new file mode 100644 index 00000000..6f3668d6 --- /dev/null +++ b/src/plugins/slick.draggablegrouping.ts @@ -0,0 +1,493 @@ +import SortableInstance from 'sortablejs'; + +import type { Column, DOMMouseOrTouchEvent, DraggableGroupingOption, Grouping, GroupingGetterFunction } from '../models/index'; +import { BindingEventService as BindingEventService_, SlickEvent as SlickEvent_, SlickEventHandler as SlickEventHandler_, Utils as Utils_ } from '../slick.core'; +import type { SlickDataView } from '../slick.dataview'; +import type { SlickGrid } from '../slick.grid'; + +// for (iife) load Slick methods from global Slick object, or use imports for (cjs/esm) +const BindingEventService = IIFE_ONLY ? Slick.BindingEventService : BindingEventService_; +const SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_; +const SlickEventHandler = IIFE_ONLY ? Slick.EventHandler : SlickEventHandler_; +const Utils = IIFE_ONLY ? Slick.Utils : Utils_; + +/** + * + * Draggable Grouping contributed by: Muthukumar Selvarasu + * muthukumar{dot}se{at}gmail{dot}com + * github.com/muthukumarse/Slickgrid + * + * NOTES: + * This plugin provides the Draggable Grouping feature + * A plugin to add Draggable Grouping feature. + * + * USAGE: + * + * Add the plugin .js & .css files and register it with the grid. + * + * + * The plugin expose the following methods: + * destroy: used to destroy the plugin + * setDroppedGroups: provide option to set default grouping on loading + * clearDroppedGroups: provide option to clear grouping + * getSetupColumnReorder: its function to setup draggable feature agains Header Column, should be passed on grid option. Also possible to pass custom function + * + * + * The plugin expose the following event(s): + * onGroupChanged: pass the grouped columns to who subscribed. + * + */ +export class SlickDraggableGrouping { + // -- + // public API + pluginName = 'DraggableGrouping' as const; + onGroupChanged = new SlickEvent<{ caller?: string; groupColumns: Grouping[]; }>(); + + // -- + // protected props + protected _grid!: SlickGrid; + protected _gridUid = ''; + protected _gridColumns: Column[] = []; + protected _dataView!: SlickDataView; + protected _dropzoneElm!: HTMLDivElement; + protected _droppableInstance?: SortableInstance; + protected _dropzonePlaceholder!: HTMLDivElement; + protected _groupToggler?: HTMLDivElement; + protected _options: DraggableGroupingOption; + protected _defaults: DraggableGroupingOption = { + dropPlaceHolderText: 'Drop a column header here to group by the column', + hideGroupSortIcons: false, + hideToggleAllButton: false, + toggleAllButtonText: '', + toggleAllPlaceholderText: 'Toggle all Groups', + }; + protected _bindingEventService = new BindingEventService(); + protected _handler = new SlickEventHandler(); + protected _sortableLeftInstance?: SortableInstance; + protected _sortableRightInstance?: SortableInstance; + protected _columnsGroupBy: Column[] = []; + + /** + * @param options {Object} Options: + * deleteIconCssClass: an extra CSS class to add to the delete button (default undefined), if deleteIconCssClass && deleteIconImage undefined then slick-groupby-remove-image class will be added + * deleteIconImage: a url to the delete button image (default undefined) + * groupIconCssClass: an extra CSS class to add to the grouping field hint (default undefined) + * groupIconImage: a url to the grouping field hint image (default undefined) + * dropPlaceHolderText: option to specify set own placeholder note text + */ + constructor(options: Partial) { + this._options = Utils.extend(true, {}, this._defaults, options); + } + + /** + * Initialize plugin. + */ + init(grid: SlickGrid) { + this._grid = grid; + this._gridUid = this._grid.getUID(); + this._gridColumns = this._grid.getColumns(); + this._dataView = this._grid.getData(); + this._dropzoneElm = this._grid.getPreHeaderPanel(); + this._dropzoneElm.classList.add('slick-dropzone'); + + const dropPlaceHolderText = this._options.dropPlaceHolderText || 'Drop a column header here to group by the column'; + + this._dropzonePlaceholder = document.createElement('div') + this._dropzonePlaceholder.className = 'slick-placeholder'; + this._dropzonePlaceholder.textContent = dropPlaceHolderText; + + this._groupToggler = document.createElement('div'); + this._groupToggler.className = 'slick-group-toggle-all expanded'; + this._groupToggler.style.display = 'none'; + + this._dropzoneElm.appendChild(this._dropzonePlaceholder); + this._dropzoneElm.appendChild(this._groupToggler); + + this.setupColumnDropbox(); + + + this._handler.subscribe(this._grid.onHeaderCellRendered, (e, args) => { + const column = args.column; + const node = args.node; + if (!Utils.isEmptyObject(column.grouping) && node) { + node.style.cursor = 'pointer'; // add the pointer cursor on each column title + + // also optionally add an icon beside each column title that can be dragged + if (this._options.groupIconCssClass || this._options.groupIconImage) { + const groupableIconElm = document.createElement('span'); + groupableIconElm.className = 'slick-column-groupable'; + if (this._options.groupIconCssClass) { + groupableIconElm.classList.add(...this._options.groupIconCssClass.split(' ')); + } + if (this._options.groupIconImage) { + groupableIconElm.style.background = `url(${this._options.groupIconImage}) no-repeat center center`; + } + node.appendChild(groupableIconElm); + } + } + }); + + for (let i = 0; i < this._gridColumns.length; i++) { + const columnId = this._gridColumns[i].field; + this._grid.updateColumnHeader(columnId); + } + } + + /** + * Setup the column reordering + * NOTE: this function is a standalone function and is called externally and does not have access to `this` instance + * @param grid - slick grid object + * @param headers - slick grid column header elements + * @param _headerColumnWidthDiff - header column width difference + * @param setColumns - callback to reassign columns + * @param setupColumnResize - callback to setup the column resize + * @param columns - columns array + * @param getColumnIndex - callback to find index of a column + * @param uid - grid UID + * @param trigger - callback to execute when triggering a column grouping + */ + getSetupColumnReorder(grid: SlickGrid, headers: any, _headerColumnWidthDiff: any, setColumns: (columns: Column[]) => void, setupColumnResize: () => void, _columns: Column[], getColumnIndex: (columnId: string) => number, _uid: string, trigger: (slickEvent: SlickEvent_, data?: any) => void) { + this.destroySortableInstances(); + const dropzoneElm = grid.getPreHeaderPanel(); + const groupTogglerElm = dropzoneElm.querySelector('.slick-group-toggle-all'); + + const sortableOptions = { + animation: 50, + // chosenClass: 'slick-header-column-active', + ghostClass: 'slick-sortable-placeholder', + draggable: '.slick-header-column', + dataIdAttr: 'data-id', + group: { + name: 'shared', + pull: 'clone', + put: false, + }, + revertClone: true, + // filter: function (_e, target) { + // // block column from being able to be dragged if it's already a grouped column + // // NOTE: need to disable for now since it also blocks the column reordering + // return this.columnsGroupBy.some(c => c.id === target.getAttribute('data-id')); + // }, + onStart: () => { + dropzoneElm.classList.add('slick-dropzone-hover'); + dropzoneElm.classList.add('slick-dropzone-placeholder-hover'); + const draggablePlaceholderElm = dropzoneElm.querySelector('.slick-placeholder'); + if (draggablePlaceholderElm) { + draggablePlaceholderElm.style.display = 'inline-block'; + } + + const droppedGroupingElms = dropzoneElm.querySelectorAll('.slick-dropped-grouping'); + droppedGroupingElms.forEach(droppedGroupingElm => droppedGroupingElm.style.display = 'none'); + if (groupTogglerElm) { + groupTogglerElm.style.display = 'none'; + } + }, + onEnd: (e: Event & { item: any; clone: HTMLElement; }) => { + const draggablePlaceholderElm = dropzoneElm.querySelector('.slick-placeholder'); + dropzoneElm.classList.remove('slick-dropzone-hover'); + draggablePlaceholderElm?.classList.remove('slick-dropzone-placeholder-hover'); + + + if (this._dropzonePlaceholder) { + this._dropzonePlaceholder.style.display = 'none'; + } + if (draggablePlaceholderElm) { + draggablePlaceholderElm.parentElement?.classList.remove('slick-dropzone-placeholder-hover'); + } + + const droppedGroupingElms = dropzoneElm.querySelectorAll('.slick-dropped-grouping'); + if (droppedGroupingElms.length) { + droppedGroupingElms.forEach(droppedGroupingElm => droppedGroupingElm.style.display = 'inline-flex'); + if (draggablePlaceholderElm) { + draggablePlaceholderElm.style.display = 'none'; + } + if (groupTogglerElm) { + groupTogglerElm.style.display = 'inline-block'; + } + } + + if (!grid.getEditorLock().commitCurrentEdit()) { + return; + } + + const reorderedIds = this._sortableLeftInstance?.toArray() ?? []; + + // when frozen columns are used, headers has more than one entry and we need the ids from all of them. + // though there is only really a left and right header, this will work even if that should change. + if (headers.length > 1) { + const ids = this._sortableRightInstance?.toArray() ?? []; + + // Note: the loop below could be simplified with: + // reorderedIds.push.apply(reorderedIds,ids); + // However, the loop is more in keeping with way-backward compatibility + for (const id of ids) { + reorderedIds.push(id); + } + } + + const finalReorderedColumns: Column[] = []; + const reorderedColumns = grid.getColumns(); + for (const reorderedId of reorderedIds) { + finalReorderedColumns.push(reorderedColumns[getColumnIndex.call(grid, reorderedId)]); + } + setColumns.call(grid, finalReorderedColumns); + trigger.call(grid, grid.onColumnsReordered, { grid }); + e.stopPropagation(); + setupColumnResize.call(grid); + } + } + + this._sortableLeftInstance = Sortable.create(document.querySelector(`.${grid.getUID()} .slick-header-columns.slick-header-columns-left`) as HTMLDivElement, sortableOptions); + this._sortableRightInstance = Sortable.create(document.querySelector(`.${grid.getUID()} .slick-header-columns.slick-header-columns-right`) as HTMLDivElement, sortableOptions); + + return { + sortableLeftInstance: this._sortableLeftInstance, + sortableRightInstance: this._sortableRightInstance + }; + } + + /** + * Destroy plugin. + */ + destroy() { + this.destroySortableInstances(); + this.onGroupChanged.unsubscribe(); + this._handler.unsubscribeAll(); + this._bindingEventService.unbindAll(); + Utils.emptyElement(document.querySelector(`.${this._gridUid} .slick-preheader-panel`)); + } + + protected destroySortableInstances() { + if (this._sortableLeftInstance?.el) { + this._sortableLeftInstance?.destroy(); + } + if (this._sortableRightInstance?.el) { + this._sortableRightInstance?.destroy(); + } + } + + protected addDragOverDropzoneListeners() { + const draggablePlaceholderElm = this._dropzoneElm.querySelector('.slick-placeholder'); + + if (draggablePlaceholderElm && this._dropzoneElm) { + this._bindingEventService.bind(draggablePlaceholderElm, 'dragover', (e) => e.preventDefault()); + this._bindingEventService.bind(draggablePlaceholderElm, 'dragenter', () => this._dropzoneElm.classList.add('slick-dropzone-hover')); + this._bindingEventService.bind(draggablePlaceholderElm, 'dragleave', () => this._dropzoneElm.classList.remove('slick-dropzone-hover')); + } + } + + protected setupColumnDropbox() { + const dropzoneElm = this._dropzoneElm; + + this._droppableInstance = Sortable.create(dropzoneElm, { + group: 'shared', + // chosenClass: 'slick-header-column-active', + ghostClass: 'slick-droppable-sortitem-hover', + draggable: '.slick-dropped-grouping', + dragoverBubble: true, + onAdd: (evt) => { + const el = evt.item; + const elId = el.getAttribute('id'); + if (elId?.replace(this._gridUid, '')) { + this.handleGroupByDrop(dropzoneElm, (Sortable.utils).clone(evt.item)); + } + evt.clone.style.opacity = '.5'; + el.parentNode?.removeChild(el); + }, + onUpdate: () => { + const sortArray = this._droppableInstance?.toArray() ?? []; + let newGroupingOrder: Column[] = []; + for (let i = 0, l = sortArray.length; i < l; i++) { + for (let a = 0, b = this._columnsGroupBy.length; a < b; a++) { + if (this._columnsGroupBy[a].id == sortArray[i]) { + newGroupingOrder.push(this._columnsGroupBy[a]); + break; + } + } + } + this._columnsGroupBy = newGroupingOrder; + this.updateGroupBy("sort-group"); + }, + }); + + // Sortable doesn't have onOver, we need to implement it ourselves + this.addDragOverDropzoneListeners(); + + if (this._groupToggler) { + this._bindingEventService.bind(this._groupToggler, 'click', ((event: DOMMouseOrTouchEvent) => { + const target = event.target; + this.toggleGroupToggler(target, target?.classList.contains('expanded')); + }) as EventListener); + } + } + + protected handleGroupByDrop(containerElm: HTMLDivElement, headerColumnElm: HTMLDivElement) { + const headerColDataId = headerColumnElm.getAttribute('data-id'); + const columnId = headerColDataId?.replace(this._gridUid, ''); + let columnAllowed = true; + for (const colGroupBy of this._columnsGroupBy) { + if (colGroupBy.id === columnId) { + columnAllowed = false; + } + } + + if (columnAllowed) { + for (const col of this._gridColumns) { + if (col.id === columnId && col.grouping && !Utils.isEmptyObject(col.grouping)) { + const columnNameElm = headerColumnElm.querySelector('.slick-column-name'); + const entryElm = document.createElement('div'); + entryElm.id = `${this._gridUid}_${col.id}_entry`; + entryElm.className = 'slick-dropped-grouping'; + entryElm.dataset.id = `${col.id}`; + + const groupTextElm = document.createElement('div'); + groupTextElm.className = 'slick-dropped-grouping-title'; + groupTextElm.style.display = 'inline-flex'; + groupTextElm.textContent = columnNameElm ? columnNameElm.textContent : headerColumnElm.textContent; + entryElm.appendChild(groupTextElm); + + // delete icon + const groupRemoveIconElm = document.createElement('div'); + groupRemoveIconElm.className = 'slick-groupby-remove'; + if (this._options.deleteIconCssClass) { + groupRemoveIconElm.classList.add(...this._options.deleteIconCssClass.split(' ')); + } + if (this._options.deleteIconImage) { + groupRemoveIconElm.classList.add(...this._options.deleteIconImage.split(' ')); + } + if (!this._options.deleteIconCssClass) { + groupRemoveIconElm.classList.add('slick-groupby-remove-icon'); + } + if (!this._options.deleteIconCssClass && !this._options.deleteIconImage) { + groupRemoveIconElm.classList.add('slick-groupby-remove-image'); + } + + // sorting icons when enabled + if (this._options?.hideGroupSortIcons !== true && col.sortable) { + if (col.grouping?.sortAsc === undefined) { + col.grouping.sortAsc = true; + } + } + + entryElm.appendChild(groupRemoveIconElm); + entryElm.appendChild(document.createElement('div')); + containerElm.appendChild(entryElm); + + this.addColumnGroupBy(col); + this.addGroupByRemoveClickHandler(col.id, groupRemoveIconElm, headerColumnElm, entryElm); + } + } + + // show the "Toggle All" when feature is enabled + if (this._groupToggler && this._columnsGroupBy.length > 0) { + this._groupToggler.style.display = 'inline-block'; + } + } + } + + protected addColumnGroupBy(column: Column) { + this._columnsGroupBy.push(column); + this.updateGroupBy("add-group"); + } + + protected addGroupByRemoveClickHandler(id: string | number, groupRemoveIconElm: HTMLDivElement, headerColumnElm: HTMLDivElement, entry: any) { + this._bindingEventService.bind(groupRemoveIconElm, 'click', () => { + const boundedElms = this._bindingEventService.getBoundedEvents().filter(boundedEvent => boundedEvent.element === groupRemoveIconElm); + for (const boundedEvent of boundedElms) { + this._bindingEventService.unbind(boundedEvent.element, 'click', boundedEvent.listener); + } + this.removeGroupBy(id, headerColumnElm, entry); + }); + } + + setDroppedGroups(groupingInfo: Array | string) { + const groupingInfos = Array.isArray(groupingInfo) ? groupingInfo : [groupingInfo]; + this._dropzonePlaceholder.style.display = 'none'; + for (const groupInfo of groupingInfos) { + const columnElm = this._grid.getHeaderColumn(groupInfo as string); + this.handleGroupByDrop(this._dropzoneElm, columnElm); + } + } + + clearDroppedGroups() { + this._columnsGroupBy = []; + this.updateGroupBy('clear-all'); + const allDroppedGroupingElms = this._dropzoneElm.querySelectorAll('.slick-dropped-grouping'); + + for (const groupElm of Array.from(allDroppedGroupingElms)) { + const groupRemoveBtnElm = this._dropzoneElm.querySelector('.slick-groupby-remove'); + groupRemoveBtnElm?.remove(); + groupElm?.remove(); + } + + // show placeholder text & hide the "Toggle All" when that later feature is enabled + this._dropzonePlaceholder.style.display = 'inline-block'; + if (this._groupToggler) { + this._groupToggler.style.display = 'none'; + } + } + + protected removeFromArray(arrayToModify: any[], itemToRemove: any) { + if (Array.isArray(arrayToModify)) { + const itemIdx = arrayToModify.findIndex(a => a.id === itemToRemove.id); + if (itemIdx >= 0) { + arrayToModify.splice(itemIdx, 1); + } + } + return arrayToModify; + } + + protected removeGroupBy(id: string | number, _hdrColumnElm: HTMLDivElement, entry: any) { + entry.remove(); + const groupby: Column[] = []; + this._gridColumns.forEach((col) => groupby[col.id] = col); + this.removeFromArray(this._columnsGroupBy, groupby[id]); + if (this._columnsGroupBy.length === 0) { + this._dropzonePlaceholder.style.display = 'block'; + if (this._groupToggler) { + this._groupToggler.style.display = 'none'; + } + } + this.updateGroupBy("remove-group"); + } + + protected toggleGroupToggler(targetElm: Element | null, collapsing = true, shouldExecuteDataViewCommand = true) { + if (targetElm) { + if (collapsing === true) { + targetElm.classList.add('collapsed'); + targetElm.classList.remove('expanded'); + if (shouldExecuteDataViewCommand) { + this._dataView.collapseAllGroups(); + } + } else { + targetElm.classList.remove('collapsed'); + targetElm.classList.add('expanded'); + if (shouldExecuteDataViewCommand) { + this._dataView.expandAllGroups(); + } + } + } + } + + protected updateGroupBy(originator: string) { + if (this._columnsGroupBy.length === 0) { + this._dataView.setGrouping([]); + this.onGroupChanged.notify({ caller: originator, groupColumns: [] }); + return; + } + const groupingArray: Grouping[] = []; + this._columnsGroupBy.forEach((element) => groupingArray.push(element.grouping!)); + this._dataView.setGrouping(groupingArray); + this.onGroupChanged.notify({ caller: originator, groupColumns: groupingArray }); + } +} + +// extend Slick namespace on window object when building as iife +if (IIFE_ONLY && window.Slick) { + Utils.extend(true, window, { + Slick: { + DraggableGrouping: SlickDraggableGrouping + } + }); +} + diff --git a/src/slick.compositeeditor.ts b/src/slick.compositeeditor.ts index 2e781fc3..367e4e52 100644 --- a/src/slick.compositeeditor.ts +++ b/src/slick.compositeeditor.ts @@ -2,7 +2,7 @@ import type { CompositeEditorOption, Editor, EditorArguments } from './models/in import { Utils as Utils_ } from './slick.core'; // for (iife) load Slick methods from global Slick object, or use imports for (cjs/esm) -const Utils = (IIFE_ONLY ? Slick.Utils : Utils_) as typeof Utils_; +const Utils = IIFE_ONLY ? Slick.Utils : Utils_; /** * A composite SlickGrid editor factory. diff --git a/src/slick.core.ts b/src/slick.core.ts index 9a074546..3f0f8b3c 100644 --- a/src/slick.core.ts +++ b/src/slick.core.ts @@ -585,7 +585,7 @@ function createDomElement(...args: any[]): T { * Unbinding is a necessary step to make sure that all event listeners are removed to avoid memory leaks when destroing the grid */ export class BindingEventService { - protected boundedEvents: ElementEventListener[] = []; + protected _boundedEvents: ElementEventListener[] = []; + + getBoundedEvents() { + return this._boundedEvents; + } destroy() { this.unbindAll(); - this.boundedEvents = []; } /** Bind an event listener to any element */ bind(element: Element, eventName: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions) { element.addEventListener(eventName, listener, options); - this.boundedEvents.push({ element: element, eventName, listener }); + this._boundedEvents.push({ element, eventName, listener }); } /** Unbind all will remove every every event handlers that were bounded earlier */ unbind(element: Element, eventName: string, listener: EventListenerOrEventListenerObject) { - if (element && element.removeEventListener) { + if (element?.removeEventListener) { element.removeEventListener(eventName, listener); } } - unbindByEventName(element, eventName) { - const boundedEvent = this.boundedEvents.find(e => e.element === element && e.eventName === eventName); + unbindByEventName(element: Element, eventName: string) { + const boundedEvent = this._boundedEvents.find(e => e.element === element && e.eventName === eventName); if (boundedEvent) { this.unbind(boundedEvent.element, boundedEvent.eventName, boundedEvent.listener); } @@ -869,10 +872,9 @@ export class BindingEventService { /** Unbind all will remove every every event handlers that were bounded earlier */ unbindAll() { - while (this.boundedEvents.length > 0) { - const boundedEvent = this.boundedEvents.pop() as ElementEventListener; - const { element, eventName, listener } = boundedEvent; - this.unbind(element, eventName, listener); + while (this._boundedEvents.length > 0) { + const boundedEvent = this._boundedEvents.pop() as ElementEventListener; + this.unbind(boundedEvent.element, boundedEvent.eventName, boundedEvent.listener); } } } diff --git a/src/slick.dataview.ts b/src/slick.dataview.ts index 45bd1b70..c3704082 100644 --- a/src/slick.dataview.ts +++ b/src/slick.dataview.ts @@ -27,12 +27,12 @@ import type { SlickGrid } from './slick.grid'; import { SlickGroupItemMetadataProvider as SlickGroupItemMetadataProvider_ } from './slick.groupitemmetadataprovider'; // for (iife) load Slick methods from global Slick object, or use imports for (cjs/esm) -const SlickEvent = (IIFE_ONLY ? Slick.Event : SlickEvent_) as typeof SlickEvent_; -const SlickEventData = (IIFE_ONLY ? Slick.EventData : SlickEventData_) as typeof SlickEventData_; -const SlickGroup = (IIFE_ONLY ? Slick.Group : SlickGroup_) as typeof SlickGroup_; -const SlickGroupTotals = (IIFE_ONLY ? Slick.GroupTotals : SlickGroupTotals_) as typeof SlickGroupTotals_; -const Utils = (IIFE_ONLY ? Slick.Utils : Utils_) as typeof Utils_; -const SlickGroupItemMetadataProvider = (IIFE_ONLY ? Slick.Data?.GroupItemMetadataProvider ?? {} : SlickGroupItemMetadataProvider_) as typeof SlickGroupItemMetadataProvider_; +const SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_; +const SlickEventData = IIFE_ONLY ? Slick.EventData : SlickEventData_; +const SlickGroup = IIFE_ONLY ? Slick.Group : SlickGroup_; +const SlickGroupTotals = IIFE_ONLY ? Slick.GroupTotals : SlickGroupTotals_; +const Utils = IIFE_ONLY ? Slick.Utils : Utils_; +const SlickGroupItemMetadataProvider = IIFE_ONLY ? Slick.Data?.GroupItemMetadataProvider ?? {} : SlickGroupItemMetadataProvider_; export interface DataViewOption { groupItemMetadataProvider?: SlickGroupItemMetadataProvider_; diff --git a/src/slick.editors.ts b/src/slick.editors.ts index 9d333b38..c56c2bc5 100644 --- a/src/slick.editors.ts +++ b/src/slick.editors.ts @@ -2,8 +2,8 @@ import type { Editor, EditorArguments, EditorValidationResult } from './models/i import { keyCode as keyCode_, Utils as Utils_ } from './slick.core'; // for (iife) load Slick methods from global Slick object, or use imports for (cjs/esm) -const keyCode = (IIFE_ONLY ? Slick.keyCode : keyCode_) as typeof keyCode_; -const Utils = (IIFE_ONLY ? Slick.Utils : Utils_) as typeof Utils_; +const keyCode = IIFE_ONLY ? Slick.keyCode : keyCode_; +const Utils = IIFE_ONLY ? Slick.Utils : Utils_; /*** * Contains basic SlickGrid editors. diff --git a/src/slick.formatters.ts b/src/slick.formatters.ts index 7382cf06..e8134036 100644 --- a/src/slick.formatters.ts +++ b/src/slick.formatters.ts @@ -2,7 +2,7 @@ import type { Formatter } from './models/index'; import { Utils as Utils_ } from './slick.core'; // for (iife) load Slick methods from global Slick object, or use imports for (cjs/esm) -const Utils = (IIFE_ONLY ? Slick.Utils : Utils_) as typeof Utils_; +const Utils = IIFE_ONLY ? Slick.Utils : Utils_; /*** * Contains basic SlickGrid formatters. diff --git a/src/slick.grid.ts b/src/slick.grid.ts index 231482cb..4511553d 100644 --- a/src/slick.grid.ts +++ b/src/slick.grid.ts @@ -75,22 +75,22 @@ import { import { Draggable as Draggable_, MouseWheel as MouseWheel_, Resizable as Resizable_ } from './slick.interactions'; // for (iife) load Slick methods from global Slick object, or use imports for (cjs/esm) -const BindingEventService = (IIFE_ONLY ? Slick.BindingEventService : BindingEventService_) as typeof BindingEventService_; -const ColAutosizeMode = (IIFE_ONLY ? Slick.ColAutosizeMode : ColAutosizeMode_); -const SlickEvent = (IIFE_ONLY ? Slick.Event : SlickEvent_) as typeof SlickEvent_; -const SlickEventData = (IIFE_ONLY ? Slick.EventData : SlickEventData_) as typeof SlickEventData_; -const GlobalEditorLock = (IIFE_ONLY ? Slick.GlobalEditorLock : GlobalEditorLock_) as typeof GlobalEditorLock_; -const GridAutosizeColsMode = (IIFE_ONLY ? Slick.GridAutosizeColsMode : GridAutosizeColsMode_); -const keyCode = (IIFE_ONLY ? Slick.keyCode : keyCode_); -const preClickClassName = (IIFE_ONLY ? Slick.preClickClassName : preClickClassName_); -const SlickRange = (IIFE_ONLY ? Slick.Range : SlickRange_) as typeof SlickRange_; -const RowSelectionMode = (IIFE_ONLY ? Slick.RowSelectionMode : RowSelectionMode_); -const ValueFilterMode = (IIFE_ONLY ? Slick.ValueFilterMode : ValueFilterMode_); -const Utils = (IIFE_ONLY ? Slick.Utils : Utils_) as typeof Utils_; -const WidthEvalMode = (IIFE_ONLY ? Slick.WidthEvalMode : WidthEvalMode_); -const Draggable = (IIFE_ONLY ? Slick.Draggable : Draggable_); -const MouseWheel = (IIFE_ONLY ? Slick.MouseWheel : MouseWheel_); -const Resizable = (IIFE_ONLY ? Slick.Resizable : Resizable_); +const BindingEventService = IIFE_ONLY ? Slick.BindingEventService : BindingEventService_; +const ColAutosizeMode = IIFE_ONLY ? Slick.ColAutosizeMode : ColAutosizeMode_; +const SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_; +const SlickEventData = IIFE_ONLY ? Slick.EventData : SlickEventData_; +const GlobalEditorLock = IIFE_ONLY ? Slick.GlobalEditorLock : GlobalEditorLock_; +const GridAutosizeColsMode = IIFE_ONLY ? Slick.GridAutosizeColsMode : GridAutosizeColsMode_; +const keyCode = IIFE_ONLY ? Slick.keyCode : keyCode_; +const preClickClassName = IIFE_ONLY ? Slick.preClickClassName : preClickClassName_; +const SlickRange = IIFE_ONLY ? Slick.Range : SlickRange_; +const RowSelectionMode = IIFE_ONLY ? Slick.RowSelectionMode : RowSelectionMode_; +const ValueFilterMode = IIFE_ONLY ? Slick.ValueFilterMode : ValueFilterMode_; +const Utils = IIFE_ONLY ? Slick.Utils : Utils_; +const WidthEvalMode = IIFE_ONLY ? Slick.WidthEvalMode : WidthEvalMode_; +const Draggable = IIFE_ONLY ? Slick.Draggable : Draggable_; +const MouseWheel = IIFE_ONLY ? Slick.MouseWheel : MouseWheel_; +const Resizable = IIFE_ONLY ? Slick.Resizable : Resizable_; /** * @license @@ -1257,7 +1257,7 @@ export class SlickGrid { * @param {String} title New column name. * @param {String} [toolTip] New column tooltip. */ - updateColumnHeader(columnId: number | string, title: string, toolTip?: string) { + updateColumnHeader(columnId: number | string, title?: string, toolTip?: string) { if (!this.initialized) { return; } let idx = this.getColumnIndex(columnId); if (idx == null) { @@ -1314,7 +1314,7 @@ export class SlickGrid { let targetHeader = this.hasFrozenColumns() ? ((idx <= this._options.frozenColumn) ? this._headerL : this._headerR) : this._headerL; let targetIndex = this.hasFrozenColumns() ? ((idx <= this._options.frozenColumn) ? idx : idx - this._options.frozenColumn - 1) : idx; - return targetHeader.children[targetIndex]; + return targetHeader.children[targetIndex] as HTMLDivElement; } /** Get the Header Row DOM element */ @@ -1584,7 +1584,7 @@ export class SlickGrid { this.setSortColumns(this.sortColumns); this.setupColumnResize(); if (this._options.enableColumnReorder) { - if (typeof this._options.enableColumnReorder == 'function') { + if (typeof this._options.enableColumnReorder === 'function') { this._options.enableColumnReorder(this, this._headers, this.headerColumnWidthDiff, this.setColumns, this.setupColumnResize, this.columns, this.getColumnIndex, this.uid, this.trigger); } else { this.setupColumnReorder(); @@ -3093,7 +3093,7 @@ export class SlickGrid { * Returns the index of a column with a given id. Since columns can be reordered by the user, this can be used to get the column definition independent of the order: * @param id A column id. */ - getColumnIndex(id: number | string) { + getColumnIndex(id: number | string): number { return this.columnsById[id]; } diff --git a/src/slick.groupitemmetadataprovider.ts b/src/slick.groupitemmetadataprovider.ts index c34a15f1..d58dbfcd 100644 --- a/src/slick.groupitemmetadataprovider.ts +++ b/src/slick.groupitemmetadataprovider.ts @@ -3,9 +3,9 @@ import { SlickGroup as SlickGroup_, keyCode as keyCode_, Utils as Utils_ } from import type { SlickGrid } from './slick.grid'; // for (iife) load Slick methods from global Slick object, or use imports for (cjs/esm) -const SlickGroup = (IIFE_ONLY ? Slick.Group : SlickGroup_) as typeof SlickGroup_; -const keyCode = (IIFE_ONLY ? Slick.keyCode : keyCode_) as typeof keyCode_; -const Utils = (IIFE_ONLY ? Slick.Utils : Utils_) as typeof Utils_; +const keyCode = IIFE_ONLY ? Slick.keyCode : keyCode_; +const SlickGroup = IIFE_ONLY ? Slick.Group : SlickGroup_; +const Utils = IIFE_ONLY ? Slick.Utils : Utils_; /** * Provides item metadata for group (Slick.Group) and totals (Slick.Totals) rows produced by the DataView. diff --git a/src/slick.interactions.ts b/src/slick.interactions.ts index 7499d037..ae7869ee 100644 --- a/src/slick.interactions.ts +++ b/src/slick.interactions.ts @@ -2,7 +2,7 @@ import type { DraggableOption, MouseWheelOption, ResizableOption } from './model import { Utils as Utils_ } from './slick.core'; // for (iife) load Slick methods from global Slick object, or use imports for (cjs/esm) -const Utils = (IIFE_ONLY ? Slick.Utils : Utils_) as typeof Utils_; +const Utils = IIFE_ONLY ? Slick.Utils : Utils_; /*** * Interactions, add basic behaviors to any element. diff --git a/src/styles/slick-alpine-theme.scss b/src/styles/slick-alpine-theme.scss index 1b9966d5..c42b2a69 100644 --- a/src/styles/slick-alpine-theme.scss +++ b/src/styles/slick-alpine-theme.scss @@ -128,11 +128,9 @@ opacity: var(--alpine-group-icon-opacity, $alpine-group-icon-opacity); &.collapsed { - background: none; @include createSvgStyle('alpine-group-collapsed-icon-svg', $alpine-group-collapsed-icon-svg-path); } &.expanded { - background: none; @include createSvgStyle('alpine-group-expanded-icon-svg', $alpine-group-expanded-icon-svg-path); } }