- fixed grouped rows
- added shortcut hint in menu items
This commit is contained in:
parent
ebd8ff8c1d
commit
552ba7f3c9
9 changed files with 71 additions and 44 deletions
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
|
@ -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"
|
||||||
|
|
24
src/app.css
24
src/app.css
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>;
|
||||||
|
|
|
@ -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
|
||||||
await command?.();
|
? async () => {
|
||||||
|
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
7
src/global.d.ts
vendored
|
@ -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[];
|
|
||||||
}
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue