finished refactoring. all the code is now way more clearly seperated and reusable
This commit is contained in:
parent
c2b7a9ccf3
commit
998a788baa
13 changed files with 141 additions and 231 deletions
|
@ -1,14 +1,14 @@
|
|||
import { Accessor, createContext, createEffect, createMemo, createSignal, JSX, useContext } from "solid-js";
|
||||
import { Mutation } from "~/utilities";
|
||||
import { SelectionMode, Table, Column as TableColumn, TableApi, DataSet, CellRenderer } from "~/components/table";
|
||||
import { SelectionMode, Table, Column as TableColumn, TableApi, DataSet, CellRenderer as TableCellRenderer } from "~/components/table";
|
||||
import css from './grid.module.css';
|
||||
|
||||
export interface CellEditor<T extends Record<string, any>, K extends keyof T> {
|
||||
(cell: Parameters<CellRenderer<T, K>>[0] & { mutate: (next: T[K]) => any }): JSX.Element;
|
||||
export interface CellRenderer<T extends Record<string, any>, K extends keyof T> {
|
||||
(cell: Parameters<TableCellRenderer<T, K>>[0] & { mutate: (next: T[K]) => any }): JSX.Element;
|
||||
}
|
||||
|
||||
export interface Column<T extends Record<string, any>> extends TableColumn<T> {
|
||||
editor?: CellEditor<T, keyof T>;
|
||||
export interface Column<T extends Record<string, any>> extends Omit<TableColumn<T>, 'renderer'> {
|
||||
renderer?: CellRenderer<T, keyof T>;
|
||||
}
|
||||
|
||||
export interface GridApi<T extends Record<string, any>> extends TableApi<T> {
|
||||
|
@ -63,16 +63,16 @@ export function Grid<T extends Record<string, any>>(props: GridProps<T>) {
|
|||
},
|
||||
};
|
||||
|
||||
const cellEditors = createMemo(() => Object.fromEntries(
|
||||
const cellRenderers = createMemo(() => Object.fromEntries(
|
||||
props.columns
|
||||
.filter(c => c.editor !== undefined)
|
||||
.filter(c => c.renderer !== undefined)
|
||||
.map(c => {
|
||||
const Editor: CellRenderer<T, keyof T> = ({ row, column, value }) => {
|
||||
const mutate = (next: T[keyof T]) => {
|
||||
ctx.mutate(row, column, next);
|
||||
};
|
||||
|
||||
return c.editor!({ row, column, value, mutate });
|
||||
return c.renderer!({ row, column, value, mutate });
|
||||
};
|
||||
|
||||
return [c.id, Editor] as const;
|
||||
|
@ -83,7 +83,7 @@ export function Grid<T extends Record<string, any>>(props: GridProps<T>) {
|
|||
<Api api={props.api} table={table()} />
|
||||
|
||||
<Table api={setTable} class={`${css.grid} ${props.class}`} rows={rows()} columns={columns()} selectionMode={SelectionMode.Multiple}>{
|
||||
cellEditors()
|
||||
cellRenderers()
|
||||
}</Table>
|
||||
</GridContext.Provider>;
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
export type { DataSetRowNode, DataSetGroupNode, DataSetNode, SelectionMode, SortingFunction, SortOptions, GroupingFunction, GroupOptions } from '../table';
|
||||
export type { GridApi, Column, CellEditor } from './grid';
|
||||
export type { GridApi, Column, CellRenderer as CellEditor } from './grid';
|
||||
export { Grid } from './grid';
|
|
@ -1,5 +1,5 @@
|
|||
import { Accessor, createEffect, createMemo } from "solid-js";
|
||||
import { createStore, NotWrappable, StoreSetter, unwrap } from "solid-js/store";
|
||||
import { createStore, NotWrappable, produce, StoreSetter, unwrap } from "solid-js/store";
|
||||
import { CustomPartial } from "solid-js/store/types/store.js";
|
||||
import { deepCopy, deepDiff, Mutation } from "~/utilities";
|
||||
|
||||
|
@ -38,13 +38,14 @@ export type Setter<T> =
|
|||
|
||||
export interface DataSet<T extends Record<string, any>> {
|
||||
data: T[];
|
||||
value: Accessor<DataSetNode<keyof T, T>[]>;
|
||||
nodes: Accessor<DataSetNode<keyof T, T>[]>;
|
||||
value: Accessor<(T | undefined)[]>;
|
||||
mutations: Accessor<Mutation[]>;
|
||||
sorting: Accessor<SortOptions<T> | undefined>;
|
||||
grouping: Accessor<GroupOptions<T> | undefined>;
|
||||
|
||||
// mutate<K extends keyof T>(index: number, value: T): void;
|
||||
mutate<K extends keyof T>(index: number, prop: K, value: T[K]): void;
|
||||
mutateEach(setter: (value: T) => T): void;
|
||||
remove(indices: number[]): void;
|
||||
insert(item: T, at?: number): void;
|
||||
|
||||
|
@ -59,15 +60,14 @@ function defaultGroupingFunction<T>(groupBy: keyof T): GroupingFunction<number,
|
|||
}
|
||||
|
||||
export const createDataSet = <T extends Record<string, any>>(data: T[], initialOptions?: { sort?: SortOptions<T>, group?: GroupOptions<T> }): DataSet<T> => {
|
||||
const nodes = data;
|
||||
const [state, setState] = createStore<DataSetState<T>>({
|
||||
value: deepCopy(nodes),
|
||||
snapshot: nodes,
|
||||
value: deepCopy(data),
|
||||
snapshot: data,
|
||||
sorting: initialOptions?.sort,
|
||||
grouping: initialOptions?.group,
|
||||
});
|
||||
|
||||
const value = createMemo(() => {
|
||||
const nodes = createMemo(() => {
|
||||
const sorting = state.sorting;
|
||||
const grouping = state.grouping;
|
||||
|
||||
|
@ -106,7 +106,8 @@ export const createDataSet = <T extends Record<string, any>>(data: T[], initialO
|
|||
|
||||
const set: DataSet<T> = {
|
||||
data,
|
||||
value,
|
||||
nodes,
|
||||
value: createMemo(() => state.value),
|
||||
mutations,
|
||||
sorting,
|
||||
grouping,
|
||||
|
@ -115,6 +116,10 @@ export const createDataSet = <T extends Record<string, any>>(data: T[], initialO
|
|||
setState('value', index, prop as any, value);
|
||||
},
|
||||
|
||||
mutateEach(setter) {
|
||||
setState('value', value => value.map(i => i === undefined ? undefined : setter(i)));
|
||||
},
|
||||
|
||||
remove(indices) {
|
||||
setState('value', value => value.map((item, i) => indices.includes(i) ? undefined : item));
|
||||
},
|
||||
|
|
|
@ -163,6 +163,7 @@
|
|||
|
||||
& > .header {
|
||||
border-block-end-color: transparent;
|
||||
animation: none;
|
||||
|
||||
& .cell {
|
||||
justify-content: start;
|
||||
|
@ -171,6 +172,7 @@
|
|||
& > label {
|
||||
--state: 0;
|
||||
display: contents;
|
||||
cursor: pointer;
|
||||
|
||||
& input[type="checkbox"] {
|
||||
display: none;
|
||||
|
@ -181,6 +183,7 @@
|
|||
transition: rotate .3s ease-in-out;
|
||||
inline-size: 1em;
|
||||
aspect-ratio: 1;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
&:has(input:not(:checked)) {
|
||||
|
|
|
@ -6,17 +6,18 @@ import css from './table.module.css';
|
|||
|
||||
selectable;
|
||||
|
||||
export type Column<T> = {
|
||||
export type CellRenderer<T extends Record<string, any>, K extends keyof T> = (cell: { row: number, column: K, value: T[K] }) => JSX.Element;
|
||||
export type CellRenderers<T extends Record<string, any>> = { [K in keyof T]?: CellRenderer<T, K> };
|
||||
|
||||
export interface Column<T extends Record<string, any>> {
|
||||
id: keyof T,
|
||||
label: string,
|
||||
sortable?: boolean,
|
||||
group?: string,
|
||||
renderer?: CellRenderer<T, keyof T>,
|
||||
readonly groupBy?: (rows: DataSetRowNode<keyof T, T>[]) => DataSetNode<keyof T, T>[],
|
||||
};
|
||||
|
||||
export type CellRenderer<T extends Record<string, any>, K extends keyof T> = (cell: { row: number, column: K, value: T[K] }) => JSX.Element;
|
||||
export type CellRenderers<T extends Record<string, any>> = { [K in keyof T]?: CellRenderer<T, K> };
|
||||
|
||||
export interface TableApi<T extends Record<string, any>> {
|
||||
readonly selection: Accessor<SelectionItem<keyof T, T>[]>;
|
||||
readonly rows: Accessor<DataSet<T>>;
|
||||
|
@ -96,7 +97,7 @@ function InnerTable<T extends Record<string, any>>(props: InnerTableProps<T>) {
|
|||
<Head />
|
||||
|
||||
<tbody class={css.main}>
|
||||
<For each={props.rows.value()}>{
|
||||
<For each={props.rows.nodes()}>{
|
||||
node => <Node node={node} depth={0} />
|
||||
}</For>
|
||||
</tbody>
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
.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;
|
||||
}
|
||||
}
|
|
@ -1,185 +1,70 @@
|
|||
import { Accessor, Component, createContext, createEffect, createMemo, createSignal, For, ParentComponent, Show, useContext } from "solid-js";
|
||||
import { createStore, produce, unwrap } from "solid-js/store";
|
||||
import { debounce, deepCopy, deepDiff, Mutation, splitAt } from "~/utilities";
|
||||
import { DataSetRowNode, DataSetNode, SelectionMode, Table } from "~/components/table";
|
||||
import css from './grid.module.css';
|
||||
|
||||
type Rows = Map<string, Record<string, string>>;
|
||||
|
||||
export interface GridContextType {
|
||||
readonly rows: Accessor<Rows>;
|
||||
readonly mutations: Accessor<Mutation[]>;
|
||||
// readonly selection: Accessor<SelectionItem[]>;
|
||||
mutate(prop: string, value: string): void;
|
||||
remove(props: string[]): void;
|
||||
insert(prop: string): void;
|
||||
addColumn(name: string): void;
|
||||
}
|
||||
import { Accessor, Component, createEffect, createMemo, createSignal } from "solid-js";
|
||||
import { debounce, Mutation } from "~/utilities";
|
||||
import { Column, GridApi as GridCompApi, Grid as GridComp } from "~/components/grid";
|
||||
import { createDataSet, DataSetNode, DataSetRowNode } from "~/components/table";
|
||||
import css from "./grid.module.css"
|
||||
|
||||
export type Entry = { key: string } & { [lang: string]: string };
|
||||
export interface GridApi {
|
||||
readonly selection: Accessor<Record<string, Record<string, string>>>;
|
||||
readonly rows: Accessor<Record<string, Record<string, string>>>;
|
||||
readonly mutations: Accessor<Mutation[]>;
|
||||
selectAll(): void;
|
||||
clear(): void;
|
||||
remove(keys: string[]): void;
|
||||
insert(prop: string): void;
|
||||
addColumn(name: string): void;
|
||||
remove(indices: number[]): void;
|
||||
addKey(key: string): void;
|
||||
addLocale(locale: string): void;
|
||||
};
|
||||
|
||||
const groupBy = (rows: DataSetRowNode<number, Entry>[]) => {
|
||||
type R = DataSetRowNode<number, Entry> & { _key: string };
|
||||
|
||||
const group = (nodes: R[]): DataSetNode<number, Entry>[] => Object
|
||||
.entries(Object.groupBy(nodes, r => r._key.split('.').at(0)!) as Record<number, R[]>)
|
||||
.map<any>(([key, nodes]) => nodes.at(0)?._key === key
|
||||
? nodes[0]
|
||||
: ({ kind: 'group', key, groupedBy: 'key', nodes: group(nodes.map(n => ({ ...n, _key: n._key.slice(key.length + 1) }))) })
|
||||
);
|
||||
|
||||
return group(rows.map<R>(r => ({ ...r, _key: r.value.key }))) as any;
|
||||
}
|
||||
|
||||
const GridContext = createContext<GridContextType>();
|
||||
export function Grid(props: { class?: string, rows: Entry[], api?: (api: GridApi) => any }) {
|
||||
const rows = createMemo(() => createDataSet<Entry>(props.rows, { group: { by: 'key', with: groupBy } }));
|
||||
const locales = createMemo(() => Object.keys(rows().value().at(0) ?? {}).filter(k => k !== 'key'));
|
||||
const columns = createMemo<Column<Entry>[]>(() => [
|
||||
{
|
||||
id: 'key',
|
||||
label: 'Key',
|
||||
renderer: ({ value }) => value.split('.').at(-1),
|
||||
},
|
||||
...locales().map<Column<Entry>>(lang => ({
|
||||
id: lang,
|
||||
label: lang,
|
||||
renderer: ({ row, column, value, mutate }) => {
|
||||
const entry = rows().value()[row]!;
|
||||
|
||||
const useGrid = () => useContext(GridContext)!;
|
||||
|
||||
export const Grid: Component<{ class?: string, columns: string[], rows: Rows, api?: (api: GridApi) => any }> = (props) => {
|
||||
const [table, setTable] = createSignal();
|
||||
const [state, setState] = createStore<{ rows: Record<string, Record<string, string>>, columns: string[], snapshot: Rows, numberOfRows: number }>({
|
||||
rows: {},
|
||||
columns: [],
|
||||
snapshot: new Map,
|
||||
numberOfRows: 0,
|
||||
});
|
||||
|
||||
const mutations = createMemo(() => {
|
||||
// enumerate all values to make sure the memo is recalculated on any change
|
||||
Object.values(state.rows).map(entry => Object.values(entry));
|
||||
|
||||
return deepDiff(state.snapshot, state.rows).toArray();
|
||||
});
|
||||
|
||||
type Entry = { key: string, [lang: string]: string };
|
||||
|
||||
const groupBy = (rows: DataSetRowNode<Entry>[]) => {
|
||||
const group = (nodes: DataSetRowNode<Entry>[]): DataSetNode<Entry>[] => Object
|
||||
.entries(Object.groupBy(nodes, r => r.key.split('.').at(0)!) as Record<string, DataSetRowNode<Entry>[]>)
|
||||
.map<DataSetNode<Entry>>(([key, nodes]) => nodes.at(0)?.key === key
|
||||
? { ...nodes[0], key: nodes[0].value.key, value: { ...nodes[0].value, key: nodes[0].key } }
|
||||
: ({ kind: 'group', key, groupedBy: 'key', nodes: group(nodes.map(n => ({ ...n, key: n.key.slice(key.length + 1) }))) })
|
||||
);
|
||||
|
||||
return group(rows.map(r => ({ ...r, key: r.value.key })));
|
||||
}
|
||||
|
||||
const rows = createMemo(() => Object.entries(state.rows).map(([key, values]) => ({ key, ...values })));
|
||||
const columns = createMemo(() => [
|
||||
{ id: 'key', label: 'Key', groupBy },
|
||||
...state.columns.map(c => ({ id: c, label: c })),
|
||||
return <TextArea row={row} key={entry.key} lang={String(column)} value={value} oninput={e => mutate(e.data ?? '')} />;
|
||||
},
|
||||
}))
|
||||
]);
|
||||
|
||||
createEffect(() => {
|
||||
setState('rows', Object.fromEntries(deepCopy(props.rows).entries()));
|
||||
setState('snapshot', props.rows);
|
||||
const r = rows();
|
||||
|
||||
props.api?.({
|
||||
mutations: r.mutations,
|
||||
remove: r.remove,
|
||||
addKey(key) {
|
||||
r.insert({ key, ...Object.fromEntries(locales().map(l => [l, ''])) });
|
||||
},
|
||||
addLocale(locale) {
|
||||
r.mutateEach(entry => ({ ...entry, [locale]: '' }));
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
setState('columns', [...props.columns]);
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
setState('numberOfRows', Object.keys(state.rows).length);
|
||||
});
|
||||
|
||||
const ctx: GridContextType = {
|
||||
rows,
|
||||
mutations,
|
||||
// selection,
|
||||
|
||||
mutate(prop: string, value: string) {
|
||||
const [key, lang] = splitAt(prop, prop.lastIndexOf('.'));
|
||||
|
||||
setState('rows', key, lang, value);
|
||||
},
|
||||
|
||||
remove(props: string[]) {
|
||||
setState('rows', produce(rows => {
|
||||
for (const prop of props) {
|
||||
delete rows[prop];
|
||||
}
|
||||
|
||||
return rows;
|
||||
}));
|
||||
},
|
||||
|
||||
insert(prop: string) {
|
||||
setState('rows', prop, Object.fromEntries(state.columns.map(lang => [lang, ''])));
|
||||
},
|
||||
|
||||
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]: '' }]));
|
||||
|
||||
return state;
|
||||
}))
|
||||
},
|
||||
};
|
||||
|
||||
return <GridContext.Provider value={ctx}>
|
||||
<Api api={props.api} table={table()} />
|
||||
|
||||
<Table api={setTable} class={props.class} rows={rows()} columns={columns()} groupBy="key" selectionMode={SelectionMode.Multiple}>{
|
||||
Object.fromEntries(state.columns.map(c => [c, ({ key, value }: any) => {
|
||||
return <TextArea key={key} value={value} oninput={(e) => ctx.mutate(key, e.data ?? '')} />;
|
||||
}]))
|
||||
}</Table>
|
||||
</GridContext.Provider>;
|
||||
return <GridComp rows={rows()} columns={columns()} />;
|
||||
};
|
||||
|
||||
const Api: Component<{ api: undefined | ((api: GridApi) => any), table?: any }> = (props) => {
|
||||
const gridContext = useGrid();
|
||||
|
||||
const api = createMemo<GridApi | undefined>(() => {
|
||||
const table = props.table;
|
||||
|
||||
if (!table) {
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
selection: createMemo(() => {
|
||||
const selection = props.table?.selection() ?? [];
|
||||
|
||||
return Object.fromEntries(selection.map(({ key, value }) => [key, value()] as const));
|
||||
}),
|
||||
rows: createMemo(() => props.table?.rows ?? []),
|
||||
mutations: gridContext.mutations,
|
||||
selectAll() {
|
||||
props.table.selectAll();
|
||||
},
|
||||
clear() {
|
||||
props.table.clear();
|
||||
},
|
||||
remove(props: string[]) {
|
||||
gridContext.remove(props);
|
||||
},
|
||||
insert(prop: string) {
|
||||
gridContext.insert(prop);
|
||||
},
|
||||
addColumn(name: string): void {
|
||||
gridContext.addColumn(name);
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
const value = api();
|
||||
|
||||
if (value) {
|
||||
props.api?.(value);
|
||||
}
|
||||
});
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const TextArea: Component<{ key: string, value: string, oninput?: (event: InputEvent) => any }> = (props) => {
|
||||
const TextArea: Component<{ row: number, key: string, lang: string, value: string, oninput?: (event: InputEvent) => any }> = (props) => {
|
||||
const [element, setElement] = createSignal<HTMLTextAreaElement>();
|
||||
const key = createMemo(() => props.key.slice(0, props.key.lastIndexOf('.')));
|
||||
const lang = createMemo(() => props.key.slice(props.key.lastIndexOf('.') + 1));
|
||||
|
||||
const resize = () => {
|
||||
const el = element();
|
||||
|
@ -205,10 +90,11 @@ const TextArea: Component<{ key: string, value: string, oninput?: (event: InputE
|
|||
|
||||
return <textarea
|
||||
ref={setElement}
|
||||
class={css.textarea}
|
||||
value={props.value}
|
||||
lang={lang()}
|
||||
placeholder={`${key()} in ${lang()}`}
|
||||
name={`${key()}:${lang()}`}
|
||||
lang={props.lang}
|
||||
placeholder={`${props.key} in ${props.lang}`}
|
||||
name={`${props.row}[${props.lang}]`}
|
||||
spellcheck={true}
|
||||
wrap="soft"
|
||||
onkeyup={onKeyUp}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
.root {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
& > div {
|
||||
display: contents;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import { emptyFolder, FolderEntry, walk as fileTreeWalk, Tree } from "~/componen
|
|||
import { Menu } from "~/features/menu";
|
||||
import { Grid, load, useFiles } from "~/features/file";
|
||||
import { Command, CommandType, Context, createCommand, Modifier, noop, useCommands } from "~/features/command";
|
||||
import { GridApi } from "~/features/file/grid";
|
||||
import { Entry, GridApi } from "~/features/file/grid";
|
||||
import { Tab, Tabs } from "~/components/tabs";
|
||||
import { isServer } from "solid-js/web";
|
||||
import { Prompt, PromptApi } from "~/components/prompt";
|
||||
|
@ -38,7 +38,8 @@ async function* walk(directory: FileSystemDirectoryHandle, path: string[] = []):
|
|||
};
|
||||
|
||||
|
||||
interface Entries extends Map<string, Record<string, { value: string, handle: FileSystemFileHandle, id: string }>> { }
|
||||
// interface Entries extends Map<string, Record<string, { value: string, handle: FileSystemFileHandle, id: string }>> { };
|
||||
interface Entries extends Map<string, { key: string, } & Record<string, { value: string, handle: FileSystemFileHandle, id: string }>> { };
|
||||
|
||||
export default function Edit(props: ParentProps) {
|
||||
const filesContext = useFiles();
|
||||
|
@ -105,26 +106,22 @@ const Editor: Component<{ root: FileSystemDirectoryHandle }> = (props) => {
|
|||
const files = tab.files();
|
||||
const mutations = tab.api()?.mutations() ?? [];
|
||||
|
||||
// console.log(mutations);
|
||||
return mutations.flatMap((m): any => {
|
||||
const [index, lang] = splitAt(m.key, m.key.indexOf('.'));
|
||||
|
||||
return mutations.flatMap(m => {
|
||||
switch (m.kind) {
|
||||
case MutarionKind.Update: {
|
||||
const [key, lang] = splitAt(m.key, m.key.lastIndexOf('.'));
|
||||
|
||||
return { kind: MutarionKind.Update, key, lang, file: entries.get(key)?.[lang] };
|
||||
const entry = entries.get(index as any)!;
|
||||
return { kind: MutarionKind.Update, key: entry.key, lang, file: files.get(lang)! };
|
||||
}
|
||||
|
||||
case MutarionKind.Create: {
|
||||
if (typeof m.value === 'object') {
|
||||
return Object.entries(m.value).map(([lang, value]) => {
|
||||
return ({ kind: MutarionKind.Create, key: m.key, lang, file: files.get(lang)!, value });
|
||||
});
|
||||
return Object.entries(m.value).map(([lang, value]) => ({ kind: MutarionKind.Create, key: m.key, lang, file: files.get(lang)!, value }));
|
||||
}
|
||||
|
||||
const [key, lang] = splitAt(m.key, m.key.lastIndexOf('.'));
|
||||
|
||||
return { kind: MutarionKind.Create, key, lang, file: undefined, value: m.value };
|
||||
const entry = entries.get(index as any)!;
|
||||
return { kind: MutarionKind.Create, key: entry.key, lang, file: undefined, value: m.value };
|
||||
}
|
||||
|
||||
case MutarionKind.Delete: {
|
||||
|
@ -226,6 +223,11 @@ const Editor: Component<{ root: FileSystemDirectoryHandle }> = (props) => {
|
|||
return existingFiles.concat(newFiles);
|
||||
});
|
||||
|
||||
// createEffect(() => {
|
||||
// console.table(mutations());
|
||||
// console.log(mutatedFiles(), mutatedData());
|
||||
// });
|
||||
|
||||
createEffect(() => {
|
||||
const directory = props.root;
|
||||
|
||||
|
@ -296,19 +298,17 @@ const Editor: Component<{ root: FileSystemDirectoryHandle }> = (props) => {
|
|||
return;
|
||||
}
|
||||
|
||||
api()?.insert(key);
|
||||
api()?.addKey(key);
|
||||
}),
|
||||
inserNewLanguage: createCommand('insert new language', async () => {
|
||||
const formData = await newLanguagePrompt()?.showModal();
|
||||
const language = formData?.get('locale')?.toString();
|
||||
const locale = formData?.get('locale')?.toString();
|
||||
|
||||
if (!language) {
|
||||
if (!locale) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(language);
|
||||
|
||||
api()?.addColumn(language);
|
||||
api()?.addLocale(locale);
|
||||
}),
|
||||
} as const;
|
||||
|
||||
|
@ -375,11 +375,7 @@ const Editor: Component<{ root: FileSystemDirectoryHandle }> = (props) => {
|
|||
|
||||
<Tabs class={css.content} active={setActive} onClose={commands.closeTab}>
|
||||
<For each={tabs()}>{
|
||||
({ key, handle, setApi, setEntries }) => <Tab
|
||||
id={key}
|
||||
label={handle.name}
|
||||
closable
|
||||
>
|
||||
({ key, handle, setApi, setEntries }) => <Tab id={key} label={handle.name} closable>
|
||||
<Content directory={handle} api={setApi} entries={setEntries} />
|
||||
</Tab>
|
||||
}</For>
|
||||
|
@ -389,8 +385,7 @@ const Editor: Component<{ root: FileSystemDirectoryHandle }> = (props) => {
|
|||
|
||||
const Content: Component<{ directory: FileSystemDirectoryHandle, api?: Setter<GridApi | undefined>, entries?: Setter<Entries> }> = (props) => {
|
||||
const [entries, setEntries] = createSignal<Entries>(new Map());
|
||||
const [columns, setColumns] = createSignal<string[]>([]);
|
||||
const [rows, setRows] = createSignal<Map<string, Record<string, string>>>(new Map);
|
||||
const [rows, setRows] = createSignal<Entry[]>([]);
|
||||
const [api, setApi] = createSignal<GridApi>();
|
||||
|
||||
createEffect(() => {
|
||||
|
@ -420,7 +415,6 @@ const Content: Component<{ directory: FileSystemDirectoryHandle, api?: Setter<Gr
|
|||
return { id, handle, lang, entries };
|
||||
}
|
||||
);
|
||||
const languages = new Set(contents.map(c => c.lang));
|
||||
const template = contents.map(({ lang, handle }) => [lang, { handle, value: '' }]);
|
||||
|
||||
const merged = contents.reduce((aggregate, { id, handle, lang, entries }) => {
|
||||
|
@ -435,13 +429,12 @@ const Content: Component<{ directory: FileSystemDirectoryHandle, api?: Setter<Gr
|
|||
return aggregate;
|
||||
}, new Map<string, Record<string, { id: string, value: string, handle: FileSystemFileHandle }>>());
|
||||
|
||||
setColumns(languages.values().toArray());
|
||||
setEntries(merged);
|
||||
setRows(new Map(merged.entries().map(([key, langs]) => [key, Object.fromEntries(Object.entries(langs).map(([lang, { value }]) => [lang, value]))] as const)));
|
||||
setEntries(new Map(merged.entries().map(([key, langs], i) => [i.toString(), { key, ...langs }])) as Entries);
|
||||
setRows(merged.entries().map(([key, langs]) => ({ key, ...Object.fromEntries(Object.entries(langs).map(([lang, { value }]) => [lang, value])) } as Entry)).toArray());
|
||||
})();
|
||||
});
|
||||
|
||||
return <Grid columns={columns()} rows={rows()} api={setApi} />;
|
||||
return <Grid rows={rows()} api={setApi} />;
|
||||
};
|
||||
|
||||
const Blank: Component<{ open: CommandType }> = (props) => {
|
||||
|
|
|
@ -26,37 +26,37 @@ export default function GridExperiment() {
|
|||
id: 'name',
|
||||
label: 'Name',
|
||||
sortable: true,
|
||||
editor,
|
||||
renderer: editor,
|
||||
},
|
||||
{
|
||||
id: 'email',
|
||||
label: 'Email',
|
||||
sortable: true,
|
||||
editor,
|
||||
renderer: editor,
|
||||
},
|
||||
{
|
||||
id: 'address',
|
||||
label: 'Address',
|
||||
sortable: true,
|
||||
editor,
|
||||
renderer: editor,
|
||||
},
|
||||
{
|
||||
id: 'currency',
|
||||
label: 'Currency',
|
||||
sortable: true,
|
||||
editor,
|
||||
renderer: editor,
|
||||
},
|
||||
{
|
||||
id: 'phone',
|
||||
label: 'Phone',
|
||||
sortable: true,
|
||||
editor,
|
||||
renderer: editor,
|
||||
},
|
||||
{
|
||||
id: 'country',
|
||||
label: 'Country',
|
||||
sortable: true,
|
||||
editor,
|
||||
renderer: editor,
|
||||
},
|
||||
];
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue