brf/client/admin/components/table.tsx
2025-12-18 07:31:37 +01:00

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>
)
}