/** * Web Component wrapper for DataTable (hand-crafted WC adapter). * Custom element: * * @generated by scripts/generate-wc-components.ts * DO NOT EDIT MANUALLY */ import React from 'react'; import { Avatar } from '../components'; import { Badge } from '../components'; import type { ColumnDef, DataTableProps, ResponsiveColumnMeta, } from '../components'; import { createSelectColumn, DataTable } from '../components'; import { BaseCompoundWC, camelToKebab, dispatchEvent, installPropAccessors, type PropDefs, registerWC, } from './_shared'; interface SimpleColumn { accessorKey: string; header: string; cellType?: 'text' | 'badge' | 'currency' | 'date' | 'avatar' | 'boolean'; enableSorting?: boolean; width?: string; minWidth?: number; maxWidth?: number; align?: 'left' | 'center' | 'right'; headerTooltip?: string; responsive?: ResponsiveColumnMeta; badgeColors?: Record; currencyCode?: string; dateFormat?: string; } type ColumnAlign = NonNullable; type ColumnCellType = NonNullable; type DataTableWcProps = DataTableProps, unknown>; type DataTableDensity = NonNullable; type DataTableDividers = NonNullable; type DataTableRounded = NonNullable; type BadgeColor = | 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'destructive'; type DateStyle = 'full' | 'long' | 'medium' | 'short'; type PaginationSize = NonNullable; type PaginationShape = NonNullable; type PaginationType = NonNullable; type ResponsiveColumnHideBelow = NonNullable; type ResponsiveColumnPriority = NonNullable; const COLUMN_ALIGNS = { center: true, left: true, right: true, } as const satisfies Record; const COLUMN_CELL_TYPES = { avatar: true, badge: true, boolean: true, currency: true, date: true, text: true, } as const satisfies Record; const DATA_TABLE_DENSITIES = { compact: true, default: true, } as const satisfies Record; const DATA_TABLE_DIVIDERS = { alternating: true, line: true, } as const satisfies Record; const DATA_TABLE_ROUNDED = { all: true, bottom: true, none: true, top: true, } as const satisfies Record; const PAGINATION_SIZES = { lg: true, md: true, sm: true, xs: true, } as const satisfies Record; const PAGINATION_SHAPES = { circle: true, rounded: true, square: true, } as const satisfies Record; const PAGINATION_TYPES = { 'box-style': true, 'card-button-group': true, 'card-default': true, 'card-minimal': true, 'combine-steps': true, 'first-last': true, 'left-right-chevron': true, 'page-default': true, 'page-dropdown': true, 'page-dropdown-nav': true, 'page-minimal': true, 'page-number': true, 'previous-next': true, 'with-buttons': true, 'with-underline': true, combine: true, default: true, minimal: true, outline: true, } as const satisfies Record; const RESPONSIVE_COLUMN_HIDE_BELOW = { lg: true, md: true, sm: true, } as const satisfies Record; const RESPONSIVE_COLUMN_PRIORITIES = { always: true, desktop: true, mobile: true, } as const satisfies Record; function isDataRecord(value: unknown): value is Record { return typeof value === 'object' && value !== null && !Array.isArray(value); } function isStringRecord(value: unknown): value is Record { return ( isDataRecord(value) && Object.values(value).every((entry) => typeof entry === 'string') ); } function isStringOption( value: unknown, options: Record, ): value is Options { return typeof value === 'string' && value in options; } function isColumnAlign(value: unknown): value is ColumnAlign { return isStringOption(value, COLUMN_ALIGNS); } function isColumnCellType(value: unknown): value is ColumnCellType { return isStringOption(value, COLUMN_CELL_TYPES); } function readString(value: unknown): string | undefined { return typeof value === 'string' && value.length > 0 ? value : undefined; } function readNumber(value: unknown): number | undefined { return typeof value === 'number' && Number.isFinite(value) ? value : undefined; } function readPositiveNumber(value: unknown, fallback: number): number { const numberValue = readNumber(value); return numberValue && numberValue > 0 ? numberValue : fallback; } function readBoolean(value: unknown, fallback: boolean): boolean { return typeof value === 'boolean' ? value : fallback; } function readOptionalBoolean(value: unknown): boolean | undefined { return typeof value === 'boolean' ? value : undefined; } function readStringOption( value: unknown, options: Record, fallback: Options, ): Options { return isStringOption(value, options) ? value : fallback; } function readOptionalStringOption( value: unknown, options: Record, ): Options | undefined { return isStringOption(value, options) ? value : undefined; } function getResponsiveColumnMeta( responsive: unknown, ): ResponsiveColumnMeta | undefined { if (!isDataRecord(responsive)) return undefined; const hideBelow = readOptionalStringOption( responsive.hideBelow, RESPONSIVE_COLUMN_HIDE_BELOW, ); const priority = readOptionalStringOption( responsive.priority, RESPONSIVE_COLUMN_PRIORITIES, ); if (!hideBelow && !priority) return undefined; return { ...(hideBelow ? { hideBelow } : {}), ...(priority ? { priority } : {}), }; } function parseSimpleColumn(value: unknown): SimpleColumn | undefined { if (!isDataRecord(value)) return undefined; const accessorKey = readString(value.accessorKey); const header = readString(value.header); if (!accessorKey || !header) return undefined; return { accessorKey, header, align: isColumnAlign(value.align) ? value.align : undefined, badgeColors: isStringRecord(value.badgeColors) ? value.badgeColors : undefined, cellType: isColumnCellType(value.cellType) ? value.cellType : undefined, currencyCode: readString(value.currencyCode), dateFormat: readString(value.dateFormat), enableSorting: typeof value.enableSorting === 'boolean' ? value.enableSorting : undefined, headerTooltip: readString(value.headerTooltip), maxWidth: readNumber(value.maxWidth), minWidth: readNumber(value.minWidth), responsive: getResponsiveColumnMeta(value.responsive), width: readString(value.width), }; } function parseSimpleColumns(value: unknown): SimpleColumn[] { if (!Array.isArray(value)) return []; return value.flatMap((entry) => { const column = parseSimpleColumn(entry); return column ? [column] : []; }); } function parseDataRows(value: unknown): Record[] { if (!Array.isArray(value)) return []; return value.filter(isDataRecord); } function getWcRowId( row: Record, rowIdKey: string, index: number, ): string { const value = row[rowIdKey]; if (typeof value === 'string' || typeof value === 'number') { return String(value); } return `wc-row-${index}`; } function renderEmptyCell(): React.ReactElement { return React.createElement( 'span', { className: 'text-muted-foreground' }, '—', ); } function CurrencyCell({ value, currencyCode = 'USD', }: { value: number; currencyCode?: string; }): React.ReactElement { const formatter = React.useMemo( () => new Intl.NumberFormat('en-US', { style: 'currency', currency: currencyCode, }), [currencyCode], ); return React.createElement('span', null, formatter.format(value)); } function getBadgeColor(color: string | undefined): BadgeColor { switch (color) { case 'primary': case 'secondary': case 'info': case 'success': case 'warning': case 'destructive': return color; default: return 'secondary'; } } function getDateStyle(dateFormat: string | undefined): DateStyle { switch (dateFormat) { case 'full': case 'long': case 'short': return dateFormat; default: return 'medium'; } } function renderCell(column: SimpleColumn, value: unknown): React.ReactElement { if (value == null) { return renderEmptyCell(); } switch (column.cellType) { case 'badge': { const text = String(value); const color = getBadgeColor(column.badgeColors?.[text]); return React.createElement( Badge, { variant: 'soft', color, size: 'sm' }, text, ); } case 'currency': { const numberValue = typeof value === 'number' ? value : Number(value); if (!Number.isFinite(numberValue)) return renderEmptyCell(); return React.createElement(CurrencyCell, { value: numberValue, currencyCode: column.currencyCode, }); } case 'date': { const date = value instanceof Date ? value : new Date(String(value)); if (Number.isNaN(date.getTime())) return renderEmptyCell(); return React.createElement( 'span', null, date.toLocaleDateString('en-US', { dateStyle: getDateStyle(column.dateFormat), }), ); } case 'avatar': { const src = typeof value === 'string' ? value : undefined; return React.createElement(Avatar, { src, size: 'sm' }); } case 'boolean': { const enabled = Boolean(value); return React.createElement( 'span', { className: enabled ? 'text-success' : 'text-muted-foreground' }, enabled ? 'Yes' : 'No', ); } default: return React.createElement('span', null, String(value)); } } function buildColumns( simpleColumns: SimpleColumn[], enableRowSelection: boolean, ): ColumnDef, unknown>[] { const columns: ColumnDef, unknown>[] = []; if (enableRowSelection) { columns.push(createSelectColumn>()); } for (const column of simpleColumns) { const columnDef: ColumnDef, unknown> = { accessorKey: column.accessorKey, header: column.header, enableSorting: column.enableSorting ?? true, size: column.width ? Number.parseInt(column.width, 10) || undefined : undefined, minSize: column.minWidth, maxSize: column.maxWidth, meta: { cellClassName: column.align ? `text-${column.align}` : undefined, headerClassName: column.align ? `text-${column.align}` : undefined, headerTooltip: column.headerTooltip, responsive: getResponsiveColumnMeta(column.responsive), }, }; if (column.cellType && column.cellType !== 'text') { columnDef.cell = ({ getValue }) => renderCell(column, getValue()); } columns.push(columnDef); } return columns; } const PROP_DEFS: PropDefs = { columns: 'json', data: 'json', enableRowSelection: 'boolean', enableMultiRowSelection: 'boolean', enableSorting: 'boolean', enablePagination: 'boolean', pageSize: 'number', density: 'string', dividers: 'string', rounded: 'string', bordered: 'boolean', maxHeight: 'string', stickyHeader: 'boolean', caption: 'string', showSelectAll: 'boolean', paginationType: 'string', paginationShape: 'string', paginationSize: 'string', paginationShowPageNumbers: 'boolean', responsiveColumns: 'boolean', showSelectionCount: 'boolean', rowIdKey: 'string', }; class DataTableWC extends BaseCompoundWC { get propDefs() { return PROP_DEFS; } static get observedAttributes() { return Object.keys(PROP_DEFS).map(camelToKebab); } protected _renderReact(): React.ReactElement { const props = this._readAllProps(); const simpleColumns = parseSimpleColumns(props.columns); const data = parseDataRows(props.data); const enableRowSelection = readBoolean(props.enableRowSelection, false); const pageSize = readPositiveNumber(props.pageSize, 10); const rowIdKey = readString(props.rowIdKey) ?? 'id'; const columns = buildColumns(simpleColumns, enableRowSelection); return React.createElement(DataTable, { columns, data, getRowId: (row: Record, index: number) => getWcRowId(row, rowIdKey, index), enableRowSelection, enableMultiRowSelection: readBoolean(props.enableMultiRowSelection, true), enableSorting: readBoolean(props.enableSorting, true), enablePagination: readBoolean(props.enablePagination, false), pageSize, dividers: readStringOption(props.dividers, DATA_TABLE_DIVIDERS, 'line'), density: readStringOption(props.density, DATA_TABLE_DENSITIES, 'default'), rounded: readStringOption(props.rounded, DATA_TABLE_ROUNDED, 'all'), bordered: readBoolean(props.bordered, true), caption: readString(props.caption), showSelectAll: readBoolean(props.showSelectAll, true), paginationType: readOptionalStringOption( props.paginationType, PAGINATION_TYPES, ), paginationShape: readOptionalStringOption( props.paginationShape, PAGINATION_SHAPES, ), paginationSize: readOptionalStringOption( props.paginationSize, PAGINATION_SIZES, ), paginationShowPageNumbers: readBoolean( props.paginationShowPageNumbers, true, ), responsiveColumns: readBoolean(props.responsiveColumns, false), showSelectionCount: readBoolean(props.showSelectionCount, false), maxHeight: readString(props.maxHeight), stickyHeader: readOptionalBoolean(props.stickyHeader), onRowClick: (row) => dispatchEvent(this, 'row-click', row.original), onRowSelectionChange: (updater) => { const nextState = typeof updater === 'function' ? updater({}) : updater; dispatchEvent(this, 'selection-change', nextState); }, onSortingChange: (updater) => { const nextState = typeof updater === 'function' ? updater([]) : updater; dispatchEvent(this, 'sorting-change', nextState); }, onPaginationChange: (updater) => { const defaultPagination = { pageIndex: 0, pageSize, }; const nextState = typeof updater === 'function' ? updater(defaultPagination) : updater; dispatchEvent(this, 'page-change', nextState); }, }); } } installPropAccessors(DataTableWC, PROP_DEFS); registerWC('hf-data-table', DataTableWC); export { DataTableWC }; export default DataTableWC;