117 lines
3.3 KiB
JavaScript
117 lines
3.3 KiB
JavaScript
export const TreeContextMenuMixin = (superClass) => class TreeContextMenuMixin extends superClass {
|
|
async _onContextMenu(item, originalEvent) {
|
|
originalEvent.preventDefault();
|
|
|
|
if (this.selectOnRightClick && this.manageState) {
|
|
const pathStr = item.path.join('/');
|
|
const set = new Set(this._selectedPathSet);
|
|
let changed = false;
|
|
|
|
if (this.multiSelect) {
|
|
if (!set.has(pathStr)) {
|
|
set.add(pathStr);
|
|
changed = true;
|
|
}
|
|
} else {
|
|
if (!set.has(pathStr) || set.size !== 1) {
|
|
set.clear();
|
|
set.add(pathStr);
|
|
changed = true;
|
|
}
|
|
}
|
|
|
|
if (changed) {
|
|
this._selectedPathSet = set;
|
|
this.selectedPaths = Array.from(set);
|
|
this._rebuildManagedTree();
|
|
|
|
this.dispatchEvent(new CustomEvent('node-selected', {
|
|
detail: { node: item.node, path: item.path, originalEvent },
|
|
bubbles: true,
|
|
composed: true,
|
|
}));
|
|
}
|
|
}
|
|
|
|
const contextEvent = new CustomEvent('node-context', {
|
|
detail: { item, originalEvent },
|
|
bubbles: true,
|
|
composed: true,
|
|
cancelable: true,
|
|
});
|
|
|
|
this.dispatchEvent(contextEvent);
|
|
if (contextEvent.defaultPrevented) return;
|
|
|
|
const baseActions = this._mergeActions(
|
|
this.defaultActions,
|
|
this._normalizeActions(item.node?.actions)
|
|
);
|
|
|
|
const maybeModified = this.beforeContextMenu
|
|
? this.beforeContextMenu(item, baseActions)
|
|
: baseActions;
|
|
|
|
const actions = await Promise.resolve(maybeModified);
|
|
if (actions === false) return;
|
|
|
|
const x = originalEvent.clientX;
|
|
const y = originalEvent.clientY;
|
|
|
|
this._contextMenu = { item, actions: Array.isArray(actions) ? actions : baseActions };
|
|
this.requestUpdate();
|
|
await this.updateComplete;
|
|
|
|
const menu = this.shadowRoot.querySelector('.context-menu');
|
|
if (menu) {
|
|
const anchor = {
|
|
getBoundingClientRect: () => ({
|
|
top: y, bottom: y, left: x, right: x, width: 0, height: 0
|
|
})
|
|
};
|
|
this._posFixed(anchor, menu, { valign: 'bottom', halign: 'left' });
|
|
}
|
|
|
|
this._attachOutsideHandlers();
|
|
}
|
|
|
|
_onMenuAction(action, item, originalEvent) {
|
|
originalEvent.stopPropagation();
|
|
this._dispatchAction(action?.action, item, 'context-menu', originalEvent);
|
|
this._closeContextMenu();
|
|
}
|
|
|
|
_attachOutsideHandlers() {
|
|
if (this._outsideHandler) return;
|
|
this._outsideHandler = (e) => {
|
|
const menu = this.shadowRoot.querySelector('.context-menu');
|
|
if (menu && e.composedPath().includes(menu)) return;
|
|
this._closeContextMenu();
|
|
};
|
|
this._keyHandler = (e) => {
|
|
if (e.key === 'Escape') {
|
|
this._closeContextMenu();
|
|
}
|
|
};
|
|
window.addEventListener('pointerdown', this._outsideHandler, true);
|
|
window.addEventListener('keydown', this._keyHandler, true);
|
|
}
|
|
|
|
_removeOutsideHandlers() {
|
|
if (this._outsideHandler) {
|
|
window.removeEventListener('pointerdown', this._outsideHandler, true);
|
|
this._outsideHandler = null;
|
|
}
|
|
if (this._keyHandler) {
|
|
window.removeEventListener('keydown', this._keyHandler, true);
|
|
this._keyHandler = null;
|
|
}
|
|
}
|
|
|
|
_closeContextMenu() {
|
|
this._contextMenu = null;
|
|
this.requestUpdate();
|
|
this._removeOutsideHandlers();
|
|
}
|
|
};
|