/** * The grouping panel used by {@link Ext.grid.plugin.GroupingPanel} */Ext.define('Ext.grid.plugin.grouping.Panel', { extend: 'Ext.panel.Panel', alias: 'widget.groupingpanel', requires: [ 'Ext.grid.plugin.grouping.Column', 'Ext.grid.plugin.grouping.DragZone', 'Ext.grid.plugin.grouping.DropZone', 'Ext.layout.container.Column' ], mixins: [ 'Ext.mixin.FocusableContainer' ], isGroupingPanel: true, position: 'top', border: false, enableFocusableContainer: true, // the column header container has a weight of 100 so we want to dock it before that. weight: 50, height: 'auto', layout: 'column', childEls: ['innerCt', 'targetEl'], cls: Ext.baseCSSPrefix + 'grid-group-panel-body', hintTextCls: Ext.baseCSSPrefix + 'grid-group-panel-hint', config: { grid: null, store: null, columnConfig: { xtype: 'groupingpanelcolumn' } }, keyEventRe: /^key/, groupingPanelText: 'Drag a column header here to group by that column', showGroupingPanelText: 'Show Group By Panel', hideGroupingPanelText: 'Hide Group By Panel', clearGroupText: 'Clear Group', sortAscText: 'Sort Ascending', sortDescText: 'Sort Descending', moveLeftText: 'Move left', moveRightText: 'Move right', moveBeginText: 'Move to beginning', moveEndText: 'Move to end', removeText: 'Remove', ascSortIconCls: Ext.baseCSSPrefix + 'grid-group-column-sort-icon-asc', descSortIconCls: Ext.baseCSSPrefix + 'grid-group-column-sort-icon-desc', groupingPanelIconCls: Ext.baseCSSPrefix + 'grid-group-panel-icon', clearGroupIconCls: Ext.baseCSSPrefix + 'grid-group-panel-clear-icon', initComponent: function() { var me = this; Ext.apply(me, { header: { dock: 'left', title: { hidden: true }, padding: 0, tools: [{ type: 'gear', handler: me.showPanelMenu, scope: me }] } }); me.callParent(arguments); }, doDestroy: function() { var me = this; // eslint-disable-next-line max-len Ext.destroyMembers(me, 'infoEl', 'dragZone', 'dropZone', 'contextMenu', 'panelListeners', 'columnListeners', 'storeListeners', 'columnMenu', 'panelMenu'); me.setGrid(null); me.callParent(); }, afterRender: function() { var me = this, el = me.getEl(); me.callParent(); me.dragZone = new Ext.grid.plugin.grouping.DragZone(me); me.dropZone = new Ext.grid.plugin.grouping.DropZone(me); me.panelListeners = me.mon(el, { contextmenu: me.showPanelMenu, scope: me, destroyable: true }); me.columnListeners = me.mon(el, { delegate: '.' + Ext.baseCSSPrefix + 'grid-group-column', click: me.handleColumnEvent, keypress: me.handleColumnEvent, scope: me, destroyable: true }); me.infoEl = me.innerCt.createChild({ cls: me.hintTextCls + ' ' + Ext.baseCSSPrefix + 'unselectable', html: me.groupingPanelText }); me.setInfoElVisibility(); me.initGroupingColumns(); }, show: function() { var me = this, dragZone = me.dragZone, dropZone = me.dropZone, grid = me.getGrid(); if (dragZone) { dragZone.enable(); } if (dropZone) { dropZone.enable(); } me.callParent(); grid.fireEvent('showgroupingpanel', me); }, hide: function() { var me = this, dragZone = me.dragZone, dropZone = me.dropZone, grid = me.getGrid(); if (dragZone) { dragZone.disable(); } if (dropZone) { dropZone.disable(); } me.callParent(); grid.fireEvent('hidegroupingpanel', me); }, updateGrid: function(grid) { var me = this, store = null; Ext.destroy(me.gridListeners); if (grid) { // catch this event to refresh the grouping columns in case one of them changed its name me.gridListeners = grid.on({ reconfigure: me.onGridReconfigure, scope: me, destroyable: true }); store = grid.getStore(); } me.setStore(store); }, updateStore: function(store) { var me = this; Ext.destroy(me.storeListeners); if (store) { me.storeListeners = store.on({ groupchange: me.initGroupingColumns, groupschange: me.initGroupingColumns, scope: me, destroyable: true }); me.initGroupingColumns(); } }, onAdd: function() { this.setInfoElVisibility(); }, onRemove: function() { this.setInfoElVisibility(); }, /** * The container has an info text displayed inside. This function makes it visible or hidden. * * @private */ setInfoElVisibility: function() { var el = this.infoEl; if (!el) { return; } if (!this.items.length) { el.show(); } else { el.hide(); } }, handleColumnEvent: function(e) { var isKeyEvent = this.keyEventRe.test(e.type), fly, cmp; if ((isKeyEvent && e.getKey() === e.SPACE) || (e.button === 0)) { fly = Ext.fly(e.target); if (fly && (cmp = fly.component) && cmp.isGroupingPanelColumn) { this.showColumnMenu(e, cmp); } } }, showColumnMenu: function(e, target) { var me = this, grid = me.getGrid(), menu, options; Ext.destroy(me.columnMenu); menu = me.columnMenu = Ext.menu.Manager.get(me.getColumnMenu(target)); options = { menu: menu, column: target }; if (grid.fireEvent('beforeshowgroupingcolumnmenu', me, options) !== false) { menu.showBy(target); menu.focus(); grid.fireEvent('showgroupingcolumnmenu', me, options); } else { Ext.destroy(menu); } e.stopEvent(); }, getColumnMenu: function(target) { var me = this, items = [], owner = target.ownerCt, sibling; items.push({ text: me.sortAscText, direction: 'ASC', iconCls: me.ascSortIconCls, column: target, handler: me.sortColumn }, { text: me.sortDescText, direction: 'DESC', iconCls: me.descSortIconCls, column: target, handler: me.sortColumn }, { xtype: 'menuseparator' }, { text: me.removeText, handler: Ext.bind(me.removeColumn, me, [target]) }, { text: me.moveLeftText, disabled: !(sibling = target.previousSibling()), handler: Ext.bind(me.moveColumn, me, [target, sibling, 'before']) }, { text: me.moveRightText, disabled: !(sibling = target.nextSibling()), handler: Ext.bind(me.moveColumn, me, [target, sibling, 'after']) }, { text: me.moveBeginText, disabled: !(sibling = target.previousSibling()), handler: Ext.bind(me.moveColumn, me, [target, owner.items.first(), 'before']) }, { text: me.moveEndText, disabled: !(sibling = target.nextSibling()), handler: Ext.bind(me.moveColumn, me, [target, owner.items.last(), 'after']) }); return { defaults: { scope: me }, items: items }; }, showPanelMenu: function(e, target) { var me = this, grid = me.getGrid(), isKeyEvent = me.keyEventRe.test(e.type), menu, options; Ext.destroy(me.panelMenu); target.focus(); menu = me.panelMenu = Ext.menu.Manager.get(me.getPanelMenu()); options = { menu: menu }; if (grid.fireEvent('beforeshowgroupingpanelmenu', me, options) !== false) { if (isKeyEvent) { menu.showBy(target); } else { menu.show(); menu.setPosition(e.getX(), e.getY()); } menu.focus(); grid.fireEvent('showgroupingpanelmenu', me, options); } else { Ext.destroy(menu); } e.stopEvent(); }, getPanelMenu: function() { var me = this, items = [], groupers = me.getStore().getGroupers(); items.push({ iconCls: me.groupingPanelIconCls, text: me.hideGroupingPanelText, handler: me.hide }, { iconCls: me.clearGroupIconCls, text: me.clearGroupText, disabled: !groupers || !groupers.length, handler: me.clearGrouping }); return { defaults: { scope: me }, items: items }; }, clearGrouping: function() { var me = this, items = me.items.items, length = items.length, i, item, column; Ext.suspendLayouts(); // make column headers visible in the grid for (i = 0; i < length; i++) { item = items[i]; column = item.getColumn(); if (column) { column.show(); } } me.getStore().group(null); me.getHeader().focus(); Ext.resumeLayouts(true); }, sortColumn: function(target) { var grouper = target.column.getGrouper(); if (grouper) { grouper.setDirection(target.direction); } }, /** * Check if the specified grid column is already added to the panel * * @param {Ext.grid.column.Column} col */ isNewColumn: function(col) { return this.items.findIndex('idColumn', col.id) < 0; }, addColumn: function(config, pos, notify) { var me = this, colConfig = Ext.apply({}, me.getColumnConfig()), newCol; Ext.suspendLayouts(); newCol = Ext.create(Ext.apply(colConfig, config)); if (pos !== -1) { me.insert(pos, newCol); } else { me.add(newCol); } if (notify === true) { newCol.focus(); me.notifyGroupChange(); } Ext.resumeLayouts(true); }, getColumnPosition: function(column, position) { var me = this, pos; if (column.isGroupingPanelColumn) { // we have to insert before or after this column pos = me.items.indexOf(column); pos = (position === 'before') ? pos : pos + 1; } else { pos = -1; } return pos; }, moveColumn: function(from, to, position) { var me = this; Ext.suspendLayouts(); if (from !== to) { if (position === 'before') { me.moveBefore(from, to); } else { me.moveAfter(from, to); } me.notifyGroupChange(); } Ext.resumeLayouts(true); }, removeColumn: function(column) { var me = this, col = column.getColumn(), sibling = column.nextSibling() || column.previousSibling() || me.getHeader(); Ext.suspendLayouts(); if (col) { col.show(); } if (sibling) { sibling.focus(); } me.remove(column, true); me.notifyGroupChange(); Ext.resumeLayouts(true); }, showGridColumn: function(col) { col.show(); }, hideGridColumn: function(col) { col.hide(); }, notifyGroupChange: function() { var me = this, store = me.getStore(), items = me.items.items, length = items.length, groupers = [], i, column, grouper; for (i = 0; i < length; i++) { column = items[i]; grouper = column.getGrouper(); if (grouper) { groupers.push(grouper); } } if (!groupers.length) { store.group(null); } else { store.setGroupers(groupers); } }, onGridReconfigure: function(grid, store) { if (store) { this.setStore(store); } }, /** * Check if the grid store has groupers and add them to the grouping panel */ initGroupingColumns: function() { var me = this, grid = me.getGrid(), store = me.getStore(), groupers = store.getGroupers(), length = groupers.length, columns = Ext.Array.toValueMap(grid.headerCt.getGridColumns(), 'dataIndex'), i, column, grouper; Ext.suspendLayouts(); // remove all previously created columns me.removeAll(true); for (i = 0; i < length; i++) { grouper = groupers.items[i]; column = columns[grouper.getProperty()]; if (column) { me.addColumn({ header: column.text, idColumn: column.id, grouper: grouper, column: column }, -1); } } Ext.resumeLayouts(true); }});