38 lines
962 B
TypeScript
38 lines
962 B
TypeScript
// A single app-wide context menu.
|
|
|
|
import type { Component } from 'svelte';
|
|
|
|
export type MenuItem =
|
|
| { label: string; icon?: Component; action: () => void; danger?: boolean; disabled?: boolean; hint?: string }
|
|
| 'separator';
|
|
|
|
class Menu {
|
|
open = $state(false);
|
|
x = $state(0);
|
|
y = $state(0);
|
|
items = $state.raw<MenuItem[]>([]);
|
|
/** Grow upward from (x, y) — for menus opened from the bottom toolbar. */
|
|
above = $state(false);
|
|
|
|
show(x: number, y: number, items: MenuItem[]) {
|
|
this.above = false;
|
|
this.x = x;
|
|
this.y = y;
|
|
this.items = items;
|
|
this.open = true;
|
|
}
|
|
|
|
/** Open below/beside an anchor element. */
|
|
showAt(el: Element, items: MenuItem[], placement: 'below' | 'above' = 'below') {
|
|
const r = el.getBoundingClientRect();
|
|
this.show(r.left, placement === 'below' ? r.bottom + 4 : r.top - 4, items);
|
|
this.above = placement === 'above';
|
|
}
|
|
|
|
close() {
|
|
this.open = false;
|
|
this.above = false;
|
|
}
|
|
}
|
|
|
|
export const menu = new Menu();
|