UNPKG

@easyquery/ui

Version:

EasyQuery.JS Community UI widgets

1,158 lines (1,153 loc) 570 kB
'use strict'; var core$1 = require('@easydata/core'); var core = require('@easyquery/core'); var ui = require('@easydata/ui'); function findHighestZIndex(elem) { let highest = 0; while (elem !== null) { const zindex = document.defaultView.getComputedStyle(elem, null) .getPropertyValue("z-index"); if (zindex != 'auto') { const numZIndex = parseInt(zindex); if (numZIndex > highest) { highest = numZIndex; } } elem = elem.parentElement; } return highest; } // simetimes setting focus in the main thread doesn't work correctly (in Firefox). So we need to 'hack' it. function safeFocus(element) { setTimeout((el) => { if (el && el.focus) { el.focus(); } }, 100, element); } const eqCssPrefix = 'eqjs'; const eqCssMobile = 'eqjs-mobile'; function getMobileCssClass() { return ui.browserUtils.isMobileMode() ? eqCssMobile : null; } /** * The entities panel wiget, implemented without JQuery. */ class EntitiesPanel extends core.Widget { get cssPrefix() { return 'eqjs-ep'; } /** * The default constructor. * @param slot The html element. */ constructor(slot) { super(slot); this.queryChangedCallbackId = null; this.group = core.WidgetGroup.Model | core.WidgetGroup.Query; this.panel = slot; } getWidgetType() { return 'entitiesPanel'; } /** * Initialize widget. * @param context The context. * @param options The options. */ init(context, options) { super.init(context, options); this.setOptions(options); this.renderProgressBlock(); } /** * Refresh widget implementation */ refreshCore() { this.render(); if (this.options.syncWithColumns) { this.refreshCheckedStateByColumns(); } } onProcessStartCore() { if (this.options.showIndicatorOnLoad) { if (!this.progressBlock.parentNode) this.panel.appendChild(this.progressBlock); } } onProcessEndCore() { if (this.options.showIndicatorOnLoad) { if (this.progressBlock.parentNode) this.panel.removeChild(this.progressBlock); } } destroyCore() { this.slot.innerHTML = ''; } setOptions(options) { this.options = { showToolbar: true, syncWithColumns: false, showSelectAllButton: false, showClearSelectionButton: true, showAddColumnButton: true, showAddConditionButton: true, showCheckboxes: true, showTooltips: true, draggableAttributes: true, showAttributes: { useInConditions: true, useInResult: true, useInSorting: false }, showFilterBox: false, filterBoxMode: 0, showIndicatorOnLoad: true, attrPlacement: 0, sortEntities: false, autoClearSelection: true, entityRenderedCallback: null, attributeRenderedCallback: null, }; if (options) { core$1.utils.assignDeep(this.options, options); } if (this.queryChangedCallbackId) { const query = this.context.getQuery(); query.removeChangedCallback(this.queryChangedCallbackId); } if (this.options.syncWithColumns) { const query = this.context.getQuery(); this.queryChangedCallbackId = query.addChangedCallback((ev) => { const edata = ev.data; if (!edata || (edata.part != core.QueryChangePart.Columns && edata.part != core.QueryChangePart.All)) { return; } this.refreshCheckedStateByColumns(); }); } } render() { let model = this.context.getModel(); this.clear(); this.panel.classList.add(`${this.cssPrefix}-panel`); this.panel.classList.add(getMobileCssClass()); this.rootEntityBlock = document.createElement('div'); if (model != null && !model.isEmpty()) { let entTree = model.getEntitiesTree({ addUIC: this.options.showAttributes.useInConditions && !this.options.syncWithColumns, addUIR: this.options.showAttributes.useInResult, addUIS: this.options.showAttributes.useInSorting && !this.options.syncWithColumns, attrPlacement: this.options.attrPlacement, sortEntities: this.options.sortEntities, includeRootData: true }); this.entityTreeBlock = this.renderEntity(entTree, this.rootEntityBlock, 0); this.panel.appendChild(this.entityTreeBlock); } if (this.options.showFilterBox) { this.createFilterBox(); } else { this.rootEntityBlock.style.top = '0'; } if (this.options.showToolbar && !this.options.syncWithColumns) { this.createToolPanel(); } else { this.rootEntityBlock.style.bottom = '0'; } } renderEntity(entity, block, offset) { let entityBlock; let entityNode = document.createElement('div'); entityNode.classList.add(`${this.cssPrefix}-entity-node`); entityNode.classList.add(getMobileCssClass()); let entityChildren = document.createElement('div'); entityChildren.classList.add(`${this.cssPrefix}-entity-children`); entityChildren.classList.add(getMobileCssClass()); let nodeLabel = document.createElement('label'); nodeLabel.classList.add(`${this.cssPrefix}-entity-node-label`); let nodeInput = document.createElement('input'); nodeInput.type = 'checkbox'; nodeInput.title = entity.text; let nodeToggleButton = document.createElement('a'); nodeToggleButton.href = 'javascript:void(0)'; nodeToggleButton.title = `${core$1.i18n.getText("EntityToggle")} ${entity.text}`; nodeToggleButton.classList.add(`${this.cssPrefix}-entity-node-button`); let attrNode, attrLabel, attrInput; let currentOffset = offset; let isAttributeInTree = (attrId, entityBlock) => { let res = entityBlock.querySelectorAll(`.${this.cssPrefix}-entity-attr[data-id="${attrId}"]`); return res.length > 0; }; if (block) { entityBlock = block; entityBlock.innerHTML = ''; } else { entityBlock = document.createElement('div'); } entityBlock.classList.add(`${this.cssPrefix}-entity`); entityBlock.classList.add(getMobileCssClass()); if (entity.id && entity.id != "") { nodeLabel.innerText = entity.text; entityNode.appendChild(nodeLabel); //caption if (this.options.showCheckboxes) { nodeLabel.insertBefore(nodeInput, nodeLabel.firstChild); } entityNode.insertBefore(nodeToggleButton, entityNode.firstChild); if (this.options.showTooltips && entity.description) { nodeLabel.setAttribute('title', entity.description); } for (let i = 0; i < currentOffset; i++) { let divElem = document.createElement('div'); divElem.classList.add(`${this.cssPrefix}-entity-offset`); entityNode.insertBefore(divElem, entityNode.firstChild); } entityBlock.appendChild(entityNode); entityChildren.hidden = true; currentOffset++; } let itemsLength = entity.items ? entity.items.length : 0; for (let i = 0; i < itemsLength; i++) { let item = entity.items[i]; if (item.isEntity) { entityChildren.appendChild(this.renderEntity(item, null, currentOffset)); } else { if (!item.lookupAttr || !(isAttributeInTree(item.lookupAttr, entityChildren))) { attrLabel = document.createElement('label'); attrLabel.innerText = item.text; attrLabel.classList.add(`${this.cssPrefix}-entity-attr-label`); if (this.options.showCheckboxes) { attrInput = document.createElement('input'); attrInput.type = 'checkbox'; attrInput.title = item.text; attrLabel.insertBefore(attrInput, attrLabel.firstChild); if (this.options.syncWithColumns) { attrInput.addEventListener('change', (ev) => { const model = this.context.getModel(); const query = this.context.getQuery(); const checkbox = ev.currentTarget; const attrId = checkbox.parentElement.parentElement.getAttribute('data-id'); if (model.checkAttrProperty(attrId, "useInResult")) { if (checkbox.checked) { const attr = model.getAttributeById(attrId); const column = query.addColumn({ attribute: attr }, true); query.fireColumnsChangedEvent(core.QueryChangeAction.Add, column); } else { const columns = query.getColumns() .filter(col => col.expr.tag === core.ExprTag.EntityAttribute && col.expr.value === attrId); if (columns.length > 0) { query.removeColumn(columns[0]); query.fireColumnsChangedEvent(core.QueryChangeAction.Delete, columns[0]); } } } }); } } if (this.options.showTooltips && item.description) { attrLabel.setAttribute('title', item.description); } for (let j = 0; j < currentOffset + 1; j++) { let divElem = document.createElement('div'); divElem.classList.add(`${this.cssPrefix}-entity-offset`); attrLabel.insertBefore(divElem, attrLabel.firstChild); } attrNode = document.createElement('div'); attrNode.classList.add(`${this.cssPrefix}-entity-attr`); if (entityNode.innerHTML.length == 0) { attrNode.classList.add(`${this.cssPrefix}-entity-attr-root`); } attrNode.innerHTML = ""; attrNode.appendChild(attrLabel); attrNode.setAttribute('data-id', item.id); if (this.options.draggableAttributes && !this.options.syncWithColumns) { ui.eqDragManager.registerDraggableItem({ element: attrNode, scope: "entityAttr", data: { attrId: item.id }, renderer: (dragImage) => { dragImage.innerHTML = ''; const attrLabel = document.createElement('span'); attrLabel.innerText = item.text; dragImage.appendChild(attrLabel); } }); } if (this.options.attributeRenderedCallback) { this.options.attributeRenderedCallback(attrNode); } entityChildren.appendChild(attrNode); } } } if (entityChildren.innerHTML.length) { entityBlock.appendChild(entityChildren); nodeToggleButton.addEventListener('click', (event) => { entityChildren.hidden = !entityChildren.hidden; let element = event.target; let className = `${this.cssPrefix}-entity-node-button-open`; if (element.classList.contains(className)) { element.classList.remove(className); } else { element.classList.add(className); } event.preventDefault(); }); } if (entityChildren) { const checkboxes = entityChildren.querySelectorAll(`.${this.cssPrefix}-entity-attr > label > input`); for (let i = 0; i < checkboxes.length; i++) { const checkbox = checkboxes[i]; checkbox.addEventListener('click', (event) => { let element = event.target; if (element.checked && !nodeInput.checked) { nodeInput.checked = true; } else if (entityChildren.querySelectorAll('input:checked').length === 0) { nodeInput.checked = false; } event.stopPropagation(); }); } } nodeInput.addEventListener('click', (event) => { let element = event.target; const entities = entityChildren.querySelectorAll('label input'); for (let i = 0; i < entities.length; i++) { const entity = entities[i]; entity.checked = element.checked; } if (this.options.syncWithColumns) { const query = this.context.getQuery(); const model = this.context.getModel(); const attrList = []; const attrElems = Array.from(entityChildren.querySelectorAll(`.${this.cssPrefix}-entity-attr`)); for (const elem of attrElems) { const attrId = elem.getAttribute('data-id'); if (model.checkAttrProperty(attrId, 'useInResult')) { attrList.push(attrId); } } if (element.checked) { for (const attrId of attrList) { query.addColumn({ attributeId: attrId }, true); } } else { const columns = query.getColumns() .filter(col => col.expr.tag === core.ExprTag.EntityAttribute && attrList.indexOf(col.expr.value) >= 0); query.removeColumns(columns, null); } query.fireColumnsChangedEvent(); } event.stopPropagation(); }); if (block != this.rootEntityBlock && this.options.entityRenderedCallback) { this.options.entityRenderedCallback(entityBlock); } return entityBlock; } createFilterBox() { this.filterBoxBlock = document.createElement('div'); this.filterBoxBlock.classList.add(`${this.cssPrefix}-filter-box`); this.filterBoxBlock.classList.add(getMobileCssClass()); this.filterBoxInput = document.createElement('input'); this.filterBoxInput.classList.add(`${this.cssPrefix}-filter-box-input`); this.filterBoxInput.addEventListener('input', (e) => { const attrs = this.panel.querySelectorAll(`.${this.cssPrefix}-entity-attr`); for (let i = 0; i < attrs.length; i++) { const attr = attrs[i]; attr.hidden = !this.checkFilterAttribute(attr); } const entities = this.panel.querySelectorAll(`:scope > .${this.cssPrefix}-entity`); for (let i = 0; i < entities.length; i++) { const entity = entities[i]; let attrs = entity.querySelectorAll(`.${this.cssPrefix}-entity-attr`); let displayAttrCount = 0; for (let i = 0; i < attrs.length; i++) { if (attrs[i].style.display !== 'None') { displayAttrCount++; } } entity.hidden = displayAttrCount === 0; } if (this.filterBoxInput.value === '') this.collapseAll(); else this.expandAll(); }); this.filterBoxBlock.appendChild(this.filterBoxInput); this.panel.insertBefore(this.filterBoxBlock, this.panel.firstChild); } createToolPanel() { let defClass = `${this.cssPrefix}-tool-panel`; let toolPanelBlock = document.createElement('div'); toolPanelBlock.classList.add(defClass); toolPanelBlock.classList.add(getMobileCssClass()); let toolSelectAll = document.createElement('div'); toolSelectAll.classList.add(`${defClass}-select-all`); toolSelectAll.title = 'Select all'; let toolDeselectAll = document.createElement('div'); toolDeselectAll.classList.add(`${defClass}-deselect-all`); toolDeselectAll.title = 'Clear selection'; let toolAddColumns = document.createElement('div'); toolAddColumns.classList.add(`${defClass}-add-columns`); toolAddColumns.title = 'Add column'; let toolAddCond = document.createElement('div'); toolAddCond.classList.add(`${defClass}-add-cond`); toolAddCond.title = 'Add condition'; let toolLeftBlock = document.createElement('div'); toolLeftBlock.classList.add(`${defClass}-left-side`); let toolRightBlock = document.createElement('div'); toolRightBlock.classList.add(`${defClass}-right-side`); let model = this.context.getModel(); if (this.options.showSelectAllButton) { toolSelectAll.title = core$1.i18n.getText("ButtonSelectAll"); toolLeftBlock.appendChild(toolSelectAll); toolSelectAll.addEventListener('click', () => { this.selectAll(); }); } if (this.options.showClearSelectionButton) { toolDeselectAll.title = core$1.i18n.getText("ButtonDeselectAll"); toolLeftBlock.appendChild(toolDeselectAll); toolDeselectAll.addEventListener('click', () => { this.deselectAll(); }); } if (this.options.showAddColumnButton) { toolAddColumns.title = core$1.i18n.getText("ButtonAddColumns"); toolRightBlock.appendChild(toolAddColumns); toolAddColumns.addEventListener('click', () => { const attrElements = this.panel.querySelectorAll(`.${this.cssPrefix}-entity-attr`); let attrList = []; let attrId; const query = this.context.getQuery(); query.fireProcessEvent({ source: self, status: "start" }); for (let i = 0; i < attrElements.length; i++) { const attrElement = attrElements[i]; let input = attrElement.getElementsByTagName('input')[0]; if (input.checked) { attrId = attrElement.getAttribute('data-id'); if (model.checkAttrProperty(attrId, "useInResult")) { attrList.push(attrId); } } } for (let attrId of attrList) { const attr = model.getAttributeById(attrId); query.addColumn({ attribute: attr }, true); } query.fireChangedEvent(); if (this.options.autoClearSelection) { this.deselectAll(); } query.fireProcessEvent({ source: self, status: "finish" }); }); } if (this.options.showAddConditionButton) { toolAddCond.title = core$1.i18n.getText("ButtonAddConditions"); toolRightBlock.appendChild(toolAddCond); toolAddCond.addEventListener('click', () => { let attrElements = this.panel.querySelectorAll(`.${this.cssPrefix}-entity-attr`); let attrList = []; let attrId; let query = this.context.getQuery(); query.fireProcessEvent({ source: self, status: "start" }); for (let i = 0; i < attrElements.length; i++) { const attrElement = attrElements[i]; let input = attrElement.getElementsByTagName('input')[0]; if (input.checked) { attrId = attrElement.getAttribute('data-id'); if (model.checkAttrProperty(attrId, "useInConditions")) { attrList.push(attrId); } } } for (let attrId of attrList) { query.addSimpleCondition({ attributeId: attrId }); } query.fireChangedEvent(); if (this.options.autoClearSelection) { this.deselectAll(); } query.fireProcessEvent({ source: self, status: "finish" }); }); } toolPanelBlock.appendChild(toolLeftBlock); toolPanelBlock.appendChild(toolRightBlock); this.panel.appendChild(toolPanelBlock); } checkFilterAttribute(attr) { const inputElem = attr.querySelector('label input'); if (inputElem.checked) { return true; } const labelElem = attr.querySelector('label'); const attrCaption = labelElem.textContent; if (this.checkFilterText(attrCaption)) { return true; } const entityNode = attr.parentElement.parentElement.querySelector(`.${this.cssPrefix}-entity-node`); const entityCaption = entityNode.querySelector('label').textContent; if (this.checkFilterText(entityCaption)) { return true; } return false; } checkFilterText(value) { let filterText = this.filterBoxInput.value; if (filterText == "") return true; if (this.options.filterBoxMode == 0 && value.toLowerCase().indexOf(filterText.toLowerCase()) >= 0) return true; if (this.options.filterBoxMode == 1 && value.toLowerCase().indexOf(filterText.toLowerCase()) == 0) return true; return false; } refreshCheckedStateByColumns() { this.deselectAll(); const query = this.context.getQuery(); const exprColumns = query.getColumns() .filter(col => col.expr.tag === core.ExprTag.EntityAttribute); for (const column of exprColumns) { const attrNode = this.rootEntityBlock .querySelector(`.${this.cssPrefix}-entity-attr[data-id="${column.expr.value}"]`); let checkbox = attrNode.querySelector(`label > input`); checkbox.checked = true; let entChildren = attrNode.parentElement; while (entChildren && entChildren.classList .contains(`${this.cssPrefix}-entity-children`)) { entChildren.hidden = false; const entElem = entChildren.parentElement; if (entElem && entChildren !== entElem.firstElementChild) { checkbox = entElem.firstElementChild .querySelector(`label > input`); checkbox.checked = true; entChildren = entElem.parentElement; } else { break; } } } } expandAll() { for (let i = 0; i < this.rootEntityBlock.childNodes.length; i++) { const child = this.rootEntityBlock.childNodes[0]; const childs = child.querySelectorAll(`.${this.cssPrefix}-entity-children`); for (let j = 0; j < childs.length; j++) { const elem = childs[j]; elem.hidden = false; } } const nodeButtons = this.rootEntityBlock.querySelectorAll(`.${this.cssPrefix}-entity-node-button`); for (let i = 0; i < nodeButtons.length; i++) { const elem = nodeButtons[i]; elem.classList.add(`${this.cssPrefix}-entity-node-button-open`); } } collapseAll() { for (let i = 0; i < this.rootEntityBlock.childNodes.length; i++) { const child = this.rootEntityBlock.childNodes[0]; const childs = child.querySelectorAll(`.${this.cssPrefix}-entity-children`); for (let j = 0; j < childs.length; j++) { const elem = childs[j]; elem.hidden = true; } } const nodeButtons = this.rootEntityBlock.querySelectorAll(`.${this.cssPrefix}-entity-node-button`); for (let i = 0; i < nodeButtons.length; i++) { const elem = nodeButtons[i]; elem.classList.remove(`${this.cssPrefix}-entity-node-button-open`); } } selectAll() { const inputs = this.entityTreeBlock.querySelectorAll('input'); for (let i = 0; i < inputs.length; i++) { const input = inputs[i]; input.checked = true; } } deselectAll() { const inputs = this.entityTreeBlock.querySelectorAll('input'); for (let i = 0; i < inputs.length; i++) { const input = inputs[i]; input.checked = false; } } renderProgressBlock() { this.progressBlock = document.createElement('div'); this.progressBlock.classList.add(`${eqCssPrefix}-progress-win8`); this.progressBlock.classList.add(getMobileCssClass()); this.progressBlock.innerHTML = '<div class="wBall" id="wBall_1"><div class="wInnerBall"></div></div><div class="wBall" id="wBall_2"><div class="wInnerBall"></div></div><div class="wBall" id="wBall_3"><div class="wInnerBall"></div></div><div class="wBall" id="wBall_4"><div class="wInnerBall"></div></div><div class="wBall" id="wBall_5"><div class="wInnerBall"></div></div>'; } clear() { this.panel.innerHTML = ""; } } exports.ColumnsEditMode = void 0; (function (ColumnsEditMode) { /** * ReadOnly edit mode allows to view columns but doesn't allow to change them. */ ColumnsEditMode[ColumnsEditMode["ReadOnly"] = 0] = "ReadOnly"; /** * FixedList edit mode allows to edit some columns parameters (formats, sorting) and change their order * but doesn't allow to add or delete columns. */ ColumnsEditMode[ColumnsEditMode["FixedList"] = 16] = "FixedList"; /** * Full edit mode allows to add, delete and change columns in any way. */ ColumnsEditMode[ColumnsEditMode["Full"] = 255] = "Full"; })(exports.ColumnsEditMode || (exports.ColumnsEditMode = {})); exports.ConditionsEditMode = void 0; (function (ConditionsEditMode) { /** * No edit mode means that conditions are not editable at all. */ ConditionsEditMode[ConditionsEditMode["Default"] = 0] = "Default"; /** * ReadOnly edit mode allows to view conditions but doesn't allow to change them, including the condition type, operator, value, etc. */ ConditionsEditMode[ConditionsEditMode["ReadOnly"] = 1] = "ReadOnly"; /** * FixedList edit mode allows to modify some parameters of conditions (like their operators or values) but doesn't allow to add or delete conditions. */ ConditionsEditMode[ConditionsEditMode["FixedList"] = 16] = "FixedList"; /** * Full edit mode allows to add, delete and change conditions in any way. */ ConditionsEditMode[ConditionsEditMode["Full"] = 255] = "Full"; })(exports.ConditionsEditMode || (exports.ConditionsEditMode = {})); class MenuLevel { get applyItem() { return this._applyItem; } get cssPrefix() { return 'eqjs-menu'; } constructor(options) { this.isFilteringMode = false; this.showSelected = false; //menu, items, levelIndex this.parentMenu = options.menu || null; this.parentLevel = options.parent || null; this.parentElement = options.container || document.body; this.levelIndex = options.levelIndex || 0; this.levelDiv = null; this.domWriteItemsId = options.domWriteItemsId || false; this.menuId = options.menuId || ''; this.itemRenderedCallback = options.itemRenderedCallback || null; //we need to define special "apply" item for this level this._applyItem = { id: null, text: null, itemDiv: null }; this.items = options.items || []; this.activeItem = null; this.selectedItem = null; this.initialized = false; this.showSelected = options.showSelected; this.updated = 0; this.renderContent(); } getItems() { return this.items; } setItems(items) { this.items = items; } renderContent() { if (!this.items) { return; } //define internal variables used in this function const itemBgColor = this.parentMenu.style.colors.bgON || "white"; const itemFgColor = this.parentMenu.style.colors.fgON || "black"; this.parentMenu.style.colors.bgOVER || "LightSteelBlue"; const itemFontFamily = this.parentMenu.style.itemStyle.fontFamily || ""; const itemFontSize = this.parentMenu.style.itemStyle.fontSize || "14px"; const multiselect = this.parentMenu.options.multiselect; const isSubQuery = this.parentMenu.options.isSubQuery; //add base DIV element which is also used to show the shadow const baseDivBuilder = ui.domel('div') .addClass(getMobileCssClass()); if (this.parentMenu.options.useDefaultStyles) { baseDivBuilder .setStyle('backgroundColor', itemBgColor) .setStyle('border', '1px solid') .setStyle('borderColor', itemBgColor) .setStyle('backgroundColor', this.parentMenu.style.colors.border) .setStyle('margin', '-2px 2px 2px -2px') .setStyle('width', 'auto') .setStyle('height', 'auto'); } baseDivBuilder .setStyle('z-index', this.parentMenu.zIndex) .setStyle('position', 'absolute') .setStyle('display', 'none'); let baseDiv = baseDivBuilder.toDOM(); baseDiv['menuLevel'] = this; const applyItem = this._applyItem; //if multiselect option is on - then we should add special "apply" item if (multiselect && this.levelIndex === 0 && this.parentMenu.options.hideButtons !== true) { this.applyDiv = document.createElement("div"); this.applyDiv.classList.add(`${this.cssPrefix}-applyDiv`); this.applyDiv['menuItem'] = applyItem; this.applyBtn = document.createElement('button'); if (this.parentMenu.options.useDefaultStyles) { this.applyDiv.style.borderBottom = '1px solid'; this.applyDiv.style.padding = '5px'; this.applyDiv.style.marginBottom = '5px'; this.applyBtn.style.padding = '0 5px'; this.applyBtn.style.cursor = "pointer"; } const applyTextNode = document.createTextNode(this.parentMenu.options.buttons.submit); this.applyBtn.appendChild(applyTextNode); this.applyDiv.appendChild(this.applyBtn); //cancel btn const cancelBtn = document.createElement('button'); cancelBtn.classList.add(`${this.cssPrefix}-cancel`); if (this.parentMenu.options.useDefaultStyles) { cancelBtn.style.padding = '0 5px'; cancelBtn.style.cursor = 'pointer'; cancelBtn.style.marginLeft = '15px'; } const cancelText = document.createTextNode(this.parentMenu.options.buttons.cancel); cancelBtn.appendChild(cancelText); this.applyDiv.appendChild(cancelBtn); baseDiv.appendChild(this.applyDiv); applyItem.itemDiv = this.applyDiv; this.applyBtn.addEventListener('click', () => { this.submit(this._applyItem); }); cancelBtn.addEventListener('click', () => { this.parentMenu.hideMenu(); }); } // if too many items - then we add a special "search" item // or there is an options to show always if (this.parentLevel == null && (this.parentMenu.options.searchBoxAlwaysShown || this.items.length >= this.parentMenu.options.showSearchBoxAfter)) { const searchDivBuilder = ui.domel('div') .addClass(`${this.cssPrefix}-searchDiv`); if (this.parentMenu.options.useDefaultStyles) { searchDivBuilder .setStyle('border-bottom', '1px solid #666') .setStyle('background-color', itemBgColor) .setStyle('border-color', this.parentMenu.style.colors.border); if (itemFontFamily != "") { searchDivBuilder.setStyle('font-family', itemFontFamily); } searchDivBuilder .setStyle('font-size', itemFontSize) .setStyle('color', itemFgColor) .setStyle('cursor', 'pointer') .setStyle('text-align', 'left') .setStyle('padding', '5px'); } const searchEditBoxBuilder = ui.domel('input') .id('searchBox') .name('searchBox') .type('text') .size(16) .addClass(`${this.cssPrefix}-searchBox`) .on('input', (ev) => { this.deactivateItem(this.activeItem); this.renderItems(this.searchBox.value); }) .on('keydown', (ev) => { let keyCode = ev.keyCode; switch (keyCode) { case 13: //enter if (this.activeItem) { this.activeItem.itemDiv.click(); } break; case 38: // up this.moveActiveItemUp(); break; case 40: // down this.moveActiveItemDown(); break; case 39: // right if (!this.searchBox.value) { this.openSubLevel(this.activeItem); } break; } }); if (this.parentMenu.options.useDefaultStyles) { searchEditBoxBuilder .setStyle('font-family', 'monospace') .setStyle('font-size', '8pt') .setStyle('width', '100%'); } if (isSubQuery) { searchEditBoxBuilder.addClass('eqjs-dialog'); } this.searchBox = searchEditBoxBuilder.toDOM(); this.searchDiv = searchDivBuilder.toDOM(); this.searchDiv.appendChild(this.searchBox); baseDiv.appendChild(this.searchDiv); } const scrollDiv = document.createElement("div"); scrollDiv.tabIndex = 1; ui.domel(scrollDiv) .addClass(`${this.cssPrefix}-scrollDiv`) .addClass(getMobileCssClass()); scrollDiv.style.overflowX = "hidden"; scrollDiv.style.overflowY = "auto"; scrollDiv.style.position = "relative"; baseDiv.appendChild(scrollDiv); this.levelDiv = baseDiv; this.scrollDiv = scrollDiv; if (this.menuId) { this.levelDiv.id = this.menuId; } if (this.parentLevel) { this.levelDiv.style.zIndex = this.parentLevel.levelDiv.style.zIndex; } this.renderItems(); const scrollDivKeyDownHandler = (event) => { switch (event.which) { case 13: // enter if (this.parentMenu.options.multiselect) { this.parentMenu.getRootLevel().applyBtn.click(); } else { if (this.activeItem) { this.activeItem.itemDiv.click(); } } break; case 32: // space if (this.activeItem) { this.activeItem.itemDiv.click(); } break; case 37: // left if (this.parentLevel && !this.isFilteringMode) { this.deactivateItem(this.activeItem); this.parentLevel.focus(); } break; case 39: // right this.openSubLevel(this.activeItem); break; case 38: // up this.moveActiveItemUp(); break; case 40: // down this.moveActiveItemDown(); break; default: return; // exit this handler for other keys } event.preventDefault(); // prevent the default action (scroll / move caret) }; scrollDiv.addEventListener("keydown", scrollDivKeyDownHandler); if (ui.browserUtils.isMobileMode()) { this.closeDiv = ui.domel('div', baseDiv) .addClass('eqjs-menu-close-btn') .addText(core$1.i18n.getText('ButtonClose')) .on('click', () => { this.parentMenu.hideMenu(); }) .toDOM(); } } moveActiveItemDown() { const items = this.isFilteringMode ? this.filteredItems : this.items; if (this.activeItem) { const idx = items.indexOf(this.activeItem); if (idx < items.length - 1) { this.activateItem(items[idx + 1], false); } } else { this.activateItem(items[0], false); } } moveActiveItemUp() { const items = this.isFilteringMode ? this.filteredItems : this.items; if (this.activeItem) { const idx = items.indexOf(this.activeItem); if (idx > 0) { this.activateItem(items[idx - 1], false); } } else { this.activateItem(items[items.length - 1], false); } } openSubLevel(menuItem) { if (menuItem && menuItem.items && !this.isFilteringMode) { this.showSubLevel(menuItem); menuItem.subLevel.activateItem(menuItem.subLevel.items[0]); } } focusScrollDiv() { this.scrollDiv.focus(); } turnCheckboxes(items) { //turn checkboxes on for selected items for (let i = 0; i < items.length; i++) { const item = items[i]; if (item.itemCheckbox) item.itemCheckbox.checked = this.isItemSelected(item); if (item.items) { this.turnCheckboxes(item.items); } } } isItemSelected(item) { if (item.items) { for (var i = 0; i < item.items.length; i++) { if (this.isItemSelected(item.items[i])) { return true; } } return false; } else { return item.selected; } } setItemSelected(item, value) { if (item.items) { for (let i = 0; i < item.items.length; i++) { this.setItemSelected(item.items[i], value); } } else { item.selected = value; } } submitItems(items, selectedItems) { for (let j = 0; j < items.length; j++) { if (items[j].items) { this.submitItems(items[j].items, selectedItems); } else { if (items[j].selected) { selectedItems.push(items[j]); } } } } allSubitemsAreFiltered(items, filterCallback) { for (let i = 0; i < items.length; i++) { if (filterCallback(items[i])) return false; } return true; } isItemDiv(element) { return element['menuItem'] ? true : false; } isLevelDiv(element) { return element['menuLevel'] ? true : false; } getMenuItem(element) { let el = element; while (!this.isItemDiv(el)) { if (this.isLevelDiv(el)) { throw "Can't get menu item"; } el = el.parentElement; } return el['menuItem']; } renderItemsWithoutFilter() { this.isFilteringMode = false; const scrollDiv = this.scrollDiv; const multiselect = this.parentMenu.options.multiselect; const activateOnMouseOver = this.parentMenu.options.activateOnMouseOver; const itemFgColor = this.parentMenu.style.colors.fgON || 'black'; this.parentMenu.style.colors.bgOVER || 'LightSteelBlue'; const itemFontSize = this.parentMenu.style.itemStyle.fontSize || '14px'; const itemFilterCallback = this.parentMenu.getItemFilterCallback(); scrollDiv.innerHTML = ""; for (let i = 0; i < this.items.length; i++) { const item = this.items[i]; if (!item || !item.text) continue; if (itemFilterCallback) { if (!itemFilterCallback(item)) continue; if (item.items && this.allSubitemsAreFiltered(item.items, itemFilterCallback)) continue; } item.data = (propName) => { return this[propName]; }; if (typeof (item.selected) == "undefined") { item.selected = false; } if (item.selected && this.selectedItem == null) { this.selectedItem = item; } const itemDiv = document.createElement("div"); ui.domel(itemDiv) .addClass(`${this.cssPrefix}-itemDiv`) .addClass(getMobileCssClass()); if (item.selected && this.showSelected && !multiselect) { itemDiv.classList.add(`${this.cssPrefix}-selected`); } if (this.domWriteItemsId && this.menuId) { itemDiv.id = 'item-' + this.menuId + '-' + item.id; } scrollDiv.appendChild(itemDiv); itemDiv['menuItem'] = item; itemDiv['menuLevel'] = this; item.itemDiv = itemDiv; if (this.parentMenu.options.useDefaultStyles) { itemDiv.style.fontSize = itemFontSize; itemDiv.style.color = itemFgColor; itemDiv.style.paddingLeft = "15px"; itemDiv.style.paddingRight = "6px"; itemDiv.style.cursor = "pointer"; } if (item.text == '---') { itemDiv.appendChild(document.createElement('hr')); } else { if (multiselect) { var cb = document.createElement("input"); cb.type = "checkbox"; cb.id = "cb" + item.id; cb.checked = this.isItemSelected(item); cb.defaultChecked = this.isItemSelected(item); itemDiv.appendChild(cb); item.itemCheckbox = cb; if (this.parentMenu.options.useDefaultStyles) { cb.style.margin = "4px 10px 0 0"; cb.style.verticalAlign = "top"; } } let itemText = item.text; if (this.parentMenu.options.showItemIds) { itemText = item.id + ':' + itemText; } const itemTextNode = document.createTextNode(itemText); itemDiv.appendChild(itemTextNode); if (item.items && item.items.length > 0) { itemDiv.classList.add(`${this.cssPrefix}-itemDiv-hasChildren`); const arrowSpan = document.createElement('span'); arrowSpan.classList.add(`${this.cssPrefix}-itemDiv-arrow`); itemDiv.appendChild(arrowSpan); const arrowText = document.createTextNode('>'); arrowSpan.appendChild(arrowText); } const itemClickHandler = (ev) => { const menuItem = this.getMenuItem(ev.target); if (multiselect) { if (!menuItem.items || ev.target == menuItem.itemCheckbox) { const itemSelected = this.isItemSelected(menuItem); this.setItemSelected(menuItem, !itemSelected); menuItem.itemCheckbox.checked = !itemSelected; this.parentMenu.refreshCheckboxes(); } else { this.activateItem(menuItem); } } else { this.activateItem(menuItem); this.submit(menuItem); } return false; }; itemDiv.removeEventListener("click", itemClickHandler); itemDiv.addEventListener('click', itemClickHandler); itemDiv.addEventListener('mouseenter', (ev) => { const item = this.getMenuItem(ev.target); this.parentMenu.isCursorInside = true; if (activateOnMouseOver) { this.activateItem(item); } }); itemDiv.addEventListener('mouseleave', (ev) => { const item = this.getMenuItem(ev.target); this.parentMenu.isCursorInside = false; setTimeout(() => { if (!this.parentMenu.isCursorInside) { if (activateOnMouseOver && item == this.activeItem && !item.subLevel) { this.deactivateItem(item); } } }, 200); }); } if (this.itemRenderedCallback) { this.itemRenderedCallback(this.menuId, itemDiv); } } } renderItemsWithFilter(filter) { this.isFilteringMode = true; const scrollDiv = this.scrollDiv; scrollDiv.innerHTML = ""; const filterSections = filter.split('>'); const pureFilterTexts = filterSections.map(section => section.replace(/\*/g, '')); const items = this.filterItems(this.items, filterSections); this.filteredItems = []; for (let i = 0; i < items.length; i++) { this.renderItemWithFilter(items[i], pureFilterTexts, 0); } } matchesFilter(text, regexp) { if (text && regexp) { return regexp.test(text); } else { return true; } } filterItems(items, filterSections) { const resultItems = []; const filter = filterSections.length > 0 ? filterSections[0] : ''; const restFilterSections = filterSections.length > 1 ? filterSections.slice(1) : filterSections; const filterRegexp = filter ? new RegExp(filter.replace(/\*/g, '.+?'), 'i') : null; for (let i = 0; i < items.length; i++) { const item = items[i]; if (item.items) { const groupItem = core$1.utils.assign({}, item); const groupItemMatches = this.matchesFilter(item.text, filterRegexp); //if we have only one filter section (or an empty filter) if (filterSections.length < 2) { //if current group item matches the filter -> show all sub-items if (groupItemMatches && filterSections.length < 2) { groupItem.items = core$1.utils.createArrayFrom(item.items); } else { groupItem.items = this.filterItems(item.items, restFilterSections); } } //if we have more than 1 filter section and there is a match -> filter sub-items as well else if (groupItemMatches) { groupItem.items = this.filterItems