94 lines
2.3 KiB
TypeScript
94 lines
2.3 KiB
TypeScript
import { h, createContext, type FunctionComponent } from 'preact'
|
|
import { useCallback, useContext } from 'preact/hooks'
|
|
import cn from 'classnames'
|
|
import { camelCase } from 'lowline'
|
|
|
|
import s from './table.module.scss'
|
|
|
|
export type onSortByFunction = (column: string | null) => void | Promise<void>
|
|
|
|
const TableContext = createContext<{ sortBy: (e: Event) => void; sortedBy: [string, boolean?] } | null>(null)
|
|
|
|
type TableProps = {
|
|
className?: string
|
|
defaultSort?: string
|
|
onSortBy?: onSortByFunction
|
|
sortedBy?: string
|
|
}
|
|
|
|
export const Table: FunctionComponent<TableProps> = ({
|
|
children,
|
|
className,
|
|
defaultSort = 'id',
|
|
onSortBy,
|
|
sortedBy,
|
|
}) => {
|
|
const sortBy = useCallback(
|
|
(e: Event) => {
|
|
e.preventDefault()
|
|
|
|
// @ts-ignore
|
|
const { sortBy } = e.currentTarget.dataset
|
|
|
|
const newSortBy = sortBy === sortedBy ? '-' + sortBy : sortBy
|
|
|
|
onSortBy?.(newSortBy === defaultSort ? null : newSortBy)
|
|
},
|
|
[onSortBy, sortedBy],
|
|
)
|
|
|
|
sortedBy ??= defaultSort
|
|
|
|
const desc = sortedBy.startsWith('-')
|
|
|
|
return (
|
|
<TableContext.Provider
|
|
value={{
|
|
sortBy,
|
|
sortedBy: [desc ? sortedBy.slice(1) : sortedBy, desc],
|
|
}}
|
|
>
|
|
<table className={cn(s.table, className)}>{children}</table>
|
|
</TableContext.Provider>
|
|
)
|
|
}
|
|
|
|
export const Td: FunctionComponent<{
|
|
className?: string
|
|
buttons?: boolean
|
|
minimize?: boolean
|
|
}> = ({ children, className, buttons, minimize }) => (
|
|
<td className={cn(className, buttons && s.buttons, minimize && s.minimize)}>
|
|
{buttons ? <div>{children}</div> : children}
|
|
</td>
|
|
)
|
|
|
|
export const Th: FunctionComponent<{
|
|
children: string
|
|
className?: string
|
|
scope?: 'col' | 'row' | 'colgroup' | 'rowgroup'
|
|
sort?: string | boolean
|
|
}> = ({ children, className, scope, sort }) => {
|
|
const column = typeof sort === 'string' ? sort : camelCase(children)
|
|
|
|
const { sortBy, sortedBy } = useContext(TableContext)!
|
|
|
|
return (
|
|
<th className={cn(s.th, className)} scope={scope}>
|
|
{sort ? (
|
|
<a
|
|
href='javascript:;'
|
|
onClick={sortBy}
|
|
data-sort-by={column}
|
|
className={cn({ [s.current]: column === sortedBy[0], [s.desc]: column === sortedBy[0] && sortedBy[1] })}
|
|
>
|
|
{children}
|
|
<div className={s.arrows} />
|
|
</a>
|
|
) : (
|
|
children
|
|
)}
|
|
</th>
|
|
)
|
|
}
|