diff --git a/src/components/table/table.module.css b/src/components/table/table.module.css index 68f349d..66c46ba 100644 --- a/src/components/table/table.module.css +++ b/src/components/table/table.module.css @@ -78,6 +78,20 @@ padding: var(--padding-m); z-index: 1; } + + & > .cell { + grid-auto-flow: column; + justify-content: space-between; + + & > svg { + transition: opacity .15s ease-in-out; + } + + &:not(.sorted):not(:hover) > svg { + opacity: 0; + } + } + } & .main { diff --git a/src/components/table/table.tsx b/src/components/table/table.tsx index 4053f8e..ab13e30 100644 --- a/src/components/table/table.tsx +++ b/src/components/table/table.tsx @@ -2,12 +2,15 @@ import { Accessor, createContext, createEffect, createMemo, createSignal, For, J 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'; +import { createStore } from "solid-js/store"; +import { FaSolidSort, FaSolidSortDown, FaSolidSortUp } from "solid-icons/fa"; selectable export type Column = { id: keyof T, label: string, + sortable?: boolean, readonly groupBy?: (rows: RowNode[]) => Node[], }; @@ -17,6 +20,8 @@ const TableContext = createContext<{ readonly groupBy: Accessor, readonly sort: Accessor<{ by: string, reversed?: boolean } | undefined>, readonly cellRenderers: Accessor JSX.Element>>, + + setSort(setter: (current: { by: string, reversed?: boolean } | undefined) => { by: string, reversed: boolean } | undefined): void; }>(); const useTable = () => useContext(TableContext)! @@ -46,13 +51,33 @@ type TableProps> = { export function Table>(props: TableProps) { const [selection, setSelection] = createSignal([]); + + const [state, setState] = createStore({ + sort: props.sort ? { by: props.sort.by as string, reversed: props.sort.reversed } : undefined, + }); + + createEffect(() => { + setState('sort', props.sort ? { by: props.sort.by as string, reversed: props.sort.reversed } : undefined); + }); + 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 + const context = { + columns, + selectionMode, + groupBy, + sort: createMemo(() => state.sort), + cellRenderers, + + setSort(setter: (current: { by: string, reversed?: boolean } | undefined) => { by: string, reversed: boolean } | undefined) { + setState('sort', setter); + }, + }; + + return @@ -113,7 +138,38 @@ function Head>(props: {}) { { - column => {column.label} + ({ id, label, sortable }) => { + const sort = createMemo(() => table.sort()); + const by = String(id); + + const onPointerDown = (e: PointerEvent) => { + if (sortable !== true) { + return; + } + + table.setSort(current => { + if (current?.by !== by) { + return { by, reversed: false }; + } + + if (current.reversed === true) { + return undefined; + } + + return { by, reversed: true }; + }); + }; + + return + {label} + + + + + + + ; + } } ; }; diff --git a/src/routes/(editor)/experimental.tsx b/src/routes/(editor)/experimental.tsx index c454f94..0291086 100644 --- a/src/routes/(editor)/experimental.tsx +++ b/src/routes/(editor)/experimental.tsx @@ -22,26 +22,32 @@ export default function Experimental() { { id: 'name', label: 'Name', + sortable: true, }, { id: 'email', label: 'Email', + sortable: true, }, { id: 'address', label: 'Address', + sortable: true, }, { id: 'currency', label: 'Currency', + sortable: true, }, { id: 'phone', label: 'Phone', + sortable: true, }, { id: 'country', label: 'Country', + sortable: true, }, ]; @@ -106,7 +112,7 @@ export default function Experimental() {
{{ - address: (cell) => , + // email: (cell) => , }}
;