You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

149 lines
4.6 KiB
JavaScript

const isMobileSafari = (() => {
let v = navigator.userAgent.match(/Version\/(?<version>\S+) Mobile\/\S+/);
if (v && v.groups.version) { // Safari
return true;
}
return false;
})();
export class ContextMenuWidget {
constructor() {
this.menuItems = [];
this.menuItemsUpdated = true;
this.initDom();
}
initDom() {
let menuContainer = document.createElement('div');
menuContainer.id = 'iseai-contextmenu';
menuContainer.className = 'oo-ui-toolGroup-tools oo-ui-popupToolGroup-tools oo-ui-listToolGroup-tools oo-ui-toolGroup-enabled-tools oo-ui-popupToolGroup-active-tools';
menuContainer.style.minWidth = 'unset';
menuContainer.style.display = 'none';
this.menuContainer = menuContainer;
let menuCover = document.createElement('div');
menuCover.className = 'isekai-contextmenu-cover';
menuCover.style.display = 'none';
menuCover.addEventListener('click', () => {
this.hide();
});
this.menuCover = menuCover;
document.body.appendChild(menuContainer);
document.body.appendChild(menuCover);
}
setMenuItem(menuItems) {
this.menuItems = menuItems;
this.menuItemsUpdated = true;
}
updateMenuItemDom() {
if (this.menuItemsUpdated) {
this.menuContainer.innerHTML = '';
this.menuItems.sort((a, b) => {
return (b.priority ?? 0) - (a.priority ?? 0);
}).forEach((menuItem) => {
let menuElem = document.createElement('span');
menuElem.className = 'oo-ui-widget oo-ui-widget-enabled oo-ui-tool';
let menuLink = document.createElement('a');
menuLink.className = 'oo-ui-tool-link';
menuLink.tabIndex = 0;
menuLink.role = 'button';
menuLink.addEventListener('click', (e) => {
e.preventDefault();
menuItem.onClick?.();
this.hide();
});
let menuLabel = document.createElement('span');
menuLabel.className = 'oo-ui-tool-title';
menuLabel.innerText = menuItem.label;
menuLink.appendChild(menuLabel);
menuElem.appendChild(menuLink);
this.menuContainer.appendChild(menuElem);
});
this.menuItemsUpdated = false;
}
}
show(...args) {
let x = 0;
let y = 0;
if (args[0] instanceof MouseEvent) {
x = args[0].clientX;
y = args[0].clientY;
} else if (args[0] instanceof Element) {
let rect = args[0].getBoundingClientRect();
x = rect.left;
y = rect.top;
} else if (args.length === 2 && typeof args[0] === 'number' && typeof args[1] === 'number') {
x = args[0];
y = args[1];
}
if (this.menuItemsUpdated) {
this.updateMenuItemDom();
}
this.menuContainer.style.display = 'block';
this.menuCover.style.display = 'block';
let menuWidth = this.menuContainer.clientWidth;
let menuHeight = this.menuContainer.clientHeight;
if (x + menuWidth > window.innerWidth) {
x -= menuWidth;
}
if (y + menuHeight > window.innerHeight) {
y -= menuHeight;
}
this.menuContainer.style.left = x + 'px';
this.menuContainer.style.top = y + 'px';
}
hide() {
this.menuContainer.style.display = 'none';
this.menuCover.style.display = 'none';
}
bindToDom(dom) {
dom.addEventListener('contextmenu', (e) => {
e.preventDefault();
this.show(e);
});
if (isMobileSafari) {
// Safari下对长按的特殊处理
let longPressTimer;
dom.addEventListener('touchstart', (e) => {
document.body.classList.add('isekai-contextmenu-ios-longpress');
longPressTimer = setTimeout(() => {
e.preventDefault();
this.show({
x: e.pageX - window.scrollX,
y: e.pageY - window.scrollY
});
}, 200);
});
dom.addEventListener('touchend', function (e) {
document.body.classList.remove('isekai-contextmenu-ios-longpress');
if (longPressTimer) {
clearInterval(longPressTimer);
}
});
}
}
}