This commit is contained in:
Chris Kruining 2024-12-12 16:40:13 +01:00
parent d219ae1f9a
commit b23db1d5a8
No known key found for this signature in database
GPG key ID: EB894A3560CCCAD2
21 changed files with 579 additions and 818 deletions

View file

@ -1,9 +1,9 @@
import { Accessor, children, Component, createContext, createEffect, createMemo, JSX, ParentComponent, ParentProps, Show, useContext } from 'solid-js';
interface CommandContextType {
set(commands: CommandType<any[]>[]): void;
addContextualArguments<T extends any[] = any[]>(command: CommandType<T>, target: EventTarget, args: Accessor<T>): void;
execute<TArgs extends any[] = []>(command: CommandType<TArgs>, event: Event): void;
set(commands: CommandType<any>[]): void;
addContextualArguments<T extends (...args: any[]) => any = any>(command: CommandType<T>, target: EventTarget, args: Accessor<Parameters<T>>): void;
execute<T extends (...args: any[]) => any = any>(command: CommandType<T>, event: Event): void;
}
const CommandContext = createContext<CommandContextType>();
@ -13,16 +13,16 @@ export const useCommands = () => useContext(CommandContext);
const Root: ParentComponent<{ commands: CommandType[] }> = (props) => {
// const commands = () => props.commands ?? [];
const contextualArguments = new Map<CommandType, WeakMap<EventTarget, Accessor<any[]>>>();
const commands = new Set<CommandType<any[]>>();
const commands = new Set<CommandType<any>>();
const context = {
set(c: CommandType<any[]>[]): void {
set(c: CommandType<any>[]): void {
for (const command of c) {
commands.add(command);
}
},
addContextualArguments<T extends any[] = any[]>(command: CommandType<T>, target: EventTarget, args: Accessor<T>): void {
addContextualArguments<T extends (...args: any[]) => any = any>(command: CommandType<T>, target: EventTarget, args: Accessor<Parameters<T>>): void {
if (contextualArguments.has(command) === false) {
contextualArguments.set(command, new WeakMap());
}
@ -30,8 +30,8 @@ const Root: ParentComponent<{ commands: CommandType[] }> = (props) => {
contextualArguments.get(command)?.set(target, args);
},
execute<T extends any[] = any[]>(command: CommandType<T>, event: Event): boolean | undefined {
const args = ((): T => {
execute<T extends (...args: any[]) => any = any>(command: CommandType<T>, event: Event): boolean | undefined {
const args = ((): Parameters<T> => {
const contexts = contextualArguments.get(command);
@ -45,7 +45,8 @@ const Root: ParentComponent<{ commands: CommandType[] }> = (props) => {
return [] as any;
}
const args = contexts.get(element)! as Accessor<T>;
const args = contexts.get(element)! as Accessor<Parameters<T>>;
return args();
})();
@ -84,9 +85,9 @@ const Root: ParentComponent<{ commands: CommandType[] }> = (props) => {
</CommandContext.Provider>;
};
const Add: Component<{ command: CommandType<any[]> } | { commands: CommandType<any[]>[] }> = (props) => {
const Add: Component<{ command: CommandType<any> } | { commands: CommandType<any>[] }> = (props) => {
const context = useCommands();
const commands = createMemo<CommandType<any[]>[]>(() => props.commands ?? [props.command]);
const commands = createMemo<CommandType<any>[]>(() => props.commands ?? [props.command]);
createEffect(() => {
context?.set(commands());
@ -95,7 +96,7 @@ const Add: Component<{ command: CommandType<any[]> } | { commands: CommandType<a
return undefined;
};
const Context = <T extends any[] = any[]>(props: ParentProps<{ for: CommandType<T>, with: T }>): JSX.Element => {
const Context = <T extends (...args: any[]) => 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);
@ -139,17 +140,19 @@ export enum Modifier {
Alt = 1 << 3,
}
export interface CommandType<TArgs extends any[] = []> {
(...args: TArgs): any;
export interface CommandType<T extends (...args: any[]) => any = any> {
(...args: Parameters<T>): Promise<ReturnType<T>>;
label: string;
shortcut?: {
key: string;
modifier: Modifier;
};
withLabel(label: string): CommandType<T>;
with<A extends any[], B extends any[]>(this: (this: ThisParameterType<T>, ...args: [...A, ...B]) => ReturnType<T>, ...args: A): CommandType<(...args: B) => ReturnType<T>>;
}
export const createCommand = <TArgs extends any[] = []>(label: string, command: (...args: TArgs) => any, shortcut?: CommandType['shortcut']): CommandType<TArgs> => {
return Object.defineProperties(command as CommandType<TArgs>, {
export const createCommand = <T extends (...args: any[]) => any>(label: string, command: T, shortcut?: CommandType['shortcut']): CommandType<T> => {
return Object.defineProperties(((...args: Parameters<T>) => command(...args)) as any, {
label: {
value: label,
configurable: false,
@ -159,18 +162,24 @@ export const createCommand = <TArgs extends any[] = []>(label: string, command:
value: shortcut ? { key: shortcut.key.toLowerCase(), modifier: shortcut.modifier } : undefined,
configurable: false,
writable: false,
},
withLabel: {
value(label: string) {
return createCommand(label, command, shortcut);
},
configurable: false,
writable: false,
},
with: {
value<A extends any[], B extends any[]>(this: (this: ThisParameterType<T>, ...args: [...A, ...B]) => ReturnType<T>, ...args: A): CommandType<(...args: B) => ReturnType<T>> {
return createCommand(label, command.bind(undefined, ...args), shortcut);
},
configurable: false,
writable: false,
}
});
};
export const noop = Object.defineProperties(createCommand('noop', () => { }), {
withLabel: {
value(label: string) {
return createCommand(label, () => { });
},
configurable: false,
writable: false,
},
}) as CommandType & { withLabel(label: string): CommandType };
export const noop = createCommand('noop', () => { });
export { Context } from './contextMenu';