mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-20 10:20:15 +01:00
⌨️ a11y(Settings): Improved Keyboard Navigation & Consistent Styling (#3975)
* feat: settings tba accessible * refactor: cleanup unused code * refactor: improve accessibility and user experience in ChatDirection component * style: focus ring primary class * improve a11y of avatar dialog * style: a11y improvements for Settings * style: focus ring primary class in OriginalDialog component --------- Co-authored-by: Danny Avila <danny@librechat.ai>
This commit is contained in:
parent
1a1e6850a3
commit
d6c0121b19
17 changed files with 507 additions and 513 deletions
|
|
@ -1,7 +1,5 @@
|
|||
import React, { useState, useRef } from 'react';
|
||||
import * as Tabs from '@radix-ui/react-tabs';
|
||||
import { useClearConversationsMutation } from 'librechat-data-provider/react-query';
|
||||
import { SettingsTabValues } from 'librechat-data-provider';
|
||||
import { useConversation, useConversations, useOnClickOutside } from '~/hooks';
|
||||
import { RevokeKeysButton } from './RevokeKeysButton';
|
||||
import { DeleteCacheButton } from './DeleteCacheButton';
|
||||
|
|
@ -37,35 +35,28 @@ function Data() {
|
|||
};
|
||||
|
||||
return (
|
||||
<Tabs.Content
|
||||
value={SettingsTabValues.DATA}
|
||||
role="tabpanel"
|
||||
className="w-full md:min-h-[271px]"
|
||||
ref={dataTabRef}
|
||||
>
|
||||
<div className="flex flex-col gap-3 text-sm text-black dark:text-gray-50">
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||
<ImportConversations />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||
<SharedLinks />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||
<RevokeKeysButton all={true} />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||
<DeleteCacheButton />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||
<ClearChatsButton
|
||||
confirmClear={confirmClearConvos}
|
||||
onClick={clearConvos}
|
||||
showText={true}
|
||||
mutation={clearConvosMutation}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-3 p-1 text-sm text-text-primary">
|
||||
<div className="border-b border-border-medium pb-3 last-of-type:border-b-0">
|
||||
<ImportConversations />
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
<div className="border-b border-border-medium pb-3 last-of-type:border-b-0">
|
||||
<SharedLinks />
|
||||
</div>
|
||||
<div className="border-b border-border-medium pb-3 last-of-type:border-b-0">
|
||||
<RevokeKeysButton all={true} />
|
||||
</div>
|
||||
<div className="border-b border-border-medium pb-3 last-of-type:border-b-0">
|
||||
<DeleteCacheButton />
|
||||
</div>
|
||||
<div className="border-b border-border-medium pb-3 last-of-type:border-b-0">
|
||||
<ClearChatsButton
|
||||
confirmClear={confirmClearConvos}
|
||||
onClick={clearConvos}
|
||||
showText={true}
|
||||
mutation={clearConvosMutation}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState } from 'react';
|
||||
import { useState, useRef } from 'react';
|
||||
import { Import } from 'lucide-react';
|
||||
import type { TError } from 'librechat-data-provider';
|
||||
import { useUploadConversationsMutation } from '~/data-provider';
|
||||
|
|
@ -9,6 +9,7 @@ import { cn } from '~/utils';
|
|||
|
||||
function ImportConversations() {
|
||||
const localize = useLocalize();
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const { showToast } = useToastContext();
|
||||
const [, setErrors] = useState<string[]>([]);
|
||||
|
|
@ -26,7 +27,7 @@ function ImportConversations() {
|
|||
console.error('Error: ', error);
|
||||
setAllowImport(true);
|
||||
setError(
|
||||
(error as TError)?.response?.data?.message ?? 'An error occurred while uploading the file.',
|
||||
(error as TError).response?.data?.message ?? 'An error occurred while uploading the file.',
|
||||
);
|
||||
if (error?.toString().includes('Unsupported import type')) {
|
||||
showToast({
|
||||
|
|
@ -44,13 +45,12 @@ function ImportConversations() {
|
|||
|
||||
const startUpload = async (file: File) => {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file, encodeURIComponent(file?.name || 'File'));
|
||||
formData.append('file', file, encodeURIComponent(file.name || 'File'));
|
||||
|
||||
uploadFile.mutate(formData);
|
||||
};
|
||||
|
||||
const handleFiles = async (_file: File) => {
|
||||
/* Process files */
|
||||
try {
|
||||
await startUpload(_file);
|
||||
} catch (error) {
|
||||
|
|
@ -59,33 +59,49 @@ function ImportConversations() {
|
|||
}
|
||||
};
|
||||
|
||||
const handleFileChange = (event) => {
|
||||
const file = event.target.files[0];
|
||||
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = event.target.files?.[0];
|
||||
if (file) {
|
||||
handleFiles(file);
|
||||
}
|
||||
};
|
||||
|
||||
const handleImportClick = () => {
|
||||
fileInputRef.current?.click();
|
||||
};
|
||||
|
||||
const handleKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => {
|
||||
if (event.key === 'Enter' || event.key === ' ') {
|
||||
event.preventDefault();
|
||||
handleImportClick();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<div>{localize('com_ui_import_conversation_info')}</div>
|
||||
<label htmlFor={'import-conversations-file'} className="btn btn-neutral relative">
|
||||
<button
|
||||
onClick={handleImportClick}
|
||||
onKeyDown={handleKeyDown}
|
||||
disabled={!allowImport}
|
||||
aria-label={localize('com_ui_import_conversation')}
|
||||
className="btn btn-neutral relative"
|
||||
>
|
||||
{allowImport ? (
|
||||
<Import className="mr-1 flex h-4 w-4 items-center stroke-1" />
|
||||
) : (
|
||||
<Spinner className="mr-1 w-4" />
|
||||
)}
|
||||
<span>{localize('com_ui_import_conversation')}</span>
|
||||
<input
|
||||
id={'import-conversations-file'}
|
||||
disabled={!allowImport}
|
||||
value=""
|
||||
type="file"
|
||||
className={cn('hidden')}
|
||||
accept=".json"
|
||||
onChange={handleFileChange}
|
||||
/>
|
||||
</label>
|
||||
</button>
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
className={cn('hidden')}
|
||||
accept=".json"
|
||||
onChange={handleFileChange}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue