* chore: add i18n localization comment for AlwaysMakeProd component * feat: enhance accessibility by adding aria-label and aria-labelledby to Switch component * feat: add aria-labels for accessibility in Agent and Assistant avatar buttons * fix: add switch aria-labels for accessibility in various components * feat: add aria-labels and localization keys for accessibility in DataTable, DataTableColumnHeader, and OGDialogTemplate components * chore: refactor out nested ternary * feat: add aria-label to DataTable filter button for My Files modal * feat: add aria-labels for Buttons and localization strings * feat: add aria-labels to Checkboxes in Agent Builder * feat: enhance accessibility by adding aria-label and aria-labelledby to Checkbox component * feat: add aria-label to FileSearchCheckbox in Agent Builder * feat: add aria-label to Prompts text input area * feat: enhance accessibility by adding aria-label and aria-labelledby to TextAreaAutosize component * feat: remove improper role: "list" prop from List in Conversations.tsx to enhance accessibility and stop aria rules conflicting within react-virtualized component * feat: enhance accessibility by allowing tab navigation and adding ring highlights for conversation title editing accept/reject buttons * feat: add aria-label to Copy Link button in the conversation share modal * feat: add title to QR code svg in conversation share modal to describe the image content * feat: enhance accessibility by making Agent Avatar upload keyboard navigable and round out highlight border on focus * feat: enhance accessibility by adding aria attributes around alerting users with screen readers to invalid email address inputs in the Agent Builder * feat: add aria-labels to buttons in Advanced panel of Agent Builder * feat: enhance accessibility by making FileUpload and Clear All buttons in PresetItems keyboard navigable * feat: enchance accessiblity by indexing view and delete button aria-labels in shared links management modal to their specific chat titles * feat: add border highlighting on focus for AnimatedSearchInput * feat: add category description to aria-labels for prompts in ListCard * feat: add proper scoping to rows and columns in table headers * feat: add localized aria-labelling to EditTextPart's TextAreaAutosize component and base dynamic paramters panel components and their supporting translation keys * feat: add localized aria-labels and aria-labelledBy to Checkbox components without them * feat: add localized aria-labeledBy for endpoint settings Sliders * feat: add localized aria-labels for TextareaAutosize components * chore: remove unused i18n string * feat: add localized aria-label for BookmarkForm Checkbox * fix: add stopPropagation onKeyDown for Preview and Edit menu items in prompts that was causing the prompts to inadvertently be sent when triggered with keyboard navigation when Auto-send Prompts was toggled on * fix: switch TableCell to TableHead for title cells according to harvard issue #789 * fix: add more descriptive localization key for file filter button in DataTable * chore: remove self-explanatory code comment from RenameForm * fix: remove stray bg-yellow highlight that was left in during debugging * fix: add aria-label to model configurator panel back button * fix: undo incorrect hoist of tool name split for aria-label and span in MCPInput --------- Co-authored-by: Danny Avila <danny@librechat.ai>
101 lines
2.8 KiB
TypeScript
101 lines
2.8 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { FileUp } from 'lucide-react';
|
|
import { cn } from '~/utils/';
|
|
import { useLocalize } from '~/hooks';
|
|
|
|
type FileUploadProps = {
|
|
onFileSelected: (jsonData: Record<string, unknown>) => void;
|
|
className?: string;
|
|
containerClassName?: string;
|
|
successText?: string;
|
|
invalidText?: string;
|
|
validator?: ((data: Record<string, unknown>) => boolean) | null;
|
|
text?: string;
|
|
id?: string;
|
|
};
|
|
|
|
const FileUpload: React.FC<FileUploadProps> = ({
|
|
onFileSelected,
|
|
className = '',
|
|
containerClassName = '',
|
|
successText = null,
|
|
invalidText = null,
|
|
validator = null,
|
|
text = null,
|
|
id = '1',
|
|
}) => {
|
|
const [statusColor, setStatusColor] = useState<string>('text-gray-600');
|
|
const [status, setStatus] = useState<null | string>(null);
|
|
const localize = useLocalize();
|
|
|
|
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
|
const file = event.target.files?.[0];
|
|
if (!file) {
|
|
return;
|
|
}
|
|
|
|
const reader = new FileReader();
|
|
reader.onload = (e) => {
|
|
const jsonData = JSON.parse(e.target?.result as string);
|
|
if (validator && !validator(jsonData)) {
|
|
setStatus('invalid');
|
|
setStatusColor('text-red-600');
|
|
return;
|
|
}
|
|
|
|
if (validator) {
|
|
setStatus('success');
|
|
setStatusColor('text-green-500 dark:text-green-500');
|
|
}
|
|
|
|
onFileSelected(jsonData);
|
|
};
|
|
reader.readAsText(file);
|
|
};
|
|
|
|
let statusText: string;
|
|
if (!status) {
|
|
statusText = text ?? localize('com_ui_import');
|
|
} else if (status === 'success') {
|
|
statusText = successText ?? localize('com_ui_upload_success');
|
|
} else {
|
|
statusText = invalidText ?? localize('com_ui_upload_invalid');
|
|
}
|
|
|
|
const handleClick = () => {
|
|
const fileInput = document.getElementById(`file-upload-${id}`) as HTMLInputElement;
|
|
if (fileInput) {
|
|
fileInput.click();
|
|
}
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<button
|
|
type="button"
|
|
onClick={handleClick}
|
|
className={cn(
|
|
'mr-1 flex h-auto cursor-pointer items-center rounded bg-transparent px-2 py-1 text-xs font-normal transition-colors hover:bg-gray-100 hover:text-green-600 focus:ring-ring dark:bg-transparent dark:text-gray-300 dark:hover:bg-gray-700 dark:hover:text-green-500',
|
|
statusColor,
|
|
containerClassName,
|
|
)}
|
|
aria-label={statusText}
|
|
>
|
|
<FileUp className="mr-1 flex w-[22px] items-center stroke-1" aria-hidden="true" />
|
|
<span className="flex text-xs">{statusText}</span>
|
|
</button>
|
|
<input
|
|
id={`file-upload-${id}`}
|
|
value=""
|
|
type="file"
|
|
className={cn('hidden', className)}
|
|
accept=".json"
|
|
onChange={handleFileChange}
|
|
tabIndex={-1}
|
|
/>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default FileUpload;
|