Browse Source

Update event handling

Ensure event handling is flawless.
master
TheoryOfNekomata 1 year ago
parent
commit
1caa9c79bf
1 changed files with 57 additions and 37 deletions
  1. +57
    -37
      categories/blob/react/src/components/FileSelectBox/index.tsx

+ 57
- 37
categories/blob/react/src/components/FileSelectBox/index.tsx View File

@@ -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,


Loading…
Cancel
Save