Update input components

This commit is contained in:
2026-02-16 23:36:32 -05:00
parent da0db483aa
commit a61e7ce19a
10 changed files with 171 additions and 105 deletions

View File

@@ -1,41 +1,47 @@
import { DangerButton } from "$/component/button";
import type { FileInputProps } from "$/types/InputTypes";
import { humanReadableBytes } from "$/util/FileUtil";
import clsx from "clsx";
import { useEffect, useRef, useState } from "react";
import { useRef, useState } from "react";
import { MdClose } from "react-icons/md";
export default function DragAndDropFileInput({
id,
className,
name,
ariaLabel,
minSize,
maxSize,
showFileName,
showSize,
showFileName = true,
showSize = true,
onChange,
disabled,
children
}: FileInputProps){
}: Readonly<FileInputProps>){
const [ file, setFile ] = useState<File>();
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
onChange?.(file);
}, [ file, onChange ]);
return (
<label
className={clsx(
"flex flex-col items-center justify-center border-2 rounded-lg w-full h-full cursor-pointer",
"flex flex-col items-center justify-center border-2 rounded-lg cursor-pointer",
//TODO: Make hover classes
className
)}
onDragOver={(e) => e.preventDefault()}
onDrop={(e) => {
e.preventDefault();
setFile(e.dataTransfer.files[0]);
const currentFile = e.dataTransfer.files[0];
setFile(currentFile);
if ((minSize && currentFile.size < minSize) || (maxSize && currentFile.size > maxSize)) return;
onChange?.(currentFile);
if(inputRef.current){ inputRef.current.files = e.dataTransfer.files; }
}}
aria-label={ariaLabel}
>
<input
ref={inputRef}
@@ -43,24 +49,40 @@ export default function DragAndDropFileInput({
id={id}
className="sr-only"
name={name}
onChange={(e) => setFile(e.target.files?.[0])}
onChange={(e) => {
const currentFile = e.target.files?.[0];
setFile(currentFile);
if ((minSize && currentFile && currentFile.size < minSize) || (maxSize && currentFile && currentFile.size > maxSize)) return;
onChange?.(currentFile);
}}
disabled={disabled}
/>
<div
className="flex flex-col items-center justify-between w-full h-full px-2"
className="flex flex-col items-center justify-between px-2"
>
{children}
<div className="flex flex-row items-center justify-center">
{children}
</div>
<div
className="flex flex-row items-center justify-between gap-x-8 w-full"
className="flex flex-row items-center justify-between gap-x-2 w-full"
>
{
showFileName &&
<div
className="text-center"
>
<div className="flex flex-row items-center justify-center gap-x-2">
{file?.name}
</div>
}
{
file &&
<DangerButton
className="mr-4"
shape="square"
variant="icon"
onClick={(e) => { e.preventDefault(); setFile(undefined); onChange?.(undefined); }}
>
<MdClose size={22} className="fill-danger"/>
</DangerButton>
}
{
showSize &&
<div

View File

@@ -1,22 +1,25 @@
import { SecondaryButton } from "$/component/button";
import { DangerButton, SecondaryButton } from "$/component/button";
import type { FileInputProps } from "$/types/InputTypes";
import { humanReadableBytes } from "$/util/FileUtil";
import clsx from "clsx";
import { useRef, useState } from "react";
import { FaRegFolderOpen } from "react-icons/fa6";
import { MdClose } from "react-icons/md";
export default function FileInput({
id,
className,
name,
ariaLabel,
minSize,
maxSize,
showFileName,
showSize,
showFileName = true,
showSize = true,
onChange,
disabled,
children
}: FileInputProps){
}: Readonly<FileInputProps>){
const inputRef = useRef<HTMLInputElement>(null);
const [ file, setFile ] = useState<File>();
@@ -24,7 +27,7 @@ export default function FileInput({
return (
<div
className={clsx(
"flex flex-row items-center justify-between w-full border-2 rounded-lg",
"flex flex-row items-center justify-between border-2 rounded-lg",
className
)}
>
@@ -34,7 +37,13 @@ export default function FileInput({
type="file"
className="sr-only"
name={name}
onChange={(e) => { setFile(e.target.files?.[0]); onChange?.(e.target.files?.[0]); }}
aria-label={ariaLabel}
onChange={(e) => {
const currentFile = e.target.files?.[0];
setFile(currentFile);
if ((minSize && currentFile && currentFile.size < minSize) || (maxSize && currentFile && currentFile.size > maxSize)) return;
onChange?.(currentFile);
}}
disabled={disabled}
/>
<div
@@ -48,6 +57,16 @@ export default function FileInput({
showFileName &&
<div>{file?.name}</div>
}
{
file &&
<DangerButton
shape="square"
variant="icon"
onClick={(e) => { e.preventDefault(); setFile(undefined); onChange?.(undefined); }}
>
<MdClose size={22} className="fill-danger"/>
</DangerButton>
}
{
!children && !showFileName &&
<>&nbsp;</>
@@ -56,12 +75,13 @@ export default function FileInput({
showSize &&
<div
className={clsx(
"ml-4",
{
"text-red-600": minSize && file?.size && file?.size < minSize,
"text-red-600 ": maxSize && file?.size && file?.size > maxSize,
"text-green-600": minSize && !maxSize && file?.size && file?.size > minSize,
"text-green-600 ": !minSize && maxSize && file?.size && file?.size < maxSize,
" text-green-600": minSize && maxSize && file?.size && file?.size > minSize && file?.size < maxSize
"text-danger": minSize && file?.size && file?.size < minSize,
"text-danger ": maxSize && file?.size && file?.size > maxSize,
"text-success": minSize && !maxSize && file?.size && file?.size > minSize,
"text-success ": !minSize && maxSize && file?.size && file?.size < maxSize,
" text-success": minSize && maxSize && file?.size && file?.size > minSize && file?.size < maxSize
}
)}
>
@@ -75,10 +95,13 @@ export default function FileInput({
<SecondaryButton
className="text-nowrap rounded-r-lg"
rounding="none"
shape="square"
size="lg"
onClick={() => { inputRef.current?.click(); }}
disabled={disabled}
aria-label="Select File"
>
Click Me
<FaRegFolderOpen />
</SecondaryButton>
</div>
</div>