woop in a working state again
This commit is contained in:
parent
4a5f0cf2d1
commit
ab68df340f
15 changed files with 259 additions and 159 deletions
|
@ -1,11 +1,14 @@
|
|||
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, CellEditors, CellEditor, createDataSet, DataSet, DataSetNode } from "~/components/table";
|
||||
import { Mutation } from "~/utilities";
|
||||
import { SelectionMode, Table, Column as TableColumn, TableApi, DataSet, CellRenderer } 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 Column<T extends Record<string, any>> extends TableColumn<T> {
|
||||
editor?: (cell: { row: number, column: keyof T, value: T[keyof T], mutate: (next: T[keyof T]) => any }) => JSX.Element;
|
||||
editor?: CellEditor<T, keyof T>;
|
||||
}
|
||||
|
||||
export interface GridApi<T extends Record<string, any>> extends TableApi<T> {
|
||||
|
@ -16,7 +19,6 @@ export interface GridApi<T extends Record<string, any>> extends TableApi<T> {
|
|||
}
|
||||
|
||||
interface GridContextType<T extends Record<string, any>> {
|
||||
readonly rows: Accessor<DataSetNode<keyof T, T>[]>;
|
||||
readonly mutations: Accessor<Mutation[]>;
|
||||
readonly selection: TableApi<T>['selection'];
|
||||
mutate<K extends keyof T>(row: number, column: K, value: T[K]): void;
|
||||
|
@ -29,36 +31,31 @@ 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 GridProps<T extends Record<string, any>> = { class?: string, groupBy?: keyof T, columns: Column<T>[], rows: DataSet<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 data = createMemo(() => createDataSet(props.rows));
|
||||
|
||||
const rows = createMemo(() => data().value());
|
||||
const mutations = createMemo(() => data().mutations());
|
||||
const rows = createMemo(() => props.rows);
|
||||
const columns = createMemo(() => props.columns);
|
||||
const mutations = createMemo(() => rows().mutations());
|
||||
|
||||
const ctx: GridContextType<T> = {
|
||||
rows,
|
||||
mutations,
|
||||
selection: createMemo(() => table()?.selection() ?? []),
|
||||
|
||||
mutate<K extends keyof T>(row: number, column: K, value: T[K]) {
|
||||
data().mutate(row, column, value);
|
||||
rows().mutate(row, column, value);
|
||||
},
|
||||
|
||||
remove(rows: number[]) {
|
||||
// setState('rows', (r) => r.filter((_, i) => rows.includes(i) === false));
|
||||
remove(indices: number[]) {
|
||||
rows().remove(indices);
|
||||
table()?.clear();
|
||||
},
|
||||
|
||||
insert(row: T, at?: number) {
|
||||
if (at === undefined) {
|
||||
// setState('rows', state.rows.length, row);
|
||||
} else {
|
||||
|
||||
}
|
||||
rows().insert(row, at);
|
||||
},
|
||||
|
||||
addColumn(column: keyof T, value: T[keyof T]): void {
|
||||
|
@ -70,10 +67,8 @@ export function Grid<T extends Record<string, any>>(props: GridProps<T>) {
|
|||
props.columns
|
||||
.filter(c => c.editor !== undefined)
|
||||
.map(c => {
|
||||
const Editor: CellEditor<T, keyof T> = ({ row, column, value }) => {
|
||||
const Editor: CellRenderer<T, keyof T> = ({ row, column, value }) => {
|
||||
const mutate = (next: T[keyof T]) => {
|
||||
console.log('KAAS', { next })
|
||||
|
||||
ctx.mutate(row, column, next);
|
||||
};
|
||||
|
||||
|
@ -87,11 +82,9 @@ export function Grid<T extends Record<string, any>>(props: GridProps<T>) {
|
|||
return <GridContext.Provider value={ctx}>
|
||||
<Api api={props.api} table={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>
|
||||
<Table api={setTable} class={`${css.grid} ${props.class}`} rows={rows()} columns={columns()} selectionMode={SelectionMode.Multiple}>{
|
||||
cellEditors()
|
||||
}</Table>
|
||||
</GridContext.Provider>;
|
||||
};
|
||||
|
||||
|
@ -114,8 +107,8 @@ function Api<T extends Record<string, any>>(props: { api: undefined | ((api: Gri
|
|||
insert(row: T, at?: number) {
|
||||
gridContext.insert(row, at);
|
||||
},
|
||||
addColumn(column: keyof T, value: T[keyof T]): void {
|
||||
gridContext.addColumn(column, value);
|
||||
addColumn(column: keyof T): void {
|
||||
// gridContext.addColumn(column, value);
|
||||
},
|
||||
};
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
export type { DataSetRowNode, DataSetGroupNode, DataSetNode, SelectionMode, SortingFunction, SortOptions, GroupingFunction, GroupOptions } from '../table';
|
||||
export type { GridApi, Column } from './grid';
|
||||
export type { GridApi, Column, CellEditor } from './grid';
|
||||
export { Grid } from './grid';
|
|
@ -1,4 +1,4 @@
|
|||
import { createEffect, createSignal, createUniqueId, JSX, onMount, ParentComponent, Show } from "solid-js";
|
||||
import { createEffect, createSignal, JSX, ParentComponent, Show } from "solid-js";
|
||||
import css from './prompt.module.css';
|
||||
|
||||
export interface PromptApi {
|
||||
|
@ -72,4 +72,7 @@ export const Prompt: ParentComponent<{ api: (api: PromptApi) => any, title?: str
|
|||
</footer>
|
||||
</form>
|
||||
</dialog>;
|
||||
};
|
||||
};
|
||||
|
||||
let idCounter = 0;
|
||||
const createUniqueId = () => `prompt-${idCounter++}`;
|
|
@ -1,6 +1,6 @@
|
|||
import { Accessor, createMemo } from "solid-js";
|
||||
import { createStore, NotWrappable, StoreSetter } from "solid-js/store";
|
||||
import { snapshot } from "vinxi/dist/types/runtime/storage";
|
||||
import { Accessor, createEffect, createMemo } from "solid-js";
|
||||
import { createStore, NotWrappable, StoreSetter, unwrap } from "solid-js/store";
|
||||
import { CustomPartial } from "solid-js/store/types/store.js";
|
||||
import { deepCopy, deepDiff, Mutation } from "~/utilities";
|
||||
|
||||
|
||||
|
@ -17,54 +17,63 @@ export interface SortOptions<T extends Record<string, any>> {
|
|||
with?: SortingFunction<T>;
|
||||
}
|
||||
|
||||
export interface GroupingFunction<T> {
|
||||
(nodes: DataSetRowNode<keyof T, T>[]): DataSetNode<keyof T, T>[];
|
||||
export interface GroupingFunction<K, T> {
|
||||
(nodes: DataSetRowNode<K, T>[]): DataSetNode<K, T>[];
|
||||
}
|
||||
export interface GroupOptions<T extends Record<string, any>> {
|
||||
by: keyof T;
|
||||
with: GroupingFunction<T>;
|
||||
with?: GroupingFunction<number, T>;
|
||||
}
|
||||
interface DataSetState<T extends Record<string, any>> {
|
||||
value: DataSetRowNode<keyof T, T>[];
|
||||
snapshot: DataSetRowNode<keyof T, T>[];
|
||||
value: (T | undefined)[];
|
||||
snapshot: (T | undefined)[];
|
||||
sorting?: SortOptions<T>;
|
||||
grouping?: GroupOptions<T>;
|
||||
}
|
||||
|
||||
export type Setter<T> =
|
||||
| T
|
||||
| CustomPartial<T>
|
||||
| ((prevState: T) => T | CustomPartial<T>);
|
||||
|
||||
export interface DataSet<T extends Record<string, any>> {
|
||||
data: T[];
|
||||
value: Accessor<DataSetNode<keyof T, T>[]>;
|
||||
mutations: Accessor<Mutation[]>;
|
||||
sort: Accessor<SortOptions<T> | undefined>;
|
||||
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;
|
||||
remove(indices: number[]): void;
|
||||
insert(item: T, at?: number): void;
|
||||
|
||||
setSorting(options: SortOptions<T> | undefined): void;
|
||||
setGrouping(options: GroupOptions<T> | undefined): void;
|
||||
sort(options: Setter<SortOptions<T> | undefined>): DataSet<T>;
|
||||
group(options: Setter<GroupOptions<T> | undefined>): DataSet<T>;
|
||||
}
|
||||
|
||||
const defaultComparer = <T>(a: T, b: T) => a < b ? -1 : a > b ? 1 : 0;
|
||||
function defaultGroupingFunction<T>(groupBy: keyof T): GroupingFunction<T> {
|
||||
return (nodes: DataSetRowNode<keyof T, T>[]): DataSetNode<keyof T, T>[] => Object.entries(Object.groupBy(nodes, r => r.value[groupBy] as PropertyKey))
|
||||
.map(([key, nodes]) => ({ kind: 'group', key, groupedBy: groupBy, nodes: nodes! } as DataSetGroupNode<keyof T, T>));
|
||||
function defaultGroupingFunction<T>(groupBy: keyof T): GroupingFunction<number, T> {
|
||||
return <K>(nodes: DataSetRowNode<K, T>[]): DataSetNode<K, T>[] => Object.entries(Object.groupBy(nodes, r => r.value[groupBy] as PropertyKey))
|
||||
.map(([key, nodes]) => ({ kind: 'group', key, groupedBy: groupBy, nodes: nodes! } as DataSetGroupNode<K, T>));
|
||||
}
|
||||
|
||||
export const createDataSet = <T extends Record<string, any>>(data: T[]): DataSet<T> => {
|
||||
const nodes = data.map<DataSetRowNode<keyof T, T>>((value, key) => ({ kind: 'row', key: key as keyof T, value }));
|
||||
|
||||
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,
|
||||
sorting: undefined,
|
||||
grouping: undefined,
|
||||
sorting: initialOptions?.sort,
|
||||
grouping: initialOptions?.group,
|
||||
});
|
||||
|
||||
const value = createMemo(() => {
|
||||
const sorting = state.sorting;
|
||||
const grouping = state.grouping;
|
||||
|
||||
let value = state.value as DataSetNode<keyof T, T>[];
|
||||
let value: DataSetNode<number, T>[] = state.value
|
||||
.map<DataSetRowNode<number, T> | undefined>((value, key) => value === undefined ? undefined : ({ kind: 'row', key, value }))
|
||||
.filter(node => node !== undefined);
|
||||
|
||||
if (sorting) {
|
||||
const comparer = sorting.with ?? defaultComparer;
|
||||
|
@ -79,37 +88,57 @@ export const createDataSet = <T extends Record<string, any>>(data: T[]): DataSet
|
|||
if (grouping) {
|
||||
const implementation = grouping.with ?? defaultGroupingFunction(grouping.by);
|
||||
|
||||
value = implementation(value as DataSetRowNode<keyof T, T>[]);
|
||||
value = implementation(value as DataSetRowNode<number, T>[]);
|
||||
}
|
||||
|
||||
return value;
|
||||
return value as DataSetNode<keyof T, T>[];
|
||||
});
|
||||
|
||||
const mutations = createMemo(() => {
|
||||
// enumerate all values to make sure the memo is recalculated on any change
|
||||
Object.values(state.value).map(entry => Object.values(entry));
|
||||
Object.values(state.value).map(entry => Object.values(entry ?? {}));
|
||||
|
||||
return deepDiff(state.snapshot, state.value).toArray();
|
||||
});
|
||||
const sort = createMemo(() => state.sorting);
|
||||
|
||||
return {
|
||||
const sorting = createMemo(() => state.sorting);
|
||||
const grouping = createMemo(() => state.grouping);
|
||||
|
||||
const set: DataSet<T> = {
|
||||
data,
|
||||
value,
|
||||
mutations,
|
||||
sort,
|
||||
sorting,
|
||||
grouping,
|
||||
|
||||
mutate(index, prop, value) {
|
||||
console.log({ index, prop, value });
|
||||
// setState('value', index, 'value', prop as any, value);
|
||||
setState('value', index, prop as any, value);
|
||||
},
|
||||
|
||||
setSorting(options) {
|
||||
remove(indices) {
|
||||
setState('value', value => value.map((item, i) => indices.includes(i) ? undefined : item));
|
||||
},
|
||||
|
||||
insert(item, at) {
|
||||
if (at === undefined) {
|
||||
setState('value', state.value.length, item);
|
||||
} else {
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
sort(options) {
|
||||
setState('sorting', options);
|
||||
|
||||
return set;
|
||||
},
|
||||
|
||||
setGrouping(options) {
|
||||
group(options) {
|
||||
setState('grouping', options)
|
||||
|
||||
return set;
|
||||
},
|
||||
};
|
||||
|
||||
return set
|
||||
};
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
export type { Column, TableApi, CellEditor, CellEditors } from './table';
|
||||
export type { Column, TableApi, CellRenderer, CellRenderers } from './table';
|
||||
export type { DataSet, DataSetGroupNode, DataSetRowNode, DataSetNode, SortingFunction, SortOptions, GroupingFunction, GroupOptions } from './dataset';
|
||||
export { SelectionMode, Table } from './table';
|
||||
export { createDataSet } from './dataset';
|
|
@ -32,7 +32,7 @@
|
|||
& :is(.cell:first-child, .checkbox + .cell) {
|
||||
position: sticky;
|
||||
inset-inline-start: 1px;
|
||||
padding-inline-start: calc(var(--depth, 0) * 1em + var(--padding-m));
|
||||
padding-inline-start: calc(var(--depth, 0) * (1em + var(--padding-s)) + var(--padding-m));
|
||||
z-index: 1;
|
||||
|
||||
&::after {
|
||||
|
@ -61,7 +61,6 @@
|
|||
}
|
||||
|
||||
& .caption {
|
||||
/* grid-column: 1 / -1; */
|
||||
position: sticky;
|
||||
inset-inline-start: 0;
|
||||
}
|
||||
|
@ -148,40 +147,58 @@
|
|||
font-weight: var(--text-bold);
|
||||
}
|
||||
|
||||
& details {
|
||||
& .group {
|
||||
display: contents;
|
||||
background-color: inherit;
|
||||
|
||||
&::details-content {
|
||||
grid-column: 1 / -1;
|
||||
display: block grid;
|
||||
grid-template-columns: subgrid;
|
||||
& > td {
|
||||
display: contents;
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
&:not([open])::details-content {
|
||||
display: none;
|
||||
}
|
||||
& > table {
|
||||
grid-column: 1 / -1;
|
||||
grid-template-columns: subgrid;
|
||||
background-color: inherit;
|
||||
overflow: visible;
|
||||
|
||||
& > summary {
|
||||
position: sticky;
|
||||
inset-inline-start: 1px;
|
||||
grid-column: 1;
|
||||
padding: var(--padding-m);
|
||||
padding-inline-start: calc(var(--depth) * 1em + var(--padding-m));
|
||||
& > .header {
|
||||
border-block-end-color: transparent;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset-inline-start: calc(100% - 1px);
|
||||
inset-block-start: -.5px;
|
||||
display: block;
|
||||
inline-size: 2em;
|
||||
block-size: 100%;
|
||||
animation: column-scroll-shadow linear both;
|
||||
animation-timeline: scroll(inline);
|
||||
animation-range: 0 2em;
|
||||
pointer-events: none;
|
||||
& .cell {
|
||||
justify-content: start;
|
||||
column-gap: var(--padding-s);
|
||||
|
||||
& > label {
|
||||
--state: 0;
|
||||
display: contents;
|
||||
|
||||
& input[type="checkbox"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
& > svg {
|
||||
rotate: calc(var(--state) * -.25turn);
|
||||
transition: rotate .3s ease-in-out;
|
||||
inline-size: 1em;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
&:has(input:not(:checked)) {
|
||||
--state: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& > .main {
|
||||
block-size: calc-size(auto, size);
|
||||
transition: block-size .3s ease-in-out;
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
&:has(> .header input:not(:checked)) > .main {
|
||||
block-size: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Accessor, createContext, createEffect, createMemo, createSignal, For, JSX, Match, Show, Switch, useContext } from "solid-js";
|
||||
import { selectable, SelectionItem, SelectionProvider, useSelection } from "~/features/selectable";
|
||||
import { DataSetRowNode, DataSetNode, DataSet } from './dataset';
|
||||
import { FaSolidSort, FaSolidSortDown, FaSolidSortUp } from "solid-icons/fa";
|
||||
import { FaSolidAngleDown, FaSolidSort, FaSolidSortDown, FaSolidSortUp } from "solid-icons/fa";
|
||||
import css from './table.module.css';
|
||||
|
||||
selectable;
|
||||
|
@ -14,8 +14,8 @@ export type Column<T> = {
|
|||
readonly groupBy?: (rows: DataSetRowNode<keyof T, T>[]) => DataSetNode<keyof T, T>[],
|
||||
};
|
||||
|
||||
export type CellEditor<T extends Record<string, any>, K extends keyof T> = (cell: { row: number, column: K, value: T[K] }) => JSX.Element;
|
||||
export type CellEditors<T extends Record<string, any>> = { [K in keyof T]?: CellEditor<T, K> };
|
||||
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>[]>;
|
||||
|
@ -30,7 +30,7 @@ interface TableContextType<T extends Record<string, any>> {
|
|||
readonly columns: Accessor<Column<T>[]>,
|
||||
readonly selection: Accessor<SelectionItem<keyof T, T>[]>,
|
||||
readonly selectionMode: Accessor<SelectionMode>,
|
||||
readonly cellRenderers: Accessor<CellEditors<T>>,
|
||||
readonly cellRenderers: Accessor<CellRenderers<T>>,
|
||||
}
|
||||
|
||||
const TableContext = createContext<TableContextType<any>>();
|
||||
|
@ -48,7 +48,7 @@ type TableProps<T extends Record<string, any>> = {
|
|||
rows: DataSet<T>,
|
||||
columns: Column<T>[],
|
||||
selectionMode?: SelectionMode,
|
||||
children?: CellEditors<T>,
|
||||
children?: CellRenderers<T>,
|
||||
api?: (api: TableApi<T>) => any,
|
||||
};
|
||||
|
||||
|
@ -58,7 +58,7 @@ export function Table<T extends Record<string, any>>(props: TableProps<T>) {
|
|||
const rows = createMemo(() => props.rows);
|
||||
const columns = createMemo<Column<T>[]>(() => props.columns ?? []);
|
||||
const selectionMode = createMemo(() => props.selectionMode ?? SelectionMode.None);
|
||||
const cellRenderers = createMemo<CellEditors<T>>(() => props.children ?? {});
|
||||
const cellRenderers = createMemo<CellRenderers<T>>(() => props.children ?? {});
|
||||
|
||||
const context: TableContextType<T> = {
|
||||
rows,
|
||||
|
@ -86,11 +86,11 @@ function InnerTable<T extends Record<string, any>>(props: InnerTableProps<T>) {
|
|||
const columnCount = createMemo(() => table.columns().length);
|
||||
|
||||
return <table class={`${css.table} ${selectable() ? css.selectable : ''} ${props.class}`} style={{ '--columns': columnCount() }}>
|
||||
<Show when={(props.summary?.length ?? 0) > 0 ? props.summary : undefined}>{
|
||||
{/* <Show when={(props.summary?.length ?? 0) > 0 ? props.summary : undefined}>{
|
||||
summary => {
|
||||
return <caption class={css.caption}>{summary()}</caption>;
|
||||
}
|
||||
}</Show>
|
||||
}</Show> */}
|
||||
|
||||
<Groups />
|
||||
<Head />
|
||||
|
@ -165,7 +165,7 @@ function Head(props: {}) {
|
|||
|
||||
<For each={table.columns()}>{
|
||||
({ id, label, sortable }) => {
|
||||
const sort = createMemo(() => table.rows().sort());
|
||||
const sort = createMemo(() => table.rows().sorting());
|
||||
const by = String(id);
|
||||
|
||||
const onPointerDown = (e: PointerEvent) => {
|
||||
|
@ -173,27 +173,27 @@ function Head(props: {}) {
|
|||
return;
|
||||
}
|
||||
|
||||
// table.setSort(current => {
|
||||
// if (current?.by !== by) {
|
||||
// return { by, reversed: false };
|
||||
// }
|
||||
table.rows().sort(current => {
|
||||
if (current?.by !== by) {
|
||||
return { by, reversed: false };
|
||||
}
|
||||
|
||||
// if (current.reversed === true) {
|
||||
// return undefined;
|
||||
// }
|
||||
if (current.reversed === true) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// return { by, reversed: true };
|
||||
// });
|
||||
return { by, reversed: true };
|
||||
});
|
||||
};
|
||||
|
||||
return <th scope="col" class={`${css.cell} ${sort()?.by === by ? css.sorted : ''}`} onpointerdown={onPointerDown}>
|
||||
{label}
|
||||
|
||||
{/* <Switch>
|
||||
<Switch>
|
||||
<Match when={sortable && sort()?.by !== by}><FaSolidSort /></Match>
|
||||
<Match when={sortable && sort()?.by === by && sort()?.reversed !== true}><FaSolidSortUp /></Match>
|
||||
<Match when={sortable && sort()?.by === by && sort()?.reversed === true}><FaSolidSortDown /></Match>
|
||||
</Switch> */}
|
||||
</Switch>
|
||||
</th>;
|
||||
}
|
||||
}</For>
|
||||
|
@ -239,13 +239,29 @@ function Row<T extends Record<string, any>>(props: { key: keyof T, value: T, dep
|
|||
};
|
||||
|
||||
function Group<T extends Record<string, any>>(props: { key: keyof T, groupedBy: keyof T, nodes: DataSetNode<keyof T, T>[], depth: number }) {
|
||||
return <details open>
|
||||
<summary style={{ '--depth': props.depth }}>{String(props.key)}</summary>
|
||||
const table = useTable();
|
||||
|
||||
<For each={props.nodes}>{
|
||||
node => <Node node={node} depth={props.depth + 1} groupedBy={props.groupedBy} />
|
||||
}</For>
|
||||
</details>;
|
||||
return <tr class={css.group}>
|
||||
<td colSpan={table.columns().length}>
|
||||
<table class={css.table}>
|
||||
<thead class={css.header}>
|
||||
<tr><th class={css.cell} colSpan={table.columns().length} style={{ '--depth': props.depth }}>
|
||||
<label>
|
||||
<input type="checkbox" checked name="collapse" />
|
||||
<FaSolidAngleDown />
|
||||
|
||||
{String(props.key)}</label>
|
||||
</th></tr>
|
||||
</thead>
|
||||
|
||||
<tbody class={css.main}>
|
||||
<For each={props.nodes}>{
|
||||
node => <Node node={node} depth={props.depth + 1} groupedBy={props.groupedBy} />
|
||||
}</For>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>;
|
||||
};
|
||||
|
||||
declare module "solid-js" {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue