some improvements to context menu
This commit is contained in:
parent
7e5af28ac2
commit
b9dbdb5aa9
9 changed files with 131 additions and 15 deletions
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 };
|
|
@ -15,6 +15,7 @@ export default function Experimental(props: ParentProps) {
|
|||
<Menu.Root>
|
||||
<Menu.Item command={goTo.withLabel('table').with('table')} />
|
||||
<Menu.Item command={goTo.withLabel('grid').with('grid')} />
|
||||
<Menu.Item command={goTo.withLabel('context-menu').with('context-menu')} />
|
||||
</Menu.Root>
|
||||
|
||||
{props.children}
|
||||
|
|
43
src/routes/(editor)/experimental/context-menu.module.css
Normal file
43
src/routes/(editor)/experimental/context-menu.module.css
Normal file
|
@ -0,0 +1,43 @@
|
|||
.root {
|
||||
display: grid;
|
||||
grid: 100% / auto minmax(0, 1fr);
|
||||
inline-size: 100%;
|
||||
block-size: 100%;
|
||||
|
||||
& .sidebar {
|
||||
z-index: 1;
|
||||
padding: var(--padding-xl);
|
||||
background-color: var(--surface-300);
|
||||
max-inline-size: 25vw;
|
||||
overflow: auto;
|
||||
|
||||
& > ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
& fieldset {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
gap: var(--padding-m);
|
||||
}
|
||||
|
||||
ol {
|
||||
margin-block: 0;
|
||||
}
|
||||
}
|
||||
|
||||
& .content {
|
||||
display: block grid;
|
||||
place-content: start;
|
||||
background-color: var(--surface-500);
|
||||
border-top-left-radius: var(--radii-xl);
|
||||
padding: var(--padding-m);
|
||||
|
||||
& > fieldset {
|
||||
border-radius: var(--radii-l);
|
||||
overflow: auto;
|
||||
background-color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
37
src/routes/(editor)/experimental/context-menu.tsx
Normal file
37
src/routes/(editor)/experimental/context-menu.tsx
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { Sidebar } from "~/components/sidebar";
|
||||
import { Command, Context, createCommand, Modifier } from "~/features/command";
|
||||
import { createSignal } from "solid-js";
|
||||
import css from './context-menu.module.css';
|
||||
|
||||
export default function ContextMenu(props: {}) {
|
||||
const [message, setMessage] = createSignal('');
|
||||
|
||||
const commands = {
|
||||
back: createCommand('Back', () => setMessage('Back command is triggered'), { key: '[', modifier: Modifier.Control }),
|
||||
forward: createCommand('Forward', () => setMessage('forward command is triggered'), { key: ']', modifier: Modifier.Control }),
|
||||
reload: createCommand('Reload', () => setMessage('reload command is triggered'), { key: 'r', modifier: Modifier.Control }),
|
||||
showBookmarks: createCommand('Show bookmarks', () => setMessage('showBookmarks command is triggered'), { key: 'b', modifier: Modifier.Control }),
|
||||
showFullUrls: createCommand('Show full URL\'s', () => setMessage('showFullUrls command is triggered')),
|
||||
allModifiers: createCommand('shell.command.openCommandPalette', () => setMessage('allModifiers command is triggered'), { key: 'a', modifier: Modifier.Alt | Modifier.Control | Modifier.Meta | Modifier.Shift }),
|
||||
};
|
||||
|
||||
return <div class={css.root}>
|
||||
<Sidebar as="aside" label={'Options'} class={css.sidebar}>
|
||||
<fieldset>
|
||||
<legend>Message</legend>
|
||||
|
||||
<p>{message()}</p>
|
||||
</fieldset>
|
||||
</Sidebar>
|
||||
|
||||
<div class={css.content}>
|
||||
<Context.Root commands={Object.values(commands)}>
|
||||
<Context.Menu>{
|
||||
command => <Command.Handle command={command} />
|
||||
}</Context.Menu>
|
||||
|
||||
<Context.Handle>Right click on me</Context.Handle>
|
||||
</Context.Root>
|
||||
</div>
|
||||
</div >;
|
||||
}
|
5
src/routes/(editor)/experimental/index.tsx
Normal file
5
src/routes/(editor)/experimental/index.tsx
Normal file
|
@ -0,0 +1,5 @@
|
|||
|
||||
|
||||
export default function Index(props: {}) {
|
||||
return <></>;
|
||||
}
|
|
@ -3,7 +3,6 @@ import { Column, createDataSet, DataSetGroupNode, DataSetNode, DataSetRowNode, G
|
|||
import { createStore } from 'solid-js/store';
|
||||
import { Person, people } from './experimental.data';
|
||||
import { createEffect, createMemo, For } from 'solid-js';
|
||||
import { Command, createCommand, Modifier } from '~/features/command';
|
||||
import css from './table.module.css';
|
||||
|
||||
export default function TableExperiment() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue