mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-03-21 15:16:33 +01:00
* 🎨 feat: Enhance Import Conversations UI with loading state and new localization key * fix: Correct pluralization in selected items message in translation.json * Refactor Chat Input File Table Headers to Use SortFilterHeader Component - Replaced button-based sorting headers in the Chat Input Files Table with a new SortFilterHeader component for better code organization and consistency. - Updated the header for filename, updatedAt, and bytes columns to utilize the new component. Enhance Navigation Component with Skeleton Loading States - Added Skeleton loading states to the Nav component for better user experience during data fetching. - Updated Suspense fallbacks for AgentMarketplaceButton and BookmarkNav components to display Skeletons. Refactor Avatar Component for Improved UI - Enhanced the Avatar component by adding a Label for drag-and-drop functionality. - Improved styling and structure for the file upload area. Update Shared Links Component for Better Error Handling and Sorting - Improved error handling in the Shared Links component for fetching next pages and deleting shared links. - Simplified the header rendering for sorting columns and added sorting functionality to the title and createdAt columns. Refactor Archived Chats Component - Merged ArchivedChats and ArchivedChatsTable components into a single ArchivedChats component for better maintainability. - Implemented sorting and searching functionality with debouncing for improved performance. - Enhanced the UI with better loading states and error handling. Update DataTable Component for Sorting Icons - Added sorting icons (ChevronUp, ChevronDown, ChevronsUpDown) to the DataTable headers for better visual feedback on sorting state. Localization Updates - Updated translation.json to fix missing translations and improve existing ones for better user experience. * ✨ feat: Update DataTable component to streamline props and enhance sorting icons * fix: TS issues * feat: polish and redefine DataTable + shared links and archived chats * feat: enhance DataTable with column pinning and improve sorting functionality * feat: enhance deepEqual function for array support and improve column style stability * refactor: DataTable and ArchivedChats; fix: sorting ArchivedChats API * feat(DataTable): Implement new DataTable component with hooks and optimized features - Added DataTable component with support for virtual scrolling, row selection, and customizable columns. - Introduced hooks for debouncing search input, managing row selection, and calculating column styles. - Enhanced accessibility with keyboard navigation and selection checkboxes. - Implemented skeleton loading state for better user experience during data fetching. - Added DataTableSearch component for filtering data with debounced input. - Created utility logger for improved debugging in development. - Updated translations to support new UI elements and actions. * refactor: update SharedLinks and ArchivedChats to use desktopOnly instead of hideOnMobile; remove unused DataTableColumnHeader component * fix: ensure desktopOnly columns are hidden on mobile in DataTable * refactor: reorganize imports in DataTable components and update index exports * refactor: improve styling and animations in Artifacts, ArtifactsSubMenu, and MCPSubMenu components; update border-radius in style.css * refactor(Artifacts): enhance button toggle functionality and manage expanded state with useEffect * refactor: comment out desktopOnly property in SharedLinks and ArchivedChats components; update translation.json with new keys for link actions * refactor(DataTable): streamline column visibility logic and enhance type definitions; improve cleanup timers and optimize rendering * refactor(DataTable): enhance type definitions for processed data rows and update custom actions renderer type * refactor(DataTable): optimize processed data handling and improve warning for missing IDs; streamline DataTableComponents imports * refactor(DataTable): enhance accessibility features and improve localization for selection and loading states * refactor: improve padding in dialog content and enhance row selection functionality in ArchivedChats and DataTable components * refactor(DataTable): remove unnecessary role and tabindex attributes from select all button for improved accessibility * refactor(translation): remove outdated error messages and unused UI strings for cleaner localization * refactor(DataTable): enhance virtualization and scrolling performance with dynamic overscan adjustments * refactor(DataTableErrorBoundary): enhance error handling and localization support * refactor(DataTable): improve column sizing and visibility handling; remove deprecated features * refactor: enhance UI components with improved class handling and state management * refactor(DataTable): improve column width handling and responsiveness; disable row selection * refactor(DataTable): enhance accessibility with row header support and improve column visibility handling * chore(DataTable): comments update * refactor(Table): add unwrapped prop for direct table rendering; adjust minWidth calculation for responsiveness * refactor(DataTable): simplify search handling by removing unnecessary trimming; adjust column width handling for better responsiveness * refactor(translation): remove redundant drag and drop UI text for clarity * refactor(parsers): change uiResources to a constant and streamline artifacts handling * chore: remove unused file, bump @librechat/client to 0.3.2; fix(SharedLinks): missing import; * refactor: change button variant from destructive to ghost for delete actions in SharedLinks and ArchivedChats components * refactor(DataTable): simplify aria-sort assignment for better readability * refactor(DataTable): update aria-label and ariaLabel to use indexed placeholder for localization * refactor(translation): update no data messages for consistency * Refactor code structure for improved readability and maintainability * chore: restore linting fixes * chore: restore linting fixes 2; refactor: remove unused translation keys * feat(tests): add unit tests for DataTable components and error handling - Implement tests for SelectionCheckbox and SkeletonRows components in DataTable. - Add tests for DataTableErrorBoundary to ensure proper error handling and UI rendering. - Create tests for DataTableSearch to validate search functionality and accessibility. - Update DialogTemplate tests to reflect hardcoded cancel text. - Remove redundant IntersectionObserver mock in SplitText tests. - Unmock react-i18next in Translation tests to validate actual i18n functionality. * refactor: Remove jest-environment-jsdom dependency from package.json; fix: reset package-lock * chore: revert lint fixes * chore: clean up package.json by removing unused devDependencies and redundant test scripts * chore: update package dependencies in package.json and package-lock.json - Added new devDependencies: @babel/core, @babel/preset-env, @babel/preset-react, @babel/preset-typescript, @tanstack/react-table, @tanstack/react-virtual, @testing-library/jest-dom, identity-obj-proxy, jest, jest-environment-jsdom, and lucide-react. - Updated existing devDependencies to their latest versions. - Added new module @asamuzakjp/css-color to package-lock.json with its dependencies. - Updated version of @babel/plugin-transform-destructuring and added @babel/plugin-transform-explicit-resource-management in package-lock.json. --------- Co-authored-by: Danny Avila <danny@librechat.ai>
168 lines
5.2 KiB
TypeScript
168 lines
5.2 KiB
TypeScript
import React, { memo, forwardRef } from 'react';
|
|
import { flexRender } from '@tanstack/react-table';
|
|
import type { TableColumn } from './DataTable.types';
|
|
import type { Row } from '@tanstack/react-table';
|
|
import { TableCell, TableRow, TableRowHeader } from '../Table';
|
|
import { Checkbox } from '../Checkbox';
|
|
import { Skeleton } from '../Skeleton';
|
|
import { cn } from '~/utils';
|
|
|
|
export const SelectionCheckbox = memo(
|
|
({
|
|
checked,
|
|
onChange,
|
|
ariaLabel,
|
|
}: {
|
|
checked: boolean;
|
|
onChange: (value: boolean) => void;
|
|
ariaLabel: string;
|
|
}) => (
|
|
<div
|
|
role="button"
|
|
tabIndex={0}
|
|
onKeyDown={(e) => {
|
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
e.preventDefault();
|
|
onChange(!checked);
|
|
}
|
|
e.stopPropagation();
|
|
}}
|
|
className="flex h-full w-8 items-center justify-center"
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
onChange(!checked);
|
|
}}
|
|
>
|
|
<Checkbox checked={checked} onCheckedChange={onChange} aria-label={ariaLabel} />
|
|
</div>
|
|
),
|
|
);
|
|
|
|
SelectionCheckbox.displayName = 'SelectionCheckbox';
|
|
|
|
interface TableRowComponentProps<TData extends Record<string, unknown>> {
|
|
row: Row<TData>;
|
|
virtualIndex?: number;
|
|
style?: React.CSSProperties;
|
|
selected: boolean;
|
|
}
|
|
|
|
// ...existing code...
|
|
const TableRowComponent = <TData extends Record<string, unknown>>(
|
|
{ row, virtualIndex, style, selected }: TableRowComponentProps<TData>,
|
|
ref: React.Ref<HTMLTableRowElement>,
|
|
) => {
|
|
// Check if we're on mobile - use window.innerWidth for component-level check
|
|
const isSmallScreen = typeof window !== 'undefined' && window.innerWidth < 768;
|
|
|
|
return (
|
|
<TableRow
|
|
ref={ref}
|
|
data-state={selected ? 'selected' : undefined}
|
|
data-index={virtualIndex}
|
|
className="border-none hover:bg-surface-secondary"
|
|
style={style}
|
|
>
|
|
{row.getVisibleCells().map((cell) => {
|
|
const meta = cell.column.columnDef.meta as
|
|
| { className?: string; desktopOnly?: boolean; width?: number; isRowHeader?: boolean }
|
|
| undefined;
|
|
const isDesktopOnly = meta?.desktopOnly;
|
|
const isRowHeader = meta?.isRowHeader;
|
|
const percent = meta?.width;
|
|
const widthStyle =
|
|
cell.column.id === 'select'
|
|
? { width: '32px', maxWidth: '32px', minWidth: '32px' }
|
|
: percent
|
|
? {
|
|
width: `${percent}%`,
|
|
maxWidth: `${percent}%`,
|
|
minWidth: `${percent}%`, // Don't shrink on mobile
|
|
}
|
|
: undefined;
|
|
|
|
const CellComponent = isRowHeader ? TableRowHeader : TableCell;
|
|
|
|
// For desktop-only columns on mobile, keep them in DOM but visually hidden
|
|
// This ensures screen readers can still access the content
|
|
const cellProps =
|
|
isDesktopOnly && isSmallScreen
|
|
? { 'aria-hidden': false as const } // Keep accessible to screen readers
|
|
: {};
|
|
|
|
return (
|
|
<CellComponent
|
|
key={cell.id}
|
|
className={cn(
|
|
'max-w-0 truncate px-2 py-2 md:px-3 md:py-3',
|
|
cell.column.id === 'select' && 'w-8 p-1',
|
|
meta?.className,
|
|
isDesktopOnly && 'hidden md:table-cell',
|
|
)}
|
|
style={widthStyle}
|
|
{...cellProps}
|
|
>
|
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
</CellComponent>
|
|
);
|
|
})}
|
|
</TableRow>
|
|
);
|
|
};
|
|
// ...existing code...
|
|
|
|
type ForwardTableRowComponentType = <TData extends Record<string, unknown>>(
|
|
props: TableRowComponentProps<TData> & React.RefAttributes<HTMLTableRowElement>,
|
|
) => JSX.Element;
|
|
|
|
const ForwardTableRowComponent = forwardRef(TableRowComponent) as ForwardTableRowComponentType;
|
|
|
|
interface GenericRowProps {
|
|
row: Row<Record<string, unknown>>;
|
|
virtualIndex?: number;
|
|
style?: React.CSSProperties;
|
|
selected: boolean;
|
|
}
|
|
|
|
export const MemoizedTableRow = memo(
|
|
ForwardTableRowComponent as (props: GenericRowProps) => JSX.Element,
|
|
(prev: GenericRowProps, next: GenericRowProps) =>
|
|
prev.row.original === next.row.original && prev.selected === next.selected,
|
|
);
|
|
|
|
export const SkeletonRows = memo(
|
|
<TData extends Record<string, unknown>, TValue>({
|
|
count = 10,
|
|
columns,
|
|
}: {
|
|
count?: number;
|
|
columns: TableColumn<TData, TValue>[];
|
|
}) => (
|
|
<>
|
|
{Array.from({ length: count }, (_, index) => (
|
|
<TableRow key={`skeleton-${index}`} className="h-[56px] border-b border-border-light">
|
|
{columns.map((column) => {
|
|
const columnKey = String(
|
|
column.id ?? ('accessorKey' in column && column.accessorKey) ?? '',
|
|
);
|
|
const meta = column.meta as { className?: string; desktopOnly?: boolean } | undefined;
|
|
return (
|
|
<TableCell
|
|
key={columnKey}
|
|
className={cn(
|
|
'px-2 py-2 md:px-3',
|
|
meta?.className,
|
|
meta?.desktopOnly && 'hidden md:table-cell',
|
|
)}
|
|
>
|
|
<Skeleton className="h-6 w-full" />
|
|
</TableCell>
|
|
);
|
|
})}
|
|
</TableRow>
|
|
))}
|
|
</>
|
|
),
|
|
);
|
|
|
|
SkeletonRows.displayName = 'SkeletonRows';
|