Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CI / CI OK (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Issue Close Require / close-issues (push) Has been cancelled
Close stale issues / stale (push) Has been cancelled
117 lines
2.7 KiB
TypeScript
117 lines
2.7 KiB
TypeScript
import { reactive } from 'vue';
|
|
|
|
import type { ContextMenuItem, ContextMenuState } from './types';
|
|
|
|
const MENU_WIDTH = 180;
|
|
const MENU_ITEM_HEIGHT = 36;
|
|
const MENU_DIVIDER_HEIGHT = 9;
|
|
const SUBMENU_GAP = 4;
|
|
|
|
let listenersBound = false;
|
|
|
|
export const contextMenuState = reactive<ContextMenuState>({
|
|
visible: false,
|
|
x: 0,
|
|
y: 0,
|
|
items: [],
|
|
payload: null,
|
|
});
|
|
|
|
export const submenuState = reactive({
|
|
activeKey: null as string | null,
|
|
items: [] as ContextMenuItem[],
|
|
left: 0,
|
|
top: 0,
|
|
});
|
|
|
|
function bindGlobalListeners() {
|
|
if (listenersBound) {
|
|
return;
|
|
}
|
|
listenersBound = true;
|
|
|
|
const hide = () => hideContextMenu();
|
|
|
|
document.addEventListener('click', hide, true);
|
|
document.addEventListener('scroll', hide, true);
|
|
window.addEventListener('resize', hide);
|
|
}
|
|
|
|
export function resetSubmenu() {
|
|
submenuState.activeKey = null;
|
|
submenuState.items = [];
|
|
submenuState.left = 0;
|
|
submenuState.top = 0;
|
|
}
|
|
|
|
export function showSubmenu(key: string, items: ContextMenuItem[], left: number, top: number) {
|
|
submenuState.activeKey = key;
|
|
submenuState.items = items;
|
|
submenuState.left = left;
|
|
submenuState.top = top;
|
|
}
|
|
|
|
export function showContextMenu(
|
|
e: MouseEvent,
|
|
items: ContextMenuItem[],
|
|
payload?: any,
|
|
) {
|
|
e.preventDefault();
|
|
hideContextMenu();
|
|
|
|
const estimatedHeight = estimateMenuHeight(items);
|
|
let x = e.clientX;
|
|
let y = e.clientY;
|
|
|
|
if (x + MENU_WIDTH > window.innerWidth) {
|
|
x = Math.max(8, window.innerWidth - MENU_WIDTH - 8);
|
|
}
|
|
if (y + estimatedHeight > window.innerHeight) {
|
|
y = Math.max(8, window.innerHeight - estimatedHeight - 8);
|
|
}
|
|
|
|
contextMenuState.x = x;
|
|
contextMenuState.y = y;
|
|
contextMenuState.items = items;
|
|
contextMenuState.payload = payload ?? null;
|
|
contextMenuState.visible = true;
|
|
|
|
bindGlobalListeners();
|
|
}
|
|
|
|
export function hideContextMenu() {
|
|
contextMenuState.visible = false;
|
|
contextMenuState.items = [];
|
|
contextMenuState.payload = null;
|
|
resetSubmenu();
|
|
}
|
|
|
|
export function estimateMenuHeight(items: ContextMenuItem[]): number {
|
|
let height = 8;
|
|
for (const item of items) {
|
|
height +=
|
|
item.type === 'divider' ? MENU_DIVIDER_HEIGHT : MENU_ITEM_HEIGHT;
|
|
}
|
|
return height;
|
|
}
|
|
|
|
export function getSubmenuPosition(
|
|
parentRect: DOMRect,
|
|
childCount: number,
|
|
): { left: number; top: number } {
|
|
const submenuHeight = childCount * MENU_ITEM_HEIGHT + 8;
|
|
let left = parentRect.right + SUBMENU_GAP;
|
|
let top = parentRect.top;
|
|
|
|
if (left + MENU_WIDTH > window.innerWidth) {
|
|
left = parentRect.left - MENU_WIDTH - SUBMENU_GAP;
|
|
}
|
|
if (top + submenuHeight > window.innerHeight) {
|
|
top = Math.max(8, window.innerHeight - submenuHeight - 8);
|
|
}
|
|
|
|
return { left, top };
|
|
}
|
|
|
|
export { MENU_WIDTH, MENU_ITEM_HEIGHT };
|