UNPKG

@coon-js/extjs-lib-comp

Version:

NPM package providing component library for ExtJS projects

490 lines (396 loc) 12.1 kB
/** * coon.js * extjs-lib-comp * Copyright (C) 2017-2021 Thorsten Suckow-Homberg https://github.com/coon-js/extjs-lib-comp * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** * Grid Feature which renders a menu on top of a grid row. Best used with * grid configurations with enabled rowBodies. * * * @example * * Ext.define('MyGrid', { * * extend : 'Ext.grid.Panel', * * features : [{ * ftype : 'cn_comp-gridfeature-rowflymenu', * id : 'cn_mail-mailMessageFeature-rowFlyMenu', * items : [{ * cls : 'fa fa-envelope-o', * title : 'Mark as Unread', * action : 'markunread', * id : 'cn_mail-mailMessageFeature-rowFlyMenu-markUnread' * }], * alignTo : ['tr-tr', [-12, 4]] * }], * * store : { * fields : ['isRead', 'subject', 'to'], * data : [{ * isRead : false, subject : 'Subject 1', to : 'to 1' * }, { * isRead : true, subject : 'Subject 2', to : 'to 2' * }, { * isRead : false, subject : 'Subject 3', to : 'to 3' * }], * }, * * columns : [{ * text : 'Read', * dataIndex : 'isRead', * }, { * text : 'Subject', * dataIndex : 'subject' * }, { * text : 'To', * dataIndex : 'to' * }] * * }); * * var myGrid = Ext.create('MyGrid', { * renderTo : document.body * )); * * Example css working with RowFlyMenu: * * .cn_comp-rowflymenu { * * position : absolute; * z-index : 100000; * background-color:inherit; * * .cn-item { * margin : 4px 2px 2px 6px; * cursor : pointer; * float : left; * * &.cn-over { * color : grey; * } * } * * } * */ Ext.define("coon.comp.grid.feature.RowFlyMenu", { extend: "Ext.grid.feature.Feature", alias: "feature.cn_comp-gridfeature-rowflymenu", cls: "cn_comp-rowflymenu", /** * Gets firec when an item of this menu was clicked that was configured * with an action. * @event itemclick * @param this * @param {HtmlElement} item * @param {String} action * @param {Ext.data.Model} record The record for which the menu was shown */ /** * Gets fired when this menu is about to get shown. * @event beforemenushow * @param this * @param {HtmlElement} item * @param {Ext.data.Model} record The record for which the menu will be shown */ /** * Maps item-ids to their actions. * @type {Object} * @private */ idToActionMap: null, /** * The menu that gets created from #items for this Feature. * @type {Ext.Element} * @private */ menu: null, /** * @cfg {Array} An array of element-configurations representing the items * for the RowFlyMenu. See #processItems * @cfg {Array} items */ items: null, /** * Lets you specify how the RowFlyMenu gets aligned to the rowitem for which * it is being shown. If configured as an array, the array should comply * with the specificatiosn of the alignTo()-method. * @cfg {String/Array} alignTo */ alignTo: "tr-tr", /** * The record represented by the grid row for which the menu is currently * shown. * @tape {Ext.data.Model} * @private */ currentRecord: null, /** * @inheritdoc */ init: function (grid) { const me = this; me.menu = me.buildMenu(me.items); me.mon(me.menu, { "tap": me.onMenuClick, "mouseover": me.onMenuMouseOver, "mouseout": me.onMenuMouseOut, scope: me }); delete me.items; me.installListeners(grid); me.callParent(arguments); }, /** * Callback for the menu's mouseover event. * * @param {Ext.Event}evt * @param {HtmlElement} target */ onMenuMouseOver: function (evt, target) { if (target.className.indexOf("cn-item") !== -1) { this.grid.on("beforeselect", this.preventSelection); Ext.fly(target).addCls("cn-over"); } }, /** * Callback for the menu's mouseout event. * * @param {Ext.Event}evt * @param {HtmlElement} target */ onMenuMouseOut: function (evt, target) { if (target.className.indexOf("cn-item") !== -1) { this.grid.un("beforeselect", this.preventSelection); Ext.fly(target).removeCls("cn-over"); } }, /** * Callback for the tap-event of this feature's menu. Will try to mao the * event-source's id to an action (if specified for #items). If an action was * found, the event #itemclick is fired with appropriate information. * * @param {Ext.Event} evt * @param {HtmlElement} target */ onMenuClick: function (evt, target) { const me = this, idToAction = me.idToActionMap, action = idToAction[target.id]; evt.stopEvent(); if (!action) { return; } me.fireEvent("itemclick", me, target, action, me.currentRecord); }, /** * Callback for the itemmouseenter-event of this feature's grid. Makes sure * the menu is shown and aligned properly to this event's HTML-source (item). * Does nothing if this feature is disabled. * Triggers the #beforemenushow-event. If any listener returns false, te * menu will not get shown. * * @param {Ext.view.Table} view * @param {Ext.data.Model} record * @param {HtmlElement} item * @param {Number} index * @param {Ext.Event} e * @param {Object} eOpts * * @private */ onItemMouseEnter: function (view, record, item, index, e, eOpts) { const me = this; if (me.disabled) { return; } e.stopEvent(); me.currentRecord = record; if (me.fireEvent("beforemenushow", me, item, record) === false) { me.currentRecord = null; return; } Ext.fly(item).appendChild(me.menu); me.menu.show(); me.menu.alignTo.apply(me.menu, [item].concat(me.alignTo)); }, /** * Callback for the itemmouseleave-event of this feature's grid. Makes sure * the menu is hidden again. * Does nothing if this feature is disabled. * * @param {Ext.view.Table} view * @param {Ext.data.Model} record * @param {HtmlElement} item * @param {Number} index * @param {Ext.Event} e * @param {Object} eOpts * * @private * * @see detachMenuAndUnset */ onItemMouseLeave: function (view, record, item, index, e, eOpts) { const me = this; if (me.disabled) { return; } e.stopEvent(); me.detachMenuAndUnset(); }, /** * Installs the callbacks for itemmouseenter- and itemmouseleave-events of * the specified grid. * * @param {Ext.grid.Panel} grid * * @private */ installListeners: function (grid) { const me = this; grid.view.on("beforerefresh", me.onBeforeGridViewRefresh, me); grid.on("itemmouseenter", me.onItemMouseEnter, me); grid.on("itemmouseleave", me.onItemMouseLeave, me); grid.on("beforeitemkeydown", me.onBeforeItemKeyDown, me); }, /** * Processes the specified items and creates an Ext.dom.Element wrapping the * native HTMLElement. The created Ext.Element's skipGarbageCollection-property * is explicitly set to true to prevent it from being removed by ExtJS' * (DOM-)GarbageCollector. * * @param {Array} items The items for this RowFlyMenu * * @return {Ext.dom.Element} * * @see #processItems * * @private */ buildMenu: function (items) { const me = this; let childs = me.processItems(items), el; let re = Ext.Element.create({ tag: "div", cls: me.cls, children: childs }, true); el = Ext.create("Ext.Element", re); el.skipGarbageCollection = true; return el; }, /** * Processes configuration items and returns them decorated with needed * informations for this class. * Fills the idToActionMap-object. * * @param {Array} items * * @private */ processItems: function (items) { const me = this; let childs = [], item; me.idToActionMap = {}; for (let i = 0, len = items.length; i < len; i++) { item = Ext.apply({}, items[i]); item.tag = item.tag ? item.tag : "div"; item.cls = item.cls ? "cn-item " + item.cls : "cn-item"; item.id = item.id ? item.id : Ext.id(); if (item.action) { me.idToActionMap[item.id] = item.action; delete item.action; } childs.push(item); } return childs; }, /** * @inheritdoc */ disable: function () { const me = this; me.detachMenuAndUnset(); me.callParent(arguments); }, /** * Callback for the grid view's beforerefresh-event. * Delegates to #detachMenu * * @private * * @see detachMenuAndUnset */ onBeforeGridViewRefresh: function () { const me = this; me.detachMenuAndUnset(); }, /** * Detaches the #menu from any parent it currently has and sets the * #currentRecord for this menu to null. * */ detachMenuAndUnset: function () { const me = this, menu = me.menu; if (menu.dom.parentNode) { Ext.fly(menu.dom.parentNode).removeChild(menu); } me.grid.un("beforeselect", me.preventSelection); me.currentRecord = null; }, /** * Makes sure #menu gets destroyed. * * @inheritdoc */ destroy: function () { const me = this; if (me.menu) { me.menu.destroy(); me.menu = null; } return me.callParent(arguments); }, /** * Named helper function used by the beforeselect-listener of this menu. * Makes sure no grid item is selected when the menu is clicked. * * @returns {boolean} false * * @private */ preventSelection: function () { return false; }, /** * Named helper function to make sure itemkeydown invokes detachMenuAndUnset * to make sure #preventSelection does not block key navigation and selection. * * @private */ onBeforeItemKeyDown: function () { this.detachMenuAndUnset(); } });