import { Accessor, createContext, createEffect, createMemo, createSignal, For, JSX, Match, Show, Switch, useContext } from "solid-js"; import { selectable, SelectionProvider, useSelection } from "~/features/selectable"; import { type RowNode, type GroupNode, type Node, createDataSet, toSorted, toGrouped } from './dataset'; import css from './table.module.css'; selectable export type Column = { id: keyof T, label: string, readonly groupBy?: (rows: RowNode[]) => Node[], }; const TableContext = createContext<{ readonly columns: Accessor[]>, readonly selectionMode: Accessor, readonly groupBy: Accessor, readonly sort: Accessor<{ by: string, reversed?: boolean } | undefined>, readonly cellRenderers: Accessor JSX.Element>>, }>(); const useTable = () => useContext(TableContext)! function defaultGroupingFunction(groupBy: keyof T) { return (nodes: RowNode[]): Node[] => Object.entries(Object.groupBy>(nodes, r => r.value[groupBy])) .map>(([key, nodes]) => ({ kind: 'group', key, groupedBy: groupBy, nodes: nodes! })); } export enum SelectionMode { None, Single, Multiple } type TableProps> = { class?: string, rows: T[], columns: Column[], groupBy?: keyof T, sort?: { by: keyof T, reversed?: boolean, }, selectionMode?: SelectionMode, children?: { [K in keyof T]?: (cell: { value: T[K] }) => JSX.Element }, }; export function Table>(props: TableProps) { const [selection, setSelection] = createSignal([]); const columns = createMemo[]>(() => props.columns ?? []); const selectionMode = createMemo(() => props.selectionMode ?? SelectionMode.None); const groupBy = createMemo(() => props.groupBy as string | undefined); const sort = createMemo(() => props.sort as any); const cellRenderers = createMemo(() => props.children ?? {}); return ; }; type InnerTableProps> = { class?: string, rows: T[] }; function InnerTable>(props: InnerTableProps) { const table = useTable(); const selectable = createMemo(() => table.selectionMode() !== SelectionMode.None); const columnCount = createMemo(() => table.columns().length + (selectable() ? 0 : -1)); const nodes = createMemo[]>(() => { const columns = table.columns(); const groupBy = table.groupBy(); const sort = table.sort(); let dataset = createDataSet(props.rows); if (sort) { dataset = toSorted(dataset, { by: sort.by, reversed: sort.reversed ?? false, with: (a, b) => a < b ? -1 : a > b ? 1 : 0 }) } if (groupBy) { dataset = toGrouped(dataset, { by: groupBy, with: columns.find(({ id }) => id === groupBy)?.groupBy ?? defaultGroupingFunction(groupBy) }); } return dataset; }); return
{ node => }
}; function Head>(props: {}) { const table = useTable(); const context = useSelection(); return
{ column => {column.label} }
; }; function Node>(props: { node: Node, depth: number, groupedBy?: keyof T }) { return { row => } { group => } ; } function Row>(props: { key: string, value: T, depth: number, groupedBy?: keyof T }) { const table = useTable(); const context = useSelection(); const values = createMemo(() => Object.entries(props.value)); const isSelected = context.isSelected(props.key); return
{ ([k, v]) =>
{table.cellRenderers()[k]?.({ value: v }) ?? v}
}
; }; function Group>(props: { key: string, groupedBy: keyof T, nodes: Node[], depth: number }) { const table = useTable(); return
{props.key} { node => }
; };