You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
158 lines
3.9 KiB
158 lines
3.9 KiB
1 year ago
|
/*
|
||
|
** Zabbix
|
||
|
** Copyright (C) 2001-2023 Zabbix SIA
|
||
|
**
|
||
|
** This program is free software; you can redistribute it and/or modify
|
||
|
** it under the terms of the GNU General Public License as published by
|
||
|
** the Free Software Foundation; either version 2 of the License, or
|
||
|
** (at your option) any later version.
|
||
|
**
|
||
|
** This program is distributed in the hope that it will be useful,
|
||
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
** GNU General Public License for more details.
|
||
|
**
|
||
|
** You should have received a copy of the GNU General Public License
|
||
|
** along with this program; if not, write to the Free Software
|
||
|
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
|
**/
|
||
|
|
||
|
|
||
|
const MENU_EXPAND_SELECTED_DELAY = 5000;
|
||
|
|
||
|
const MENU_EVENT_BLUR = 'blur';
|
||
|
const MENU_EVENT_EXPAND = 'expand';
|
||
|
const MENU_EVENT_FOCUS = 'focus';
|
||
|
|
||
|
class CMenu extends CBaseComponent {
|
||
|
|
||
|
constructor(target, level) {
|
||
|
super(target);
|
||
|
|
||
|
this.init(level || 0);
|
||
|
this.registerEvents();
|
||
|
}
|
||
|
|
||
|
init(level) {
|
||
|
this._expanded_item = null;
|
||
|
this._selected_item = null;
|
||
|
this._items = [];
|
||
|
this._level = level;
|
||
|
|
||
|
for (const el of this._target.childNodes) {
|
||
|
const item = new CMenuItem(el, this._level);
|
||
|
if (item.isExpanded()) {
|
||
|
this._expanded_item = item;
|
||
|
}
|
||
|
if (item.isSelected()) {
|
||
|
this._selected_item = item;
|
||
|
}
|
||
|
this._items.push(item);
|
||
|
}
|
||
|
|
||
|
this.hasClass('submenu') && this.updateHeight();
|
||
|
}
|
||
|
|
||
|
getItems() {
|
||
|
return this._items;
|
||
|
}
|
||
|
|
||
|
getLevel() {
|
||
|
return this._level;
|
||
|
}
|
||
|
|
||
|
collapseExpanded(from_level) {
|
||
|
if (this._expanded_item !== null && this._expanded_item.collapseSubmenu(from_level)) {
|
||
|
this._expanded_item = null;
|
||
|
}
|
||
|
|
||
|
return this._level > (from_level || 0);
|
||
|
}
|
||
|
|
||
|
expandSelected(till_level) {
|
||
|
if (this._level < till_level && this._selected_item !== null && this._selected_item !== this._expanded_item) {
|
||
|
this.collapseExpanded();
|
||
|
this._selected_item.hasSubmenu() && this._selected_item.expandSubmenu(till_level);
|
||
|
}
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
focusSelected(till_level) {
|
||
|
if (this._selected_item !== null) {
|
||
|
if (this._selected_item.hasSubmenu() && this._level < till_level) {
|
||
|
this.expandSelected(till_level);
|
||
|
this._selected_item.getSubmenu().focusSelected(till_level);
|
||
|
}
|
||
|
else {
|
||
|
this._selected_item.focus();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
getExpanded() {
|
||
|
return this._expanded_item;
|
||
|
}
|
||
|
|
||
|
getSelected() {
|
||
|
return this._selected_item;
|
||
|
}
|
||
|
|
||
|
updateHeight() {
|
||
|
this._target.style.maxHeight = `${this._target.scrollHeight}px`;
|
||
|
}
|
||
|
|
||
|
updateRect(relative_item, limit) {
|
||
|
const r_rect = relative_item.getBoundingClientRect();
|
||
|
|
||
|
limit = Object.assign({top: 0, bottom: 0}, limit || {});
|
||
|
|
||
|
this._target.style.top = `${Math.max(limit.top,
|
||
|
Math.min(r_rect.y, window.innerHeight - this._target.scrollHeight - limit.bottom)
|
||
|
)}px`;
|
||
|
this._target.style.left = `${r_rect.x + r_rect.width}px`;
|
||
|
this._target.style.maxWidth = `${this._target.scrollWidth}px`;
|
||
|
this._target.style.maxHeight = `${this._target.scrollHeight}px`;
|
||
|
|
||
|
if (this._expanded_item && this._expanded_item.hasSubmenu()) {
|
||
|
this._expanded_item.getSubmenu().updateRect(this._expanded_item._target, limit);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Register all DOM events.
|
||
|
*/
|
||
|
registerEvents() {
|
||
|
this._events = {
|
||
|
|
||
|
focus: (e) => {
|
||
|
if (!this._target.contains(e.relatedTarget)) {
|
||
|
this.fire((e.type === 'focusin') ? MENU_EVENT_FOCUS : MENU_EVENT_BLUR);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
expand: (e) => {
|
||
|
this._expanded_item !== e.detail.target && this.collapseExpanded();
|
||
|
this._expanded_item = e.detail.target;
|
||
|
|
||
|
this.fire(MENU_EVENT_EXPAND, {menu_item: this._expanded_item});
|
||
|
},
|
||
|
|
||
|
collapse: () => {
|
||
|
this._expanded_item = null;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
this.on('focusin focusout', this._events.focus);
|
||
|
|
||
|
for (const item of this._items) {
|
||
|
if (item.hasSubmenu()) {
|
||
|
item.on(MENUITEM_EVENT_EXPAND, this._events.expand);
|
||
|
item.on(MENUITEM_EVENT_COLLAPSE, this._events.collapse);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|