Kaas
This commit is contained in:
parent
d219ae1f9a
commit
b23db1d5a8
21 changed files with 579 additions and 818 deletions
|
@ -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';
|
|
@ -1,128 +0,0 @@
|
|||
.table {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: 2em minmax(10em, max-content) repeat(var(--columns), auto);
|
||||
align-content: start;
|
||||
padding-inline: 1px;
|
||||
margin-inline: -1px;
|
||||
|
||||
block-size: 100%;
|
||||
overflow: clip auto;
|
||||
|
||||
background-color: var(--surface-600);
|
||||
|
||||
& input[type="checkbox"] {
|
||||
margin: .1em;
|
||||
}
|
||||
|
||||
& textarea {
|
||||
resize: vertical;
|
||||
min-block-size: max(2em, 100%);
|
||||
max-block-size: 50em;
|
||||
|
||||
background-color: var(--surface-600);
|
||||
color: var(--text-1);
|
||||
border-color: var(--text-2);
|
||||
border-radius: var(--radii-s);
|
||||
|
||||
&:has(::spelling-error, ::grammar-error) {
|
||||
border-color: var(--fail);
|
||||
}
|
||||
|
||||
& ::spelling-error {
|
||||
outline: 1px solid var(--fail);
|
||||
text-decoration: yellow underline;
|
||||
}
|
||||
}
|
||||
|
||||
& .cell {
|
||||
display: grid;
|
||||
padding: .5em;
|
||||
border: 1px solid transparent;
|
||||
border-radius: var(--radii-m);
|
||||
|
||||
&:has(textarea:focus) {
|
||||
border-color: var(--info);
|
||||
}
|
||||
|
||||
& > span {
|
||||
align-self: center;
|
||||
}
|
||||
}
|
||||
|
||||
& :is(.header, .main, .footer) {
|
||||
grid-column: span calc(2 + var(--columns));
|
||||
display: grid;
|
||||
grid-template-columns: subgrid;
|
||||
}
|
||||
|
||||
& .header {
|
||||
position: sticky;
|
||||
inset-block-start: 0;
|
||||
background-color: var(--surface-600);
|
||||
border-block-end: 1px solid var(--surface-300);
|
||||
}
|
||||
|
||||
& .row {
|
||||
--bg: var(--text);
|
||||
--alpha: 0;
|
||||
grid-column: span calc(2 + var(--columns));
|
||||
display: grid;
|
||||
grid-template-columns: subgrid;
|
||||
border: 1px solid transparent;
|
||||
background-color: color(from var(--bg) srgb r g b / var(--alpha));
|
||||
|
||||
&:has(> .cell > :checked) {
|
||||
--bg: var(--info);
|
||||
--alpha: .1;
|
||||
border-color: var(--bg);
|
||||
|
||||
& span {
|
||||
font-variation-settings: 'GRAD' 1000;
|
||||
}
|
||||
|
||||
& + :has(> .cell> :checked) {
|
||||
border-block-start-color: transparent;
|
||||
}
|
||||
|
||||
&:has(+ .row > .cell > :checked) {
|
||||
border-block-end-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
--alpha: .2 !important;
|
||||
}
|
||||
}
|
||||
|
||||
& details {
|
||||
display: contents;
|
||||
|
||||
&::details-content {
|
||||
grid-column: span calc(2 + var(--columns));
|
||||
display: grid;
|
||||
grid-template-columns: subgrid;
|
||||
}
|
||||
|
||||
&:not([open])::details-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
& > summary {
|
||||
grid-column: 2 / span calc(1 + var(--columns));
|
||||
padding: .5em;
|
||||
padding-inline-start: calc(var(--depth) * 1em + .5em);
|
||||
|
||||
}
|
||||
|
||||
& > .row > .cell > span {
|
||||
padding-inline-start: calc(var(--depth) * 1em);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@property --depth {
|
||||
syntax: "<number>";
|
||||
inherits: false;
|
||||
initial-value: 0;
|
||||
}
|
|
@ -105,6 +105,10 @@ export const Grid: Component<{ class?: string, columns: string[], rows: Rows, ap
|
|||
},
|
||||
|
||||
addColumn(name: string): void {
|
||||
if (state.columns.includes(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setState(produce(state => {
|
||||
state.columns.push(name);
|
||||
state.rows = Object.fromEntries(Object.entries(state.rows).map(([key, row]) => [key, { ...row, [name]: '' }]));
|
||||
|
@ -155,7 +159,6 @@ const Api: Component<{ api: undefined | ((api: GridApi) => any), table?: any }>
|
|||
insert(prop: string) {
|
||||
gridContext.insert(prop);
|
||||
},
|
||||
|
||||
addColumn(name: string): void {
|
||||
gridContext.addColumn(name);
|
||||
},
|
||||
|
|
|
@ -75,9 +75,9 @@ const useMenu = () => {
|
|||
return context;
|
||||
}
|
||||
|
||||
type ItemProps = { label: string, children: JSX.Element } | { command: CommandType };
|
||||
type ItemProps<T extends (...args: any[]) => any> = { label: string, children: JSX.Element } | { command: CommandType<T> };
|
||||
|
||||
const Item: Component<ItemProps> = (props) => {
|
||||
function Item<T extends (...args: any[]) => any>(props: ItemProps<T>) {
|
||||
const id = createUniqueId();
|
||||
|
||||
if (props.command) {
|
||||
|
@ -303,7 +303,7 @@ function SearchableList<T>(props: SearchableListProps<T>): JSX.Element {
|
|||
createEffect(() => {
|
||||
const length = results().length - 1;
|
||||
|
||||
setSelected(current => current !== undefined ? Math.min(current, length) : undefined);
|
||||
setSelected(current => Math.min(current, length));
|
||||
});
|
||||
|
||||
const onKeyDown = (e: KeyboardEvent) => {
|
||||
|
@ -334,7 +334,7 @@ function SearchableList<T>(props: SearchableListProps<T>): JSX.Element {
|
|||
};
|
||||
|
||||
return <form method="dialog" class={css.search} onkeydown={onKeyDown} onsubmit={onSubmit}>
|
||||
<input id={`search-${id}`} ref={setInput} value={term()} oninput={(e) => setTerm(e.target.value)} placeholder="start typing for command" autofocus autocomplete="off" />
|
||||
<input id={`search-${id}`} ref={setInput} value={term()} oninput={(e) => setTerm(e.target.value)} placeholder="start typing for command" autofocus autocomplete="off" enterkeyhint="go" />
|
||||
|
||||
<output for={`search-${id}`}>
|
||||
<For each={results()}>{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue