|
| 1 | +import type { JrComponent } from './_shared' |
| 2 | +import { useBoundProp } from '@json-render/vue' |
| 3 | +import { h } from 'vue' |
| 4 | +import { formatValue } from './_shared' |
| 5 | + |
| 6 | +interface DataTableColumn { key: string, label?: string } |
| 7 | +interface DataTableProps { |
| 8 | + columns?: (string | DataTableColumn)[] |
| 9 | + rows?: Record<string, unknown>[] |
| 10 | + height?: number |
| 11 | + loading?: boolean |
| 12 | +} |
| 13 | + |
| 14 | +function normalizeColumns(columns: DataTableProps['columns'], rows: DataTableProps['rows']): DataTableColumn[] { |
| 15 | + if (columns?.length) |
| 16 | + return columns.map(c => (typeof c === 'string' ? { key: c } : c)) |
| 17 | + const first = rows?.[0] |
| 18 | + return first ? Object.keys(first).map(key => ({ key })) : [] |
| 19 | +} |
| 20 | + |
| 21 | +// `@antfu/design` LayoutDataTable has no per-row click or loading state, both of |
| 22 | +// which the catalog requires (`rowClick`, `loading`), so this stays custom on |
| 23 | +// the shared tokens. (A candidate to contribute back upstream.) |
| 24 | +export const DataTable: JrComponent<DataTableProps> = ({ props, on, bindings, loading }) => { |
| 25 | + if (loading || props.loading) |
| 26 | + return h('div', { class: 'color-faint text-sm' }, 'Loading…') |
| 27 | + const rows = props.rows ?? [] |
| 28 | + const columns = normalizeColumns(props.columns, rows) |
| 29 | + const [, setSelected] = useBoundProp<unknown>(undefined, bindings?.value) |
| 30 | + const wrapperStyle = props.height != null ? { maxHeight: `${props.height}px` } : undefined |
| 31 | + |
| 32 | + return h('div', { class: 'rounded border border-base overflow-auto', style: wrapperStyle }, [ |
| 33 | + h('table', { class: 'w-full text-sm border-collapse' }, [ |
| 34 | + h('thead', { class: 'sticky top-0 bg-secondary' }, [ |
| 35 | + h('tr', columns.map(col => |
| 36 | + h('th', { class: 'text-left px2 py1.5 color-muted font-medium border-b border-base' }, col.label ?? col.key))), |
| 37 | + ]), |
| 38 | + h('tbody', rows.map((row, index) => |
| 39 | + h('tr', { |
| 40 | + class: 'border-b border-base hover:bg-secondary cursor-pointer', |
| 41 | + onClick: () => { |
| 42 | + // Deliver row + index into bound state, then fire the action. |
| 43 | + setSelected({ row, index }) |
| 44 | + on('rowClick').emit() |
| 45 | + }, |
| 46 | + }, columns.map(col => |
| 47 | + h('td', { class: 'px2 py1 color-base font-mono' }, formatValue(row[col.key])))))), |
| 48 | + ]), |
| 49 | + ]) |
| 50 | +} |
0 commit comments