This commit is contained in:
Chris Kruining 2024-12-17 08:18:25 +01:00
parent b23db1d5a8
commit 1d88565773
No known key found for this signature in database
GPG key ID: EB894A3560CCCAD2
17 changed files with 360 additions and 229 deletions

View file

@ -1,28 +1,28 @@
import { Accessor, createContext, createEffect, createMemo, createSignal, JSX, useContext } from "solid-js";
import { createStore } from "solid-js/store";
import { deepCopy, deepDiff, Mutation } from "~/utilities";
import { SelectionMode, Table, Column as TableColumn, TableApi } from "~/components/table";
import { SelectionMode, Table, Column as TableColumn, TableApi, CellEditors, CellEditor, createDataSet, DataSet, DataSetNode } from "~/components/table";
import css from './grid.module.css';
export interface Column<T extends Record<string, any>> extends TableColumn<T> {
editor?: (cell: { id: keyof T, value: T[keyof T] }) => JSX.Element;
editor?: (cell: { row: number, column: keyof T, value: T[keyof T], mutate: (next: T[keyof T]) => any }) => JSX.Element;
}
export interface GridApi<T extends Record<string, any>> extends TableApi<T> {
readonly mutations: Accessor<Mutation[]>;
remove(keys: string[]): void;
insert(prop: string): void;
addColumn(name: string): void;
remove(keys: number[]): void;
insert(row: T, at?: number): void;
addColumn(column: keyof T): void;
}
interface GridContextType<T extends Record<string, any>> {
readonly rows: Accessor<T[]>;
readonly rows: Accessor<DataSetNode<keyof T, T>[]>;
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;
readonly selection: TableApi<T>['selection'];
mutate<K extends keyof T>(row: number, column: K, value: T[K]): void;
remove(rows: number[]): void;
insert(row: T, at?: number): void;
addColumn(column: keyof T, value: T[keyof T]): void;
}
const GridContext = createContext<GridContextType<any>>();
@ -30,65 +30,68 @@ const GridContext = createContext<GridContextType<any>>();
const useGrid = () => useContext(GridContext)!;
type GridProps<T extends Record<string, any>> = { class?: string, groupBy?: keyof T, columns: Column<T>[], rows: T[], api?: (api: GridApi<T>) => any };
// type GridState<T extends Record<string, any>> = { data: DataSet<T>, columns: Column<T>[], numberOfRows: number };
export function Grid<T extends Record<string, any>>(props: GridProps<T>) {
const [table, setTable] = createSignal<TableApi<T>>();
const [state, setState] = createStore<{ rows: T[], columns: Column<T>[], snapshot: T[], numberOfRows: number }>({
rows: [],
columns: [],
snapshot: [],
numberOfRows: 0,
});
const data = createMemo(() => createDataSet(props.rows));
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();
});
createEffect(() => {
setState('rows', Object.fromEntries(deepCopy(props.rows).entries()));
setState('snapshot', props.rows);
});
createEffect(() => {
setState('columns', props.columns);
});
createEffect(() => {
setState('numberOfRows', Object.keys(state.rows).length);
});
const rows = createMemo(() => state.rows);
const columns = createMemo(() => state.columns);
const rows = createMemo(() => data().value());
const mutations = createMemo(() => data().mutations());
const columns = createMemo(() => props.columns);
const ctx: GridContextType<T> = {
rows,
mutations,
// selection,
selection: createMemo(() => table()?.selection() ?? []),
mutate(prop: string, value: string) {
mutate<K extends keyof T>(row: number, column: K, value: T[K]) {
data().mutate(row, column, value);
},
remove(props: string[]) {
remove(rows: number[]) {
// setState('rows', (r) => r.filter((_, i) => rows.includes(i) === false));
},
insert(prop: string) {
insert(row: T, at?: number) {
if (at === undefined) {
// setState('rows', state.rows.length, row);
} else {
}
},
addColumn(id: keyof T): void {
addColumn(column: keyof T, value: T[keyof T]): void {
// setState('rows', { from: 0, to: state.rows.length - 1 }, column as any, value);
},
};
const cellEditors = createMemo(() => Object.fromEntries(state.columns.filter(c => c.editor !== undefined).map(c => [c.id, c.editor!] as const)));
const cellEditors = createMemo(() => Object.fromEntries(
props.columns
.filter(c => c.editor !== undefined)
.map(c => {
const Editor: CellEditor<T, keyof T> = ({ row, column, value }) => {
const mutate = (next: T[keyof T]) => {
console.log('KAAS', { next })
ctx.mutate(row, column, next);
};
return c.editor!({ row, column, value, mutate });
};
return [c.id, Editor] as const;
})
) as any);
return <GridContext.Provider value={ctx}>
<Api api={props.api} table={table()} />
<Table api={setTable} class={`${css.grid} ${props.class}`} rows={rows()} columns={columns()} selectionMode={SelectionMode.Multiple}>{
cellEditors()
}</Table>
<form style="all: inherit; display: contents;">
<Table api={setTable} class={`${css.grid} ${props.class}`} rows={data()} columns={columns()} selectionMode={SelectionMode.Multiple}>{
cellEditors()
}</Table>
</form>
</GridContext.Provider>;
};
@ -105,14 +108,14 @@ function Api<T extends Record<string, any>>(props: { api: undefined | ((api: Gri
return {
...table,
mutations: gridContext.mutations,
remove(props: string[]) {
gridContext.remove(props);
remove(rows: number[]) {
gridContext.remove(rows);
},
insert(prop: string) {
gridContext.insert(prop);
insert(row: T, at?: number) {
gridContext.insert(row, at);
},
addColumn(name: string): void {
gridContext.addColumn(name);
addColumn(column: keyof T, value: T[keyof T]): void {
gridContext.addColumn(column, value);
},
};
});

View file

@ -1,4 +1,4 @@
export type { DataSetRowNode, DataSetGroupNode, DataSetNode, SelectionMode } from '../table';
export type { DataSetRowNode, DataSetGroupNode, DataSetNode, SelectionMode, SortingFunction, SortOptions, GroupingFunction, GroupOptions } from '../table';
export type { GridApi, Column } from './grid';
export { Grid } from './grid';