🎨 feat: enhance UI & accessibility in file handling components (#5086)

*  feat: Add localization for page display and enhance button styles

*  refactor: improve image preview component styles

*  refactor: enhance modal close behavior and prevent refocus on certain elements

*  refactor: enhance file row layout and improve image preview animation
This commit is contained in:
Marco Beretta 2024-12-23 11:14:40 +01:00 committed by GitHub
parent bdb222d5f4
commit dfe5498301
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 466 additions and 279 deletions

View file

@ -1,116 +1,25 @@
import { useCallback } from 'react';
import {
fileConfig as defaultFileConfig,
checkOpenAIStorage,
mergeFileConfig,
megabyte,
isAssistantsEndpoint,
} from 'librechat-data-provider';
import type { Row } from '@tanstack/react-table';
import type { TFile } from 'librechat-data-provider';
import { useFileMapContext, useChatContext, useToastContext } from '~/Providers';
import ImagePreview from '~/components/Chat/Input/Files/ImagePreview';
import FilePreview from '~/components/Chat/Input/Files/FilePreview';
import { useUpdateFiles, useLocalize } from '~/hooks';
import { useGetFileConfig } from '~/data-provider';
import { getFileType } from '~/utils';
export default function PanelFileCell({ row }: { row: Row<TFile> }) {
const localize = useLocalize();
const fileMap = useFileMapContext();
const { showToast } = useToastContext();
const { setFiles, conversation } = useChatContext();
const { data: fileConfig = defaultFileConfig } = useGetFileConfig({
select: (data) => mergeFileConfig(data),
});
const { addFile } = useUpdateFiles(setFiles);
const handleFileClick = useCallback(() => {
const file = row.original;
const endpoint = conversation?.endpoint;
const fileData = fileMap?.[file.file_id];
if (!fileData) {
return;
}
if (!endpoint) {
return showToast({ message: localize('com_ui_attach_error'), status: 'error' });
}
if (checkOpenAIStorage(fileData?.source ?? '') && !isAssistantsEndpoint(endpoint)) {
return showToast({
message: localize('com_ui_attach_error_openai'),
status: 'error',
});
} else if (!checkOpenAIStorage(fileData?.source ?? '') && isAssistantsEndpoint(endpoint)) {
showToast({
message: localize('com_ui_attach_warn_endpoint'),
status: 'warning',
});
}
const { fileSizeLimit, supportedMimeTypes } =
fileConfig.endpoints[endpoint] ?? fileConfig.endpoints.default;
if (fileData.bytes > fileSizeLimit) {
return showToast({
message: `${localize('com_ui_attach_error_size')} ${
fileSizeLimit / megabyte
} MB (${endpoint})`,
status: 'error',
});
}
const isSupportedMimeType = defaultFileConfig.checkType(file.type, supportedMimeTypes);
if (!isSupportedMimeType) {
return showToast({
message: `${localize('com_ui_attach_error_type')} ${file.type} (${endpoint})`,
status: 'error',
});
}
addFile({
progress: 1,
attached: true,
file_id: fileData.file_id,
filepath: fileData.filepath,
preview: fileData.filepath,
type: fileData.type,
height: fileData.height,
width: fileData.width,
filename: fileData.filename,
source: fileData.source,
size: fileData.bytes,
});
}, [addFile, fileMap, row.original, conversation, localize, showToast, fileConfig.endpoints]);
const file = row.original;
if (file.type?.startsWith('image')) {
return (
<div
onClick={handleFileClick}
className="flex cursor-pointer gap-2 rounded-md dark:hover:bg-gray-700"
>
return (
<div className="flex items-center gap-2">
{file.type.startsWith('image') ? (
<ImagePreview
url={file.filepath}
className="relative h-10 w-10 shrink-0 overflow-hidden rounded-md"
className="h-10 w-10"
source={file.source}
alt={file.filename}
/>
<span className="self-center truncate text-xs">{file.filename}</span>
</div>
);
}
const fileType = getFileType(file.type);
return (
<div
onClick={handleFileClick}
className="flex cursor-pointer gap-2 rounded-md dark:hover:bg-gray-700"
>
{fileType && <FilePreview fileType={fileType} className="relative" file={file} />}
<span className="self-center truncate">{file.filename}</span>
) : (
<FilePreview fileType={getFileType(file.type)} file={file} />
)}
<span className="truncate text-xs">{file.filename}</span>
</div>
);
}