quick and dirty key copy feature

This commit is contained in:
Chris Kruining 2025-01-08 10:20:50 +01:00
parent 9f0aa56361
commit 7b044c6050
No known key found for this signature in database
GPG key ID: EB894A3560CCCAD2
11 changed files with 43 additions and 32 deletions

View file

@ -9,7 +9,7 @@ export enum Modifier {
}
export interface CommandType<T extends (...args: any[]) => any = (...args: any[]) => any> {
(...args: Parameters<T>): Promise<ReturnType<T>>;
(...args: Parameters<T>): (ReturnType<T> extends Promise<any> ? ReturnType<T> : Promise<ReturnType<T>>);
label: DictionaryKey;
shortcut?: {
key: string;

View file

@ -106,7 +106,7 @@ const Add: Component<{ command: CommandType, commands: undefined } | { commands:
return undefined;
};
const Context = <T extends (...args: any[]) => any = any>(props: ParentProps<{ for: CommandType<T>, with: Parameters<T> }>): JSX.Element => {
const Context = <T extends (...args: any[]) => any = (...args: any[]) => any>(props: ParentProps<{ for: CommandType<T>, with: Parameters<T> }>): JSX.Element => {
const resolved = children(() => props.children);
const context = useCommands();
const args = createMemo(() => props.with);

View file

@ -1,27 +1,28 @@
import { Accessor, Component, createContext, createEffect, createMemo, createSignal, For, JSX, ParentComponent, splitProps, useContext } from "solid-js";
import { CommandType } from "./command";
import css from "./contextMenu.module.css";
import { useCommands } from "./context";
interface ContextMenuType {
readonly commands: Accessor<CommandType[]>;
readonly target: Accessor<HTMLElement | undefined>;
show(element: HTMLElement): void;
readonly event: Accessor<Event | undefined>;
show(event: Event): void;
hide(): void;
}
const ContextMenu = createContext<ContextMenuType>()
const Root: ParentComponent<{ commands: CommandType[] }> = (props) => {
const [target, setTarget] = createSignal<HTMLElement>();
const [event, setEvent] = createSignal<Event>();
const context = {
const context: ContextMenuType = {
commands: createMemo(() => props.commands),
target,
show(element: HTMLElement) {
setTarget(element);
event,
show(event) {
setEvent(event);
},
hide() {
setTarget(undefined);
setEvent(undefined);
},
};
@ -32,17 +33,18 @@ const Root: ParentComponent<{ commands: CommandType[] }> = (props) => {
const Menu: Component<{ children: (command: CommandType) => JSX.Element }> = (props) => {
const context = useContext(ContextMenu)!;
const commandContext = useCommands();
const [root, setRoot] = createSignal<HTMLElement>();
createEffect(() => {
const target = context.target();
const event = context.event();
const menu = root();
if (!menu) {
return;
}
if (target) {
if (event) {
menu.showPopover();
}
else {
@ -57,12 +59,11 @@ const Menu: Component<{ children: (command: CommandType) => JSX.Element }> = (pr
};
const onCommand = (command: CommandType) => (e: PointerEvent) => {
commandContext?.execute(command, context.event()!);
context.hide();
command();
};
return <menu ref={setRoot} class={css.menu} style={`position-anchor: ${context.target()?.style.getPropertyValue('anchor-name')};`} popover ontoggle={onToggle}>
return <menu ref={setRoot} class={css.menu} style={`position-anchor: ${context.event()?.target?.style.getPropertyValue('anchor-name')};`} popover ontoggle={onToggle}>
<For each={context.commands()}>{
command => <li onpointerdown={onCommand(command)}>{props.children(command)}</li>
}</For>
@ -73,12 +74,11 @@ const Handle: ParentComponent<Record<string, any>> = (props) => {
const [local, rest] = splitProps(props, ['children']);
const context = useContext(ContextMenu)!;
const [handle, setHandle] = createSignal<HTMLElement>();
return <span {...rest} ref={setHandle} style={`anchor-name: --context-menu-${createUniqueId()};`} oncontextmenu={(e) => {
return <span {...rest} style={`anchor-name: --context-menu-${createUniqueId()};`} oncontextmenu={(e) => {
e.preventDefault();
context.show(handle()!);
context.show(e);
return false;
}}>{local.children}</span>;

View file

@ -1,4 +1,4 @@
import { Accessor, Component, createEffect, createMemo, createSignal } from "solid-js";
import { Accessor, Component, createEffect, createMemo, createSignal, JSX } from "solid-js";
import { debounce, decode, Mutation } from "~/utilities";
import { Column, GridApi as GridCompApi, Grid as GridComp } from "~/components/grid";
import { createDataSet, DataSetNode, DataSetRowNode } from "~/components/table";
@ -29,7 +29,7 @@ const groupBy = (rows: DataSetRowNode<number, Entry>[]) => {
return group(rows.map<R>(r => ({ ...r, _key: r.value.key }))) as any;
}
export function Grid(props: { class?: string, rows: Entry[], locales: string[], api?: (api: GridApi) => any }) {
export function Grid(props: { class?: string, rows: Entry[], locales: string[], api?: (api: GridApi) => any, children?: (key: string) => JSX.Element }) {
const { t } = useI18n();
const rows = createMemo(() => createDataSet<Entry>(props.rows, { group: { by: 'key', with: groupBy } }));
@ -38,7 +38,7 @@ export function Grid(props: { class?: string, rows: Entry[], locales: string[],
{
id: 'key',
label: t('feature.file.grid.key'),
renderer: ({ value }) => value.split('.').at(-1),
renderer: ({ value }) => props.children?.(value) ?? value.split('.').at(-1),
},
...locales().map<Column<Entry>>(lang => ({
id: lang,

View file

@ -1,8 +1,8 @@
import { Accessor, createContext, createMemo, createSignal, ParentComponent, Setter, useContext } from 'solid-js';
import { translator, flatten, Translator, Flatten } from "@solid-primitives/i18n";
import { makePersisted } from '@solid-primitives/storage';
import en from '~/i18n/en-GB.json';
import nl from '~/i18n/nl-NL.json';
import { makePersisted } from '@solid-primitives/storage';
type RawDictionary = typeof en;
export type Dictionary = Flatten<RawDictionary>;