some improvements to context menu

This commit is contained in:
Chris Kruining 2025-01-08 14:27:45 +01:00
parent 7e5af28ac2
commit b9dbdb5aa9
No known key found for this signature in database
GPG key ID: EB894A3560CCCAD2
9 changed files with 131 additions and 15 deletions

View file

@ -1,7 +1,8 @@
import { Accessor, children, Component, createContext, createEffect, createMemo, For, JSX, ParentComponent, ParentProps, Show, useContext } from 'solid-js';
import { Accessor, children, Component, createContext, createEffect, createMemo, For, JSX, Match, ParentComponent, ParentProps, Show, Switch, useContext } from 'solid-js';
import { useI18n } from '../i18n';
import { createStore } from 'solid-js/store';
import { CommandType, Modifier } from './command';
import { BsCommand, BsOption, BsShift, BsWindows } from 'solid-icons/bs';
interface CommandContextType {
readonly commands: Accessor<CommandType[]>;
@ -127,24 +128,46 @@ const Context = <T extends (...args: any[]) => any = (...args: any[]) => any>(pr
const Handle: Component<{ command: CommandType }> = (props) => {
const { t } = useI18n();
const isMac = false;
const isWindows = false;
return <>
{String(t(props.command.label) ?? props.command.label)}
<Show when={props.command.shortcut}>{
shortcut => {
const modifier = shortcut().modifier;
const modifierMap: Record<number, string> = {
[Modifier.Shift]: 'Shft',
[Modifier.Control]: 'Ctrl',
[Modifier.Meta]: 'Meta',
[Modifier.Alt]: 'Alt',
const title: Record<number, string> = {
[Modifier.Shift]: 'shift',
[Modifier.Control]: 'control',
[Modifier.Meta]: 'meta',
[Modifier.Alt]: 'alt',
};
return <samp>
<For each={Object.values(Modifier).filter((m): m is number => typeof m === 'number').filter(m => modifier & m)}>{
(m) => <><kbd>{modifierMap[m]}</kbd>+</>
(m) => <><kbd title={title[m]}>
<Switch>
<Match when={m === Modifier.Shift}>
<BsShift />
</Match>
<Match when={m === Modifier.Control}>
<Show when={isMac} fallback="Ctrl"><BsCommand /></Show>
</Match>
<Match when={m === Modifier.Meta}>
<Show when={isWindows} fallback="Meta"><BsWindows /></Show>
</Match>
<Match when={m === Modifier.Alt}>
<Show when={isMac} fallback="Alt"><BsOption /></Show>
</Match>
</Switch>
</kbd>+</>
}</For>
<kbd>{shortcut().key}</kbd>
<kbd>{shortcut().key.toUpperCase()}</kbd>
</samp>;
}
}</Show>

View file

@ -5,11 +5,11 @@
place-content: start;
inset-inline-start: anchor(start);
inset-block-start: anchor(end);
inset-block-start: anchor(start);
margin: 0;
gap: var(--padding-m);
padding: var(--padding-m) 0;
padding: var(--padding-m);
font-size: var(--text-s);
background-color: var(--surface-700);
@ -24,6 +24,7 @@
align-items: center;
padding: var(--padding-s) var(--padding-m);
border-radius: var(--radii-m);
& > sub {
color: var(--text-2);

View file

@ -35,6 +35,7 @@ const Menu: Component<{ children: (command: CommandType) => JSX.Element }> = (pr
const context = useContext(ContextMenu)!;
const commandContext = useCommands();
const [root, setRoot] = createSignal<HTMLElement>();
const [pos, setPos] = createSignal([0, 0]);
createEffect(() => {
const event = context.event();
@ -52,6 +53,12 @@ const Menu: Component<{ children: (command: CommandType) => JSX.Element }> = (pr
}
});
createEffect(() => {
const { offsetX = 0, offsetY = 0 } = (context.event() ?? {}) as MouseEvent;
setPos([offsetX, offsetY]);
});
const onToggle = (e: ToggleEvent) => {
if (e.newState === 'closed') {
context.hide();
@ -63,7 +70,7 @@ const Menu: Component<{ children: (command: CommandType) => JSX.Element }> = (pr
context.hide();
};
return <menu ref={setRoot} class={css.menu} style={`position-anchor: ${context.event()?.target?.style.getPropertyValue('anchor-name')};`} popover ontoggle={onToggle}>
return <menu ref={setRoot} class={css.menu} anchor={context.event()?.target?.id} style={`translate: ${pos()[0]}px ${pos()[1]}px;`} popover ontoggle={onToggle}>
<For each={context.commands()}>{
command => <li onpointerdown={onCommand(command)}>{props.children(command)}</li>
}</For>
@ -75,7 +82,7 @@ const Handle: ParentComponent<Record<string, any>> = (props) => {
const context = useContext(ContextMenu)!;
return <span {...rest} style={`anchor-name: --context-menu-${createUniqueId()};`} oncontextmenu={(e) => {
return <span {...rest} id={`context-menu-handle-${createUniqueId()};`} oncontextmenu={(e) => {
e.preventDefault();
context.show(e);
@ -85,6 +92,6 @@ const Handle: ParentComponent<Record<string, any>> = (props) => {
};
let handleCounter = 0;
const createUniqueId = () => `handle-${handleCounter++}`
const createUniqueId = () => `${handleCounter++}`;
export const Context = { Root, Menu, Handle };