From 9c5bbdaa28e9fd4180b1e0c9ab964cfa6ceec1f8 Mon Sep 17 00:00:00 2001 From: Marco Beretta <81851188+berry-13@users.noreply.github.com> Date: Mon, 29 Sep 2025 21:19:22 +0200 Subject: [PATCH] refactor(DataTable): improve column sizing and visibility handling; remove deprecated features --- client/src/components/Conversations/Convo.tsx | 2 +- .../SettingsTabs/General/ArchivedChats.tsx | 35 +--- .../src/components/DataTable/DataTable.tsx | 196 ++++++++++-------- .../components/DataTable/DataTable.types.ts | 36 +++- .../DataTable/DataTableComponents.tsx | 42 ++-- .../components/DataTable/DataTableSearch.tsx | 2 +- .../client/src/components/DataTable/index.ts | 3 + packages/client/src/components/index.ts | 2 +- packages/client/src/index.ts | 2 - .../client/src/locales/en/translation.json | 6 +- 10 files changed, 179 insertions(+), 147 deletions(-) create mode 100644 packages/client/src/components/DataTable/index.ts diff --git a/client/src/components/Conversations/Convo.tsx b/client/src/components/Conversations/Convo.tsx index 048c2f129d..2dd936a718 100644 --- a/client/src/components/Conversations/Convo.tsx +++ b/client/src/components/Conversations/Convo.tsx @@ -132,7 +132,7 @@ export default function Conversation({ conversation, retainView, toggleNav }: Co return (
{ - console.error('DataTable error:', error); - showToast({ - message: localize('com_ui_unarchive_error'), - severity: NotificationSeverity.ERROR, - }); - }, - [showToast, localize], - ); - const flattenedConversations = useMemo( () => data?.pages?.flatMap((page) => page?.conversations?.filter(Boolean) ?? []) ?? [], [data?.pages], @@ -259,7 +248,8 @@ export default function ArchivedChatsTable() { ); }, meta: { - className: 'min-w-[150px] flex-1', + width: 65, + className: 'min-w-[150px]', }, enableSorting: true, }, @@ -279,8 +269,9 @@ export default function ArchivedChatsTable() { return formatDate(convo.createdAt?.toString() ?? '', isSmallScreen); }, meta: { - className: 'w-32 sm:w-40', - // desktopOnly: true, // Potential future use + width: 20, + className: 'min-w-[6rem]', + desktopOnly: true, }, enableSorting: true, }, @@ -298,13 +289,13 @@ export default function ArchivedChatsTable() { const isRowUnarchiving = unarchivingId === convo.conversationId; return ( -
+
{ const conversationId = convo.conversationId; if (!conversationId) return; @@ -322,7 +313,7 @@ export default function ArchivedChatsTable() { render={ -
- - ); - } - return (
, TValue>({ role="region" aria-label={localize('com_ui_data_table')} > -
+
{shouldShowSearch && } {customActionsRenderer && customActionsRenderer({ @@ -424,15 +476,22 @@ function DataTable, TValue>({ aria-label={localize('com_ui_data_table_scroll_area')} aria-describedby={showSkeletons ? 'loading-status' : undefined} > - +
{headerGroups.map((headerGroup) => ( {headerGroup.headers.map((header) => { + // Check if this column should be hidden on mobile (desktopOnly feature) const isDesktopOnly = (header.column.columnDef.meta as { desktopOnly?: boolean } | undefined) ?.desktopOnly ?? false; + // Hide header if column is not visible or if it's desktop-only on a mobile viewport if (!header.column.getIsVisible() || (isSmallScreen && isDesktopOnly)) { return null; } @@ -465,15 +524,24 @@ function DataTable, TValue>({ } }; + const metaWidth = (header.column.columnDef.meta as { width?: number } | undefined) + ?.width; + const widthStyle = isSelectHeader + ? { width: '32px', maxWidth: '32px' } + : metaWidth && metaWidth >= 1 && metaWidth <= 100 + ? { width: `${metaWidth}%`, maxWidth: `${metaWidth}%` } + : {}; return ( , TValue>({ {isSelectHeader ? ( flexRender(header.column.columnDef.header, header.getContext()) ) : ( -
+
{flexRender(header.column.columnDef.header, header.getContext())} {canSort && (
)} + {/* Resizer removed (manual resizing deprecated) */} ); })} @@ -512,54 +581,7 @@ function DataTable, TValue>({ - {showSkeletons ? ( - >[]} - /> - ) : virtualizationActive ? ( - <> - {paddingTop > 0 && ( - - )} - {virtualRows.map((virtualRow) => { - const row = rows[virtualRow.index]; - if (!row) return null; - return ( - } - virtualIndex={virtualRow.index} - selected={row.getIsSelected()} - style={{ height: rowHeight }} - /> - ); - })} - {paddingBottom > 0 && ( - - )} - - ) : ( - rows.map((row) => ( - } - virtualIndex={row.index} - selected={row.getIsSelected()} - style={{ height: rowHeight }} - /> - )) - )} + {tableBodyContent} {isFetchingNextPage && ( = ColumnDef, T export type TableColumn = ColumnDef & { accessorKey?: string | number; meta?: { - size?: string | number; - mobileSize?: string | number; - minWidth?: string | number; - priority?: number; + /** Column width as a percentage (1-100). Used for proportional column sizing. */ + width?: number; + /** Additional CSS classes to apply to the column cells and header. */ className?: string; + /** + * When true, this column will be hidden on mobile devices (viewport < 768px). + * This is useful for hiding less critical information on smaller screens. + * + * **Usage Example:** + * ```typescript + * { + * accessorKey: 'createdAt', + * header: 'Date Created', + * cell: ({ row }) => formatDate(row.original.createdAt), + * meta: { + * desktopOnly: true, // Hide this column on mobile + * width: 20, + * className: 'min-w-[6rem]' + * } + * } + * ``` + * + * The column will be completely hidden including: + * - Header cell + * - Data cells + * - Skeleton loading cells + */ desktopOnly?: boolean; }; }; @@ -33,8 +55,8 @@ export interface DataTableConfig { virtualization?: { overscan?: number; minRows?: number; - rowHeight?: number; // fixed row height to disable costly dynamic measurements - fastOverscanMultiplier?: number; // multiplier applied during fast scroll bursts + rowHeight?: number; + fastOverscanMultiplier?: number; }; pinning?: { enableColumnPinning?: boolean; @@ -55,8 +77,6 @@ export interface DataTableProps, TValue> { isFetchingNextPage?: boolean; hasNextPage?: boolean; fetchNextPage?: () => Promise; - onError?: (error: Error) => void; - onReset?: () => void; sorting?: SortingState; onSortingChange?: (updater: SortingState | ((old: SortingState) => SortingState)) => void; conversationIndex?: number; diff --git a/packages/client/src/components/DataTable/DataTableComponents.tsx b/packages/client/src/components/DataTable/DataTableComponents.tsx index 9bdeaf7bfd..2ae9433d5a 100644 --- a/packages/client/src/components/DataTable/DataTableComponents.tsx +++ b/packages/client/src/components/DataTable/DataTableComponents.tsx @@ -27,7 +27,7 @@ export const SelectionCheckbox = memo( } e.stopPropagation(); }} - className="flex h-full w-[30px] items-center justify-center" + className="flex h-full w-8 items-center justify-center" onClick={(e) => { e.stopPropagation(); onChange(!checked); @@ -40,18 +40,15 @@ export const SelectionCheckbox = memo( SelectionCheckbox.displayName = 'SelectionCheckbox'; +interface TableRowComponentProps> { + row: Row; + virtualIndex?: number; + style?: React.CSSProperties; + selected: boolean; +} + const TableRowComponent = >( - { - row, - virtualIndex, - style, - selected, - }: { - row: Row; - virtualIndex?: number; - style?: React.CSSProperties; - selected: boolean; - }, + { row, virtualIndex, style, selected }: TableRowComponentProps, ref: React.Ref, ) => ( >( > {row.getVisibleCells().map((cell) => { const meta = cell.column.columnDef.meta as - | { className?: string; desktopOnly?: boolean } + | { className?: string; desktopOnly?: boolean; width?: number } | undefined; const isDesktopOnly = meta?.desktopOnly; + const percent = meta?.width; + const widthStyle = + cell.column.id === 'select' + ? { width: '32px', maxWidth: '32px' } + : percent && percent >= 1 && percent <= 100 + ? { width: `${percent}%`, maxWidth: `${percent}%` } + : undefined; + return ( {flexRender(cell.column.columnDef.cell, cell.getContext())} @@ -84,11 +90,7 @@ const TableRowComponent = >( ); type ForwardTableRowComponentType = >( - props: { - row: Row; - virtualIndex?: number; - style?: React.CSSProperties; - } & React.RefAttributes, + props: TableRowComponentProps & React.RefAttributes, ) => JSX.Element; const ForwardTableRowComponent = forwardRef(TableRowComponent) as ForwardTableRowComponentType; diff --git a/packages/client/src/components/DataTable/DataTableSearch.tsx b/packages/client/src/components/DataTable/DataTableSearch.tsx index 277a257d13..30ced61cbe 100644 --- a/packages/client/src/components/DataTable/DataTableSearch.tsx +++ b/packages/client/src/components/DataTable/DataTableSearch.tsx @@ -24,7 +24,7 @@ export const DataTableSearch = memo( aria-label={localize('com_ui_search_table')} aria-describedby="search-description" placeholder={placeholder || localize('com_ui_search')} - className={cn('h-12 rounded-b-none border-0 bg-surface-secondary', className)} + className={cn('h-10 rounded-b-none border-0 bg-surface-secondary md:h-12', className)} /> {localize('com_ui_search_table_description')} diff --git a/packages/client/src/components/DataTable/index.ts b/packages/client/src/components/DataTable/index.ts new file mode 100644 index 0000000000..03e6fb62a1 --- /dev/null +++ b/packages/client/src/components/DataTable/index.ts @@ -0,0 +1,3 @@ +export { default as DataTable } from './DataTable'; +export * from './DataTable.types'; +// Removed legacy DataTableSettings exports (store/context) as column resizing & dynamic sizing were deprecated. diff --git a/packages/client/src/components/index.ts b/packages/client/src/components/index.ts index d091ba69f2..5f37ae25fc 100644 --- a/packages/client/src/components/index.ts +++ b/packages/client/src/components/index.ts @@ -4,7 +4,6 @@ export * from './AlertDialog'; export * from './Breadcrumb'; export * from './Button'; export * from './Checkbox'; - export * from './Dialog'; export * from './DropdownMenu'; export * from './HoverCard'; @@ -31,6 +30,7 @@ export * from './InputOTP'; export * from './MultiSearch'; export * from './Resizable'; export * from './Select'; +export * from './DataTable/index'; export { default as Radio } from './Radio'; export { default as Badge } from './Badge'; export { default as Avatar } from './Avatar'; diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 4c6c7d9df4..709697c99f 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -1,7 +1,5 @@ // Components export * from './components'; -export { default as DataTable } from './components/DataTable/DataTable'; -export * from './components/DataTable/DataTable.types'; // Hooks export * from './hooks'; diff --git a/packages/client/src/locales/en/translation.json b/packages/client/src/locales/en/translation.json index 9f61e6c0fb..b1caae15f8 100644 --- a/packages/client/src/locales/en/translation.json +++ b/packages/client/src/locales/en/translation.json @@ -16,10 +16,12 @@ "com_ui_search_table_description": "Type to filter results", "com_ui_search": "Search", "com_ui_data_table_scroll_area": "Scrollable data table area", - "com_ui_select_row": "Select Row {{0}}", + "com_ui_select_row": "Select row {{0}}", "com_ui_loading_more_data": "Loading more data...", "com_ui_no_search_results": "No search results found", "com_ui_table_error": "Table Error", "com_ui_table_error_description": "Table failed to load. Please refresh or try again.", - "com_ui_error_details": "Error Details (Dev)" + "com_ui_error_details": "Error Details (Dev)", + "com_ui_enabled": "Enabled", + "com_ui_disabled": "Disabled" }