- fixed grouped rows

- added shortcut hint in menu items
This commit is contained in:
Chris Kruining 2024-10-10 13:24:02 +02:00
parent ebd8ff8c1d
commit 552ba7f3c9
9 changed files with 71 additions and 44 deletions

BIN
bun.lockb

Binary file not shown.

View file

@ -5,6 +5,7 @@
"@solidjs/router": "^0.14.7", "@solidjs/router": "^0.14.7",
"@solidjs/start": "^1.0.8", "@solidjs/start": "^1.0.8",
"dexie": "^4.0.8", "dexie": "^4.0.8",
"iterator-helpers-polyfill": "^3.0.1",
"solid-icons": "^1.1.0", "solid-icons": "^1.1.0",
"solid-js": "^1.9.1", "solid-js": "^1.9.1",
"vinxi": "^0.4.1" "vinxi": "^0.4.1"

View file

@ -4,10 +4,11 @@
--surface-1: #eee; --surface-1: #eee;
--surface-2: #f8f8f8; --surface-2: #f8f8f8;
--surface-3: #fff; --surface-3: #fff;
--text: #222; --text-1: #222;
--text-2: #282828;
--primary: #41c6b3; --primary: #41c6b3;
color: var(--text); color: var(--text-1);
accent-color: var(--primary); accent-color: var(--primary);
--info: oklch(.71 .17 249); --info: oklch(.71 .17 249);
@ -21,7 +22,8 @@
--surface-1: #333; --surface-1: #333;
--surface-2: #383838; --surface-2: #383838;
--surface-3: #444; --surface-3: #444;
--text: #eee; --text-1: #eee;
--text-2: #d8d8d8;
--primary: #6be8d6; --primary: #6be8d6;
@ -68,7 +70,7 @@ body {
block-size: 2em; block-size: 2em;
background-color: var(--surface-2); background-color: var(--surface-2);
color: var(--text); color: var(--text-1);
& > .logo { & > .logo {
inline-size: 3em; inline-size: 3em;
@ -93,7 +95,7 @@ body {
padding: .5em 1em; padding: .5em 1em;
background-color: inherit; background-color: inherit;
color: var(--text); color: var(--text-1);
border: none; border: none;
cursor: pointer; cursor: pointer;
@ -109,7 +111,7 @@ body {
inset-inline-start: anchor(self-start); inset-inline-start: anchor(self-start);
inset-block-start: anchor(end); inset-block-start: anchor(end);
grid-auto-flow: row; grid-template-columns: auto auto;
place-content: start; place-content: start;
gap: .5em; gap: .5em;
@ -126,11 +128,21 @@ body {
} }
& > .menu-item { & > .menu-item {
grid-column: span 2;
display: grid;
grid-template-columns: subgrid;
align-items: center;
background-color: var(--surface-2); background-color: var(--surface-2);
&:hover { &:hover {
background-color: var(--surface-3); background-color: var(--surface-3);
} }
& > sub {
color: var(--text-2);
text-align: end;
}
} }
} }

View file

@ -1,5 +1,8 @@
// @refresh reload // @refresh reload
import { createHandler, StartServer } from "@solidjs/start/server"; import { createHandler, StartServer } from "@solidjs/start/server";
import { installIntoGlobal } from "iterator-helpers-polyfill";
installIntoGlobal();
export default createHandler(() => ( export default createHandler(() => (
<StartServer <StartServer

View file

@ -15,7 +15,6 @@
margin: .1em; margin: .1em;
} }
& textarea { & textarea {
resize: vertical; resize: vertical;
min-block-size: 2em; min-block-size: 2em;
@ -87,14 +86,13 @@
& > summary { & > summary {
grid-column: 2 / span calc(1 + var(--columns)); grid-column: 2 / span calc(1 + var(--columns));
padding: .5em;
padding-inline-start: calc(var(--depth) * 1em + .5em);
&.cell {
padding-inline-start: calc((var(--depth) + 1) * 1em);
}
} }
& > label > .cell > span { & > label > .cell > span {
padding-inline-start: calc(var(--depth) * 1.25em); padding-inline-start: calc(var(--depth) * 1em);
} }
} }
} }

View file

@ -61,7 +61,28 @@ const SelectionProvider: ParentComponent<{ rows: Map<string, { [lang: string]: {
export const Grid: Component<{ columns: string[], rows: Map<string, { [lang: string]: { value: string, handle: FileSystemFileHandle } }>, context?: (ctx: SelectionContextType) => any }> = (props) => { export const Grid: Component<{ columns: string[], rows: Map<string, { [lang: string]: { value: string, handle: FileSystemFileHandle } }>, context?: (ctx: SelectionContextType) => any }> = (props) => {
const columnCount = createMemo(() => props.columns.length - 1); const columnCount = createMemo(() => props.columns.length - 1);
const root = createMemo<Entry>(() => { const root = createMemo<Entry>(() => {
return Object.fromEntries(props.rows.entries().map(([key, value]) => [key, Object.fromEntries(Object.entries(value).map(([lang, { value }]) => [lang, value]))])); return props.rows
?.entries()
.map(([key, value]) => [key, Object.fromEntries(Object.entries(value).map(([lang, { value }]) => [lang, value]))] as const)
.reduce((aggregate, [key, entry]) => {
let obj: any = aggregate;
const parts = key.split('.');
for (const [i, part] of parts.entries()) {
if (Object.hasOwn(obj, part) === false) {
obj[part] = {};
}
if (i === (parts.length - 1)) {
obj[part] = entry;
}
else {
obj = obj[part];
}
}
return aggregate;
}, {});
}); });
return <section class="table" style={{ '--columns': columnCount() }}> return <section class="table" style={{ '--columns': columnCount() }}>
@ -127,7 +148,7 @@ const Row: Component<{ entry: Entry, path?: string[] }> = (props) => {
const Group: Component<{ key: string, entry: Entry, path: string[] }> = (props) => { const Group: Component<{ key: string, entry: Entry, path: string[] }> = (props) => {
return <details open> return <details open>
<summary class="cell" style={{ '--depth': props.path.length - 1 }}>{props.key}</summary> <summary style={{ '--depth': props.path.length - 1 }}>{props.key}</summary>
<Row entry={props.entry} path={props.path} /> <Row entry={props.entry} path={props.path} />
</details>; </details>;

View file

@ -12,6 +12,7 @@ export interface MenuContextType {
}; };
export enum Modifier { export enum Modifier {
None = 0,
Shift = 1 << 0, Shift = 1 << 0,
Control = 1 << 1, Control = 1 << 1,
Meta = 1 << 2, Meta = 1 << 2,
@ -22,7 +23,7 @@ export interface Command {
(): any; (): any;
shortcut?: { shortcut?: {
key: string; key: string;
modifier?: Modifier; modifier: Modifier;
}; };
} }
@ -115,17 +116,31 @@ const Root: ParentComponent<{}> = (props) => {
} }
}; };
const onExecute = (command: Command) => { const onExecute = (command?: Command) => {
return async () => { return command
? async () => {
await command?.(); await command?.();
close(); close();
} }
: () => { }
}; };
const Button: Component<{ label: string, command: Command } | { [key: string]: any }> = (props) => { const Button: Component<{ label: string, command?: Command } & { [key: string]: any }> = (props) => {
const [local, rest] = splitProps(props, ['label', 'command']); const [local, rest] = splitProps(props, ['label', 'command']);
return <button class="menu-item" type="button" on:pointerdown={onExecute(local.command)} {...rest}>{local.label}</button>; return <button class="menu-item" type="button" on:pointerdown={onExecute(local.command)} {...rest}>
{local.label}
<Show when={local.command?.shortcut}>{
shortcut => {
const shift = shortcut().modifier & Modifier.Shift ? 'Shft+' : '';
const ctrl = shortcut().modifier & Modifier.Control ? 'Ctrl+' : '';
const meta = shortcut().modifier & Modifier.Meta ? 'Meta+' : '';
const alt = shortcut().modifier & Modifier.Alt ? 'Alt+' : '';
return <sub>{ctrl}{shift}{meta}{alt}{shortcut().key}</sub>;
}
}</Show>
</button>;
}; };
return <Portal mount={menu.ref()}> return <Portal mount={menu.ref()}>
@ -151,20 +166,6 @@ const Root: ParentComponent<{}> = (props) => {
<Button <Button
label={item.label} label={item.label}
on:pointerenter={(e) => {
if (!item.children) {
return;
}
const el = current();
if (!el) {
return;
}
el.hidePopover();
}}
{...(item.children ? { popovertarget: `child-${item.id}`, style: `anchor-name: --menu-${item.id};` } : { command: item.command })} {...(item.children ? { popovertarget: `child-${item.id}`, style: `anchor-name: --menu-${item.id};` } : { command: item.command })}
/> />
</> </>

7
src/global.d.ts vendored
View file

@ -1,8 +1 @@
/// <reference types="@solidjs/start/env" /> /// <reference types="@solidjs/start/env" />
interface IterableIterator<T> {
map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): IterableIterator<U>;
filter<S extends T>(predicate: (value: T, index: number, array: T[]) => value is S, thisArg?: any): IterableIterator<S>;
toArray(): T[];
}

View file

@ -57,8 +57,6 @@ export default function Edit(props) {
return aggregate; return aggregate;
}, new Map<string, { [lang: string]: { value: string, handle: FileSystemFileHandle } }>()); }, new Map<string, { [lang: string]: { value: string, handle: FileSystemFileHandle } }>());
console.log(contents, merged);
setColumns(['key', ...new Set(contents.map(c => c.lang))]); setColumns(['key', ...new Set(contents.map(c => c.lang))]);
setRows(merged); setRows(merged);
} }