UNPKG

slickgrid

Version:

A lightning fast JavaScript grid/spreadsheet

385 lines (346 loc) 14.4 kB
/** * * 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 */ (function ($) { // Register namespace $.extend(true, window, { "Slick": { "DraggableGrouping": DraggableGrouping } }); /*** * 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 * */ function DraggableGrouping(options) { var _grid; var _gridUid; var _gridColumns; var _dataView; var $dropzone; var dropzonePlaceholder; var groupToggler; var _self = this; var _defaults = { }; var onGroupChanged = new Slick.Event(); var _handler = new Slick.EventHandler(); var _reorderedColumns = []; var sortableLeftInstance; var sortableRightInstance; /** * Initialize plugin. */ function init(grid) { options = $.extend(true, {}, _defaults, options); _grid = grid; _gridUid = _grid.getUID(); _gridColumns = _grid.getColumns(); _dataView = _grid.getData(); _reorderedColumns = _gridColumns; $dropzone = $(_grid.getPreHeaderPanel()); $dropzone.addClass("slick-dropzone"); var dropPlaceHolderText = options.dropPlaceHolderText || 'Drop a column header here to group by the column'; $dropzone.html("<div class='slick-placeholder'>" + dropPlaceHolderText + "</div><div class='slick-group-toggle-all expanded' style='display:none'></div>"); dropzonePlaceholder = $dropzone.find(".slick-placeholder"); groupToggler = $dropzone.find(".slick-group-toggle-all"); groupToggler.hide(); setupColumnDropbox(); _handler.subscribe(_grid.onHeaderCellRendered, function (e, args) { var column = args.column; var node = args.node; if (!$.isEmptyObject(column.grouping)) { var groupableIcon = $("<span class='slick-column-groupable' />"); if (options.groupIconCssClass) { groupableIcon.addClass(options.groupIconCssClass); } if (options.groupIconImage) { groupableIcon.css("background", "url(" + options.groupIconImage + ") no-repeat center center"); } $(node).css('cursor', 'pointer').append(groupableIcon); } }); 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) { var $headerDraggableGroupBy = $(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 () { $('.slick-dropzone').addClass('slick-dropzone-hover'); $headerDraggableGroupBy.find('.slick-placeholder').show(); $headerDraggableGroupBy.find('.slick-dropped-grouping').hide(); groupToggler.hide(); }, onEnd: function (e) { const $draggablePlaceholder = $headerDraggableGroupBy.find('.slick-placeholder'); $dropzone.removeClass('slick-dropzone-hover'); $draggablePlaceholder.addClass('slick-dropzone-placeholder-hover'); $('.slick-dropzone').removeClass('slick-dropzone-hover'); $('.slick-placeholder').parent().removeClass('slick-dropzone-placeholder-hover'); var hasDroppedColumn = $headerDraggableGroupBy.find('.slick-dropped-grouping').length; if (hasDroppedColumn > 0) { $headerDraggableGroupBy.find('.slick-placeholder').hide(); $headerDraggableGroupBy.find('.slick-dropped-grouping').show(); groupToggler.show(); } if (!grid.getEditorLock().commitCurrentEdit()) { return; } var reorderedIds = sortableLeftInstance.toArray(); // If 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) { for (var headerI = 1, l = $headers.length; headerI < l; headerI += 1) { var ids = 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 (var idI = 0, idL = ids.length; idI < idL; idI += 1) { reorderedIds.push(ids[idI]); } } } var reorderedColumns = []; for (var i = 0; i < reorderedIds.length; i++) { reorderedColumns.push(_reorderedColumns[getColumnIndex(reorderedIds[i])]); } setColumns(reorderedColumns); trigger(grid.onColumnsReordered, { grid: grid }); e.stopPropagation(); setupColumnResize(); _reorderedColumns = reorderedColumns; } } if ($headers[0]) { sortableLeftInstance = Sortable.create($headers[0], sortableOptions); } if ($headers[1]) { sortableRightInstance = Sortable.create($headers[1], sortableOptions); } } /** * Destroy plugin. */ function destroy() { $('.slick-placeholder').off('dragover dragenter dragleave'); onGroupChanged.unsubscribe(); _handler.unsubscribeAll(); if (sortableLeftInstance) { sortableLeftInstance.destroy(); sortableRightInstance.destroy(); } } function addDragOverDropzoneListeners() { var $dragPlaceholder = $('.slick-placeholder'); $dragPlaceholder.on('dragover', function (evt) { evt.preventDefault(); }); $dragPlaceholder.on('dragenter', function (evt) { $dragPlaceholder.parent().addClass('slick-dropzone-placeholder-hover'); }); $dragPlaceholder.on('dragleave', function (evt) { $dragPlaceholder.parent().removeClass('slick-dropzone-placeholder-hover'); }); } function setupColumnDropbox() { var dropzoneElm = $dropzone[0]; var sortableDrop = Sortable.create(dropzoneElm, { group: "shared", // chosenClass: "slick-header-column-active", ghostClass: "slick-droppable-sortitem-hover", draggable: '.slick-dropped-grouping', dragoverBubble: true, onAdd: function (evt) { var el = evt.item; if (el.getAttribute('id').replace(_gridUid, '')) { handleGroupByDrop($dropzone[0], $(Sortable.utils.clone(evt.item))); } evt.clone.style.opacity = .5; el.parentNode.removeChild(el); }, onUpdate: function (e) { var sortArray = sortableDrop.toArray(), 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(); groupToggler.on('click', function (e) { if (this.classList.contains('collapsed')) { this.classList.remove('collapsed'); this.classList.add('expanded'); _dataView.expandAllGroups(); } else { this.classList.add('collapsed'); this.classList.remove('expanded'); _dataView.collapseAllGroups(); } }); } var columnsGroupBy = []; var groupBySorters = []; function handleGroupByDrop(container, headerColumn) { var columnid = headerColumn.attr('data-id').replace(this._gridUid, ''); var columnAllowed = true; columnsGroupBy.forEach(function (e, i, a) { if (e.id == columnid) { columnAllowed = false; } }); if (columnAllowed) { _gridColumns.forEach(function (col, i, a) { if (col.id == columnid) { if (col.grouping != null && !$.isEmptyObject(col.grouping)) { var entry = $("<div id='" + _gridUid + col.id + "_entry' data-id='" + col.id + "' class='slick-dropped-grouping'>"); var $columnName = headerColumn.find('.slick-column-name'); var groupText = $("<div class='slick-dropped-grouping-title' style='display: inline-flex'>" + ($columnName.length > 0 ? $columnName.text() : headerColumn.text()) + "</div>"); groupText.appendTo(entry); var groupRemoveIcon = $("<div class='slick-groupby-remove'></div>"); if (options.deleteIconCssClass) groupRemoveIcon.addClass(options.deleteIconCssClass); if (options.deleteIconImage) groupRemoveIcon.css("background", "url(" + options.deleteIconImage + ") no-repeat center right"); if (!options.deleteIconCssClass && !options.deleteIconImage) groupRemoveIcon.addClass('slick-groupby-remove-image'); groupRemoveIcon.appendTo(entry); $("</div>").appendTo(entry); entry.appendTo(container); addColumnGroupBy(col); addGroupByRemoveClickHandler(col.id, headerColumn, entry); } } }); groupToggler.show(); } } function addColumnGroupBy(column) { columnsGroupBy.push(column); updateGroupBy("add-group"); } function addGroupByRemoveClickHandler(id, column, entry) { var text = entry; $("#" + _gridUid + id + "_entry >.slick-groupby-remove").on('click', function () { $(this).off('click'); removeGroupBy(id, column, text); }); } function setDroppedGroups(groupingInfo) { var groupingInfos = (groupingInfo instanceof Array) ? groupingInfo : [groupingInfo]; dropzonePlaceholder.hide(); for (var i = 0; i < groupingInfos.length; i++) { var column = $(_grid.getHeaderColumn(groupingInfos[i])); handleGroupByDrop($dropzone, column); } } function clearDroppedGroups() { columnsGroupBy = []; updateGroupBy("clear-all"); $dropzone.find(".slick-dropped-grouping").remove(); groupToggler.hide(); dropzonePlaceholder.show(); } 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, i, a) { groupby[e.id] = e; }); removeFromArray(columnsGroupBy, groupby[id]); if (columnsGroupBy.length === 0) { dropzonePlaceholder.show(); groupToggler.hide(); } updateGroupBy("remove-group"); } function updateGroupBy(originator) { if (columnsGroupBy.length === 0) { _dataView.setGrouping([]); onGroupChanged.notify({ caller: originator, groupColumns: [] }); return; } var groupingArray = []; columnsGroupBy.forEach(function (element, index, array) { groupingArray.push(element.grouping); }); _dataView.setGrouping(groupingArray); /* collapseAllGroups(); */ onGroupChanged.notify({ caller: originator, groupColumns: groupingArray }); } // Public API $.extend(this, { "init": init, "destroy": destroy, "pluginName": "DraggableGrouping", "onGroupChanged": onGroupChanged, "setDroppedGroups": setDroppedGroups, "clearDroppedGroups": clearDroppedGroups, "getSetupColumnReorder": setupColumnReorder, }); } })(jQuery);