|
@@ -83,30 +83,30 @@ export const FileSelectBoxDefaultPreviewComponent = React.forwardRef< |
|
|
{!mini && ( |
|
|
{!mini && ( |
|
|
<> |
|
|
<> |
|
|
{typeof file?.type === 'string' && ( |
|
|
{typeof file?.type === 'string' && ( |
|
|
<div className="w-full whitespace-nowrap overflow-hidden text-ellipsis">{file?.type}</div> |
|
|
|
|
|
|
|
|
<div className="w-full whitespace-nowrap overflow-hidden text-ellipsis">{file?.type}</div> |
|
|
)} |
|
|
)} |
|
|
{typeof file?.size === 'number' && ( |
|
|
{typeof file?.size === 'number' && ( |
|
|
<div |
|
|
|
|
|
title={new Intl.NumberFormat(undefined, { |
|
|
|
|
|
style: 'unit', |
|
|
|
|
|
unit: 'byte', |
|
|
|
|
|
unitDisplay: 'long', |
|
|
|
|
|
}).format(file.size ?? 0)} |
|
|
|
|
|
className="w-full whitespace-nowrap overflow-hidden text-ellipsis tabular-nums" |
|
|
|
|
|
> |
|
|
|
|
|
{new Intl.NumberFormat(undefined, { |
|
|
|
|
|
style: 'unit', |
|
|
|
|
|
unit: 'kilobyte', |
|
|
|
|
|
unitDisplay: 'long', |
|
|
|
|
|
}).format(file.size ?? 0)} |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
<div |
|
|
|
|
|
title={new Intl.NumberFormat(undefined, { |
|
|
|
|
|
style: 'unit', |
|
|
|
|
|
unit: 'byte', |
|
|
|
|
|
unitDisplay: 'long', |
|
|
|
|
|
}).format(file.size ?? 0)} |
|
|
|
|
|
className="w-full whitespace-nowrap overflow-hidden text-ellipsis tabular-nums" |
|
|
|
|
|
> |
|
|
|
|
|
{new Intl.NumberFormat(undefined, { |
|
|
|
|
|
style: 'unit', |
|
|
|
|
|
unit: 'kilobyte', |
|
|
|
|
|
unitDisplay: 'long', |
|
|
|
|
|
}).format(file.size ?? 0)} |
|
|
|
|
|
</div> |
|
|
)} |
|
|
)} |
|
|
{typeof file?.lastModified === 'number' && ( |
|
|
{typeof file?.lastModified === 'number' && ( |
|
|
<div className="w-full whitespace-nowrap overflow-hidden text-ellipsis"> |
|
|
|
|
|
<time dateTime={new Date(file.lastModified).toISOString()}> |
|
|
|
|
|
{new Date(file.lastModified).toDateString()} |
|
|
|
|
|
</time> |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
<div className="w-full whitespace-nowrap overflow-hidden text-ellipsis"> |
|
|
|
|
|
<time dateTime={new Date(file.lastModified).toISOString()}> |
|
|
|
|
|
{new Date(file.lastModified).toDateString()} |
|
|
|
|
|
</time> |
|
|
|
|
|
</div> |
|
|
)} |
|
|
)} |
|
|
</> |
|
|
</> |
|
|
)} |
|
|
)} |
|
@@ -129,6 +129,7 @@ export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileS |
|
|
placeholder, |
|
|
placeholder, |
|
|
previewComponent: FilePreviewComponent = FileSelectBoxDefaultPreviewComponent, |
|
|
previewComponent: FilePreviewComponent = FileSelectBoxDefaultPreviewComponent, |
|
|
style, |
|
|
style, |
|
|
|
|
|
onBlur, |
|
|
...etcProps |
|
|
...etcProps |
|
|
}, |
|
|
}, |
|
|
forwardedRef, |
|
|
forwardedRef, |
|
@@ -181,18 +182,24 @@ export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileS |
|
|
const labelId = React.useId(); |
|
|
const labelId = React.useId(); |
|
|
const id = useFallbackId(idProp); |
|
|
const id = useFallbackId(idProp); |
|
|
|
|
|
|
|
|
const cancelEvent = (e: React.DragEvent) => { |
|
|
|
|
|
|
|
|
const cancelEvent: React.DragEventHandler<HTMLDivElement> = React.useCallback((e) => { |
|
|
e.stopPropagation(); |
|
|
e.stopPropagation(); |
|
|
e.preventDefault(); |
|
|
e.preventDefault(); |
|
|
}; |
|
|
|
|
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
const filesCount = fileList?.length ?? 0; |
|
|
|
|
|
|
|
|
const handleDrop: React.DragEventHandler<HTMLDivElement> = React.useCallback((e) => { |
|
|
|
|
|
cancelEvent(e); |
|
|
|
|
|
doSetFileList(e); |
|
|
|
|
|
}, [cancelEvent, doSetFileList]); |
|
|
|
|
|
|
|
|
const handleFileChange: React.ChangeEventHandler<FileSelectBoxDerivedElement> = (e) => { |
|
|
|
|
|
|
|
|
const handleFileChange: React.ChangeEventHandler< |
|
|
|
|
|
FileSelectBoxDerivedElement |
|
|
|
|
|
> = React.useCallback((e) => { |
|
|
const { currentTarget } = e; |
|
|
const { currentTarget } = e; |
|
|
if (clientSide && currentTarget.files && currentTarget.files.length > 0) { |
|
|
if (clientSide && currentTarget.files && currentTarget.files.length > 0) { |
|
|
setFileList(currentTarget.files); |
|
|
setFileList(currentTarget.files); |
|
|
setLastUpdated(Date.now()); |
|
|
setLastUpdated(Date.now()); |
|
|
|
|
|
setAboutToSelect(false); |
|
|
onChange?.(e); |
|
|
onChange?.(e); |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
@@ -205,26 +212,29 @@ export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileS |
|
|
} |
|
|
} |
|
|
e.preventDefault(); |
|
|
e.preventDefault(); |
|
|
e.currentTarget.files = fileList ?? null; |
|
|
e.currentTarget.files = fileList ?? null; |
|
|
}; |
|
|
|
|
|
|
|
|
setAboutToSelect(false); |
|
|
|
|
|
}, [clientSide, fileList, onChange]); |
|
|
|
|
|
|
|
|
const handleKeyDown: React.KeyboardEventHandler<FileSelectBoxDerivedElement> = (e) => { |
|
|
|
|
|
|
|
|
const handleKeyDown: React.KeyboardEventHandler< |
|
|
|
|
|
FileSelectBoxDerivedElement |
|
|
|
|
|
> = React.useCallback((e) => { |
|
|
const { code } = e; |
|
|
const { code } = e; |
|
|
if (code === 'Backspace' || code === 'Delete') { |
|
|
if (code === 'Backspace' || code === 'Delete') { |
|
|
setDeleteKeyPressed(true); |
|
|
setDeleteKeyPressed(true); |
|
|
} else if (code === 'Enter' || code === 'Space' || code === 'Return') { |
|
|
} else if (code === 'Enter' || code === 'Space' || code === 'Return') { |
|
|
setAboutToSelect(true); |
|
|
setAboutToSelect(true); |
|
|
} |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
const handleKeyUp: React.KeyboardEventHandler<FileSelectBoxDerivedElement> = (e) => { |
|
|
|
|
|
|
|
|
const handleKeyUp: React.KeyboardEventHandler< |
|
|
|
|
|
FileSelectBoxDerivedElement |
|
|
|
|
|
> = React.useCallback((e) => { |
|
|
const { code } = e; |
|
|
const { code } = e; |
|
|
if (code === 'Backspace' || code === 'Delete') { |
|
|
if (code === 'Backspace' || code === 'Delete') { |
|
|
doSetFileList(e); |
|
|
doSetFileList(e); |
|
|
setDeleteKeyPressed(false); |
|
|
setDeleteKeyPressed(false); |
|
|
} else if (code === 'Enter' || code === 'Space' || code === 'Return') { |
|
|
|
|
|
setAboutToSelect(false); |
|
|
|
|
|
} |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
}, [doSetFileList]); |
|
|
|
|
|
|
|
|
const handleReselectMouseDown: React.MouseEventHandler< |
|
|
const handleReselectMouseDown: React.MouseEventHandler< |
|
|
HTMLButtonElement |
|
|
HTMLButtonElement |
|
@@ -253,6 +263,14 @@ export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileS |
|
|
}); |
|
|
}); |
|
|
}, [defaultRef]); |
|
|
}, [defaultRef]); |
|
|
|
|
|
|
|
|
|
|
|
const handleBlur: React.FocusEventHandler< |
|
|
|
|
|
FileSelectBoxDerivedElement |
|
|
|
|
|
> = React.useCallback((e) => { |
|
|
|
|
|
setAboutToSelect(false); |
|
|
|
|
|
setDeleteKeyPressed(false); |
|
|
|
|
|
onBlur?.(e); |
|
|
|
|
|
}, [onBlur]); |
|
|
|
|
|
|
|
|
React.useEffect(() => { |
|
|
React.useEffect(() => { |
|
|
const handleLabelMouseUp = () => { |
|
|
const handleLabelMouseUp = () => { |
|
|
setAboutToSelect(false); |
|
|
setAboutToSelect(false); |
|
@@ -265,6 +283,8 @@ export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileS |
|
|
}; |
|
|
}; |
|
|
}, [defaultRef, aboutToSelect]); |
|
|
}, [defaultRef, aboutToSelect]); |
|
|
|
|
|
|
|
|
|
|
|
const filesCount = fileList?.length ?? 0; |
|
|
|
|
|
|
|
|
return ( |
|
|
return ( |
|
|
<div |
|
|
<div |
|
|
className={clsx( |
|
|
className={clsx( |
|
@@ -276,10 +296,7 @@ export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileS |
|
|
)} |
|
|
)} |
|
|
onDragEnter={clientSide ? cancelEvent : undefined} |
|
|
onDragEnter={clientSide ? cancelEvent : undefined} |
|
|
onDragOver={clientSide ? cancelEvent : undefined} |
|
|
onDragOver={clientSide ? cancelEvent : undefined} |
|
|
onDrop={clientSide ? (e) => { |
|
|
|
|
|
cancelEvent(e); |
|
|
|
|
|
doSetFileList(e); |
|
|
|
|
|
} : undefined} |
|
|
|
|
|
|
|
|
onDrop={clientSide ? handleDrop : undefined} |
|
|
data-testid="root" |
|
|
data-testid="root" |
|
|
style={{ |
|
|
style={{ |
|
|
height: clientSide ? 64 : undefined, |
|
|
height: clientSide ? 64 : undefined, |
|
@@ -307,8 +324,9 @@ export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileS |
|
|
<button |
|
|
<button |
|
|
type="button" |
|
|
type="button" |
|
|
disabled={disabled} |
|
|
disabled={disabled} |
|
|
|
|
|
tabIndex={-1} |
|
|
className={clsx( |
|
|
className={clsx( |
|
|
'flex items-center justify-center absolute top-0 left-0 w-full h-full cursor-pointer select-none', |
|
|
|
|
|
|
|
|
'flex items-center focus:outline-0 justify-center absolute top-0 left-0 w-full h-full cursor-pointer select-none', |
|
|
(fileList?.length ?? 0) > 0 && 'opacity-0', |
|
|
(fileList?.length ?? 0) > 0 && 'opacity-0', |
|
|
)} |
|
|
)} |
|
|
data-testid="clickArea" |
|
|
data-testid="clickArea" |
|
@@ -324,6 +342,7 @@ export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileS |
|
|
disabled={disabled} |
|
|
disabled={disabled} |
|
|
ref={defaultRef} |
|
|
ref={defaultRef} |
|
|
type="file" |
|
|
type="file" |
|
|
|
|
|
onBlur={handleBlur} |
|
|
onKeyDown={clientSide ? handleKeyDown : undefined} |
|
|
onKeyDown={clientSide ? handleKeyDown : undefined} |
|
|
onKeyUp={clientSide ? handleKeyUp : undefined} |
|
|
onKeyUp={clientSide ? handleKeyUp : undefined} |
|
|
className={clsx( |
|
|
className={clsx( |
|
@@ -411,8 +430,9 @@ export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileS |
|
|
disabled={disabled} |
|
|
disabled={disabled} |
|
|
onMouseDown={handleReselectMouseDown} |
|
|
onMouseDown={handleReselectMouseDown} |
|
|
onMouseUp={handleReselectMouseUp} |
|
|
onMouseUp={handleReselectMouseUp} |
|
|
|
|
|
tabIndex={-1} |
|
|
className={clsx( |
|
|
className={clsx( |
|
|
'flex w-full h-full bg-negative text-primary cursor-pointer items-center justify-center leading-none gap-4 select-none', |
|
|
|
|
|
|
|
|
'flex w-full h-full focus:outline-0 bg-negative text-primary cursor-pointer items-center justify-center leading-none gap-4 select-none', |
|
|
{ |
|
|
{ |
|
|
'group-focus-within:text-secondary group-focus-within:active:text-tertiary': !aboutToSelect, |
|
|
'group-focus-within:text-secondary group-focus-within:active:text-tertiary': !aboutToSelect, |
|
|
'text-tertiary': aboutToSelect, |
|
|
'text-tertiary': aboutToSelect, |
|
|