import { html, nothing, type TemplateResult } from 'lit'; import type { NuiType } from '../../types/nui-type.js'; import { filterRows, getFilterOptionsForColumn, isColumnFilterable, } from './data-table-filters.js'; import { getCellValue, getCurrentPage, getPageCount, paginateRows, resolveColumns, sortRows, } from './data-table-state.js'; import type { DataTableColumn, DataTableFilters, DataTableRow, DataTableSize, DataTableSortOrder, } from './types.js'; export interface NuiDataTableViewState { value: DataTableRow[]; columns: DataTableColumn[]; dataKey: string; rows: number; first: number; paginator: boolean; filter: boolean; filters: DataTableFilters; showHeaders: boolean; stripedRows: boolean; rowHover: boolean; loading: boolean; sortField: string; sortOrder: DataTableSortOrder; emptyMessage: string; size: DataTableSize; nuiType: NuiType; dataTableClass: string; } export interface DataTableRenderHandlers { onSort: (field: string) => void; onRowClick: (row: DataTableRow, index: number) => void; onPageChange: (first: number) => void; onFilterChange: (field: string, value: string) => void; onFilterSelectOpen: (event: Event) => void; } export function getDataTableClass(dataTableClass: string): string { return ['nui-data-table', dataTableClass].filter(Boolean).join(' '); } function renderSortIcon( field: string, sortField: string, sortOrder: DataTableSortOrder, ): TemplateResult | typeof nothing { if (field !== sortField || sortOrder === 0) { return nothing; } return html` `; } function renderFilterControl( state: NuiDataTableViewState, column: DataTableColumn, handlers: DataTableRenderHandlers, ): TemplateResult | typeof nothing { if (!isColumnFilterable(column, state.filter)) { return nothing; } const value = state.filters[column.field] ?? ''; if (column.filterType === 'select') { const options = getFilterOptionsForColumn(state.value, column); return html` ({ label: option, value: option })), ]} placeholder="All" fluid @click=${(event: Event) => event.stopPropagation()} @nui-select-open=${handlers.onFilterSelectOpen} @nui-change=${(event: Event) => { handlers.onFilterChange( column.field, (event as CustomEvent<{ value: string }>).detail.value, ); }} > `; } return html` event.stopPropagation()} @input=${(event: Event) => { handlers.onFilterChange( column.field, (event as CustomEvent<{ value: string }>).detail.value, ); }} > `; } function renderFilterRow( state: NuiDataTableViewState, columns: DataTableColumn[], handlers: DataTableRenderHandlers, ): TemplateResult | typeof nothing { if (!state.filter) { return nothing; } const hasFilterableColumn = columns.some((column) => isColumnFilterable(column, state.filter), ); if (!hasFilterableColumn) { return nothing; } return html` ${columns.map( (column) => html` ${renderFilterControl(state, column, handlers)} `, )} `; } function renderHeader( state: NuiDataTableViewState, columns: DataTableColumn[], handlers: DataTableRenderHandlers, ): TemplateResult { return html` ${columns.map( (column) => html` { if (column.sortable !== false) { handlers.onSort(column.field); } }} > ${column.header || column.field} ${renderSortIcon(column.field, state.sortField, state.sortOrder)} `, )} ${renderFilterRow(state, columns, handlers)} `; } function renderBody( state: NuiDataTableViewState, columns: DataTableColumn[], rows: DataTableRow[], handlers: DataTableRenderHandlers, ): TemplateResult { if (rows.length === 0) { return html` ${state.emptyMessage} `; } return html` ${rows.map((row, index) => { const key = getCellValue(row, state.dataKey) || String(index); return html` handlers.onRowClick(row, index)} > ${columns.map( (column) => html` ${getCellValue(row, column.field)} `, )} `; })} `; } function renderPaginator( state: NuiDataTableViewState, totalRecords: number, handlers: DataTableRenderHandlers, ): TemplateResult { const pageCount = getPageCount(totalRecords, state.rows); const currentPage = getCurrentPage(state.first, state.rows); const atStart = state.first <= 0; const atEnd = state.first + state.rows >= totalRecords; return html`
${currentPage} / ${pageCount}
`; } export function renderDataTable( state: NuiDataTableViewState, handlers: DataTableRenderHandlers, ): TemplateResult { if (state.loading) { return html`
Loading...
`; } if (!state.value || state.value.length === 0) { return html`
${state.emptyMessage}
`; } const columns = resolveColumns(state.value, state.columns); const filteredRows = filterRows( state.value, state.filters, columns, state.filter, ); const sortedRows = sortRows(filteredRows, state.sortField, state.sortOrder); const visibleRows = state.paginator ? paginateRows(sortedRows, state.first, state.rows) : sortedRows; return html`
${state.showHeaders ? renderHeader(state, columns, handlers) : nothing} ${renderBody(state, columns, visibleRows, handlers)}
${ state.paginator ? renderPaginator(state, sortedRows.length, handlers) : nothing }
`; }