Files
mixly3-server/mixly/common/modules/mixly-modules/common/dropdown-menu-group.js
2026-01-24 16:12:04 +08:00

233 lines
6.9 KiB
JavaScript

goog.loadJs('common', () => {
goog.require('path');
goog.require('tippy');
goog.require('Mixly.Env');
goog.require('Mixly.Menu');
goog.require('Mixly.Registry');
goog.require('Mixly.HTMLTemplate');
goog.require('Mixly.IdGenerator');
goog.require('Mixly.ContextMenu');
goog.require('Mixly.DropdownMenu');
goog.provide('Mixly.DropdownMenuGroup');
const {
Env,
Menu,
Registry,
HTMLTemplate,
IdGenerator,
ContextMenu,
DropdownMenu
} = Mixly;
class DropdownMenuGroup {
static {
HTMLTemplate.add(
'html/dropdown-menu-item.html',
new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/dropdown-menu-item.html')))
);
}
#singleton_ = null;
#menuItems_ = [];
#ids_ = {};
#instanceIds_ = {};
#activeInstance_ = null;
#instanceShown_ = false;
#menuShown_ = false;
#trigged_ = false;
#$instancePopper_ = null;
#$instanceContent_ = null;
#$content_ = null;
constructor(elem) {
this.#$content_ = $(elem);
this.#$content_.css('z-index', 200);
this.#singleton_ = tippy.createSingleton([], {
interactive: true,
maxWidth: 'none',
offset: [0, 3],
appendTo: document.body,
arrow: false,
placement: 'bottom-end',
animation: 'shift-toward-extreme',
hideOnClick: false,
delay: [200, null],
onShow: () => {
if (this.#activeInstance_) {
this.showMenu(this.#activeInstance_.id);
}
this.#instanceShown_ = true;
},
onTrigger: (_, event) => {
const id = $(event.currentTarget).attr('data-id');
if (this.#instanceShown_) {
if (this.#activeInstance_) {
this.#trigged_ = true;
this.hideMenu(this.#activeInstance_.id);
this.#activeInstance_ = null;
}
this.showMenu(id);
}
this.#activeInstance_ = this.#instanceIds_[id].instance;
},
onHide: () => {
if (this.#menuShown_) {
return false;
}
this.#instanceShown_ = false;
return true;
}
});
this.#$instancePopper_ = $(this.#singleton_.popper);
this.#$instancePopper_.addClass('mixly-drapdown-menu');
this.#$instanceContent_ = this.#$instancePopper_.children().children();
}
add(item) {
if (!item.id) {
if (item.type) {
item.id = item.type;
} else {
item.id = IdGenerator.generate();
}
}
if (!item.weight) {
item.weight = 0;
}
this.remove(item.id);
item.$elem = $(HTMLTemplate.get('html/dropdown-menu-item.html').render({
text: item.displayText
}));
const instance = tippy(item.$elem[0]);
item.$elem.attr('data-id', instance.id);
item.$i = item.$elem.children('i');
item.instance = instance;
const contextMenuId = IdGenerator.generate();
const selector = `body > .mixly-dropdown-menus > div[m-id="${contextMenuId}"]`;
const contextMenu = new ContextMenu(selector, {
trigger: 'none',
appendTo: this.#$instanceContent_,
shadow: true,
autoHide: false,
async: false,
zIndex: 150,
position: (opt) => {
opt.$menu.css('margin', 0);
},
events: {
show: (opt) => {
item.$i.addClass('menu-shown');
this.#menuShown_ = true;
this.#singleton_.setProps({});
},
hide: (opt) => {
item.$i.removeClass('menu-shown');
if (this.#trigged_) {
this.#trigged_ = false;
return true;
}
this.#menuShown_ = false;
this.#singleton_.hide();
}
}
});
item.contextMenu = contextMenu;
contextMenu.register('menu', item.menu);
contextMenu.bind('getMenu', () => 'menu');
item.$menu = $(`<div m-id="${contextMenuId}"><div>`);
DropdownMenu.$container.append(item.$menu);
let i = 0;
for (; i < this.#menuItems_.length; i++) {
if (this.#menuItems_[i].weight <= item.weight) {
continue;
}
break;
}
if (i === this.#menuItems_.length) {
if (this.#menuItems_.length) {
this.#menuItems_[i - 1].$elem.after(item.$elem);
} else {
this.#$content_.append(item.$elem);
}
} else {
this.#menuItems_[i].$elem.before(item.$elem);
}
this.#menuItems_.splice(i, 0, item);
this.#ids_[item.id] = item;
this.#instanceIds_[instance.id] = item;
const instances = [];
for (let menuItem of this.#menuItems_) {
instances.push(menuItem.instance);
}
this.#singleton_.setInstances(instances);
return item.id;
}
getContextMenu(id) {
if (!this.#ids_[id]) {
return null;
}
return this.#ids_[id].contextMenu;
}
getInstance() {
return this.#singleton_;
}
remove(id) {
let item = this.#ids_[id];
if (!item) {
return;
}
delete this.#ids_[id];
const instanceId = item.instance.id;
delete this.#instanceIds_[instanceId];
for (let i in this.#menuItems_) {
if (this.#menuItems_[i].id !== id) {
continue;
}
this.#menuItems_.splice(i, 1);
break;
}
item.instance.destroy();
item.contextMenu.dispose();
item.$elem.remove();
item.$menu.remove();
item = null;
}
showMenu(instanceId) {
const item = this.#instanceIds_[instanceId];
item.$menu.contextMenu();
}
hideMenu(instanceId) {
const item = this.#instanceIds_[instanceId];
item.$menu.contextMenu('hide');
}
dispose() {
super.dispose();
this.#$instanceContent_.remove();
this.#$instanceContent_ = null;
this.#$instancePopper_.remove();
this.#$instancePopper_ = null;
this.#$content_.empty();
this.#$content_ = null;
for (let id in this.#ids_) {
this.remove(id);
}
this.#singleton_.destroy();
this.#singleton_ = null;
this.#menuItems_ = null;
this.#ids_ = null;
this.#instanceIds_ = null;
this.#activeInstance_ = null;
}
}
Mixly.DropdownMenuGroup = DropdownMenuGroup;
});