Browse Source

Fix fileselectbox resize

Ensure fileselectbox resizes properly for enhanced and non-enhanced versions.
master
TheoryOfNekomata 8 months ago
parent
commit
0c4fcf53a0
4 changed files with 226 additions and 206 deletions
  1. +1
    -0
      TODO.md
  2. +223
    -205
      categories/web/blob/react/src/components/FileSelectBox/index.tsx
  3. +1
    -1
      storybook/react/package.json
  4. +1
    -0
      storybook/react/tailwind.config.ts

+ 1
- 0
TODO.md View File

@@ -79,3 +79,4 @@
- [ ] react-refractor, fix rendering on Lynx!
- [ ] Limit component props to only those that are relevant
- [ ] Extract `packages/` dir
- [ ] Create tesseract plugin for defining component styles

+ 223
- 205
categories/web/blob/react/src/components/FileSelectBox/index.tsx View File

@@ -153,6 +153,7 @@ export interface FileSelectBoxProps<
* Clear label.
*/
clearLabel?: string,
resize?: boolean,
}

const isButtonElement = (el: HTMLElement): el is FileSelectBoxActionElement => el.tagName === 'BUTTON';
@@ -180,14 +181,14 @@ type SelectKey = typeof SELECT_KEYS[number];
*/
export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileSelectBoxProps>((
{
label = '',
hint = '',
border = false,
block = false,
enhanced: enhancedProp = false,
hiddenLabel = false,
multiple = false,
disabled = false,
label = '' as const,
hint = '' as const,
border = false as const,
block = false as const,
enhanced: enhancedProp = false as const,
hiddenLabel = false as const,
multiple = false as const,
disabled = false as const,
className,
onChange,
id: idProp,
@@ -195,9 +196,10 @@ export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileS
previewComponent: FilePreviewComponent = FileSelectBoxDefaultPreviewComponent,
style,
onBlur,
reselectLabel = 'Reselect',
clearLabel = 'Clear',
reselectLabel = 'Reselect' as const,
clearLabel = 'Clear' as const,
onPaste,
resize = false as const,
...etcProps
},
forwardedRef,
@@ -302,12 +304,16 @@ export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileS
setFileList(currentTarget.files);
setLastUpdated(Date.now());
setAboutToSelect(false);
} else if (clearFileListRef.current) {
onChange?.(e);
return;
}
if (clearFileListRef.current) {
clearFileListRef.current = false;
setFileList(undefined);
setLastUpdated(Date.now());
onChange?.(e);
}
onChange?.(e);
// prevent triggering onChange when the user cancels the file picker.
}, [clearFileListRef, onChange]);

const handleKeyDown: React.KeyboardEventHandler<
@@ -328,10 +334,12 @@ export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileS
const handleReselectMouseDown: React.MouseEventHandler<
FileSelectBoxActionElement
> = React.useCallback(() => {
setAboutToSelect(true);
setTimeout(() => {
const fileInput = defaultRef.current as FileSelectBoxDerivedElement;
fileInput.focus();
setAboutToSelect(true);
setTimeout(() => {
const fileInput = defaultRef.current as FileSelectBoxDerivedElement;
fileInput.focus();
});
});
}, [defaultRef]);

@@ -349,11 +357,13 @@ export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileS
const handleDeleteMouseDown: React.MouseEventHandler<
FileSelectBoxActionElement
> = React.useCallback(() => {
setAboutToClear(true);
setTimeout(() => {
const fileInput = defaultRef.current as FileSelectBoxDerivedElement;
fileInput.focus();
});
setTimeout(() => {
setAboutToClear(true);
setTimeout(() => {
const fileInput = defaultRef.current as FileSelectBoxDerivedElement;
fileInput.focus();
});
});
}, [defaultRef]);

const handleBlur: React.FocusEventHandler<
@@ -385,204 +395,211 @@ export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileS
'relative rounded ring-secondary/50 group file-select-box',
'focus-within:ring-4',
block && 'flex w-full',
!block && 'inline-flex w-64 min-h-16 justify-center items-center',
!block && 'inline-flex align-top w-64 justify-center items-center',
className,
)}
onDragEnter={clientSide ? cancelEvent : undefined}
onDragOver={clientSide ? cancelEvent : undefined}
onDrop={clientSide ? handleDrop : undefined}
data-testid="root"
style={{
height: clientSide ? DEFAULT_ENHANCED_HEIGHT_PX : undefined,
...(style ?? {}),
}}
style={style}
>
{label && (
<label
data-testid="label"
id={labelId}
htmlFor={id}
className={clsx(
'absolute z-[1] w-full top-0.5 left-0 pointer-events-none select-none pl-1 text-xxs font-bold peer-disabled:opacity-50 group-focus-within:text-secondary text-primary leading-none bg-negative',
{
'sr-only': hiddenLabel,
},
)}
>
<div className="w-full whitespace-nowrap h-[1.1em] overflow-hidden text-ellipsis">
{label}
</div>
</label>
)}
{clientSide && (
<FileSelectBoxActionElementComponent
type="button"
disabled={disabled}
tabIndex={-1}
className={clsx(
'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',
)}
data-testid="clickArea"
onMouseDown={handleReselectMouseDown}
onMouseUp={handleReselectMouseUp}
>
{placeholder}
</FileSelectBoxActionElementComponent>
)}
<FileSelectBoxDerivedElementComponent
{...etcProps}
id={id}
disabled={disabled}
ref={defaultRef}
type="file"
onBlur={handleBlur}
onKeyDown={clientSide ? handleKeyDown : undefined}
onKeyUp={clientSide ? doSetFileList : undefined}
className={clsx(
'peer box-border focus:outline-0 px-4 pt-2 block resize-y min-h-16 cursor-pointer disabled:cursor-not-allowed file:bg-transparent file:text-primary file:block file:font-bold file:font-semi-expanded file:uppercase file:p-0 file:border-0 group-focus-within:file:text-secondary',
{
'sr-only': clientSide,
'h-full w-full': !clientSide,
},
)}
onChange={clientSide ? handleFileChange : onChange}
onPaste={clientSide ? doSetFileList : onPaste}
multiple={multiple}
data-testid="input"
aria-labelledby={label ? `${labelId}` : undefined}
style={{
height: clientSide ? undefined : DEFAULT_NON_ENHANCED_SIDE_HEIGHT_PX,
}}
/>
{filesCount < 1
&& clientSide
&& hint
&& (
<div
data-testid="hint"
className="absolute top-0 left-0 w-full h-full pointer-events-none box-border overflow-hidden pt-4"
>
<div className="flex items-center justify-center w-full h-full">
{hint}
</div>
</div>
)}
{filesCount > 0
&& clientSide
&& (
<React.Fragment
key={lastUpdated}
>
<div
className="sm:absolute top-0 left-0 w-full h-full pointer-events-none pb-12 box-border overflow-hidden pt-8"
data-testid="preview"
>
{multiple
&& (
<div className="pointer-events-auto w-full h-full overflow-auto px-4 box-border">
<div className="w-full grid gap-2 grid-cols-3">
{fileListArray.map((file, i) => (
<div
key={file.name ?? i}
data-testid="selectedFileItem"
className="w-full p-2 aspect-square rounded overflow-hidden relative before:absolute before:content-[''] before:bg-current before:top-0 before:left-0 before:w-full before:h-full before:opacity-10"
>
<FilePreviewComponent
file={file}
enhanced={clientSide}
disabled={disabled}
mini
/>
</div>
))}
</div>
</div>
)}
{!multiple
&& fileListArray.map((file, i) => (
<div
key={file.name ?? i}
className="pointer-events-auto w-full h-full px-4 box-border"
>
<div
data-testid="selectedFileItem"
className="h-full w-full p-4 box-border rounded overflow-hidden relative before:absolute before:content-[''] before:bg-current before:top-0 before:left-0 before:w-full before:h-full before:opacity-10"
>
<div
className="w-full h-full relative"
>
<FilePreviewComponent
file={file}
enhanced={clientSide}
disabled={disabled}
/>
</div>
</div>
</div>
))}
</div>
<div
data-testid="actions"
className="absolute bottom-0 left-0 w-full text-center h-12 box-border flex"
>
<div className="w-0 flex-auto flex flex-col items-center justify-center h-full">
<FileSelectBoxActionElementComponent
type="button"
data-testid="reselect"
disabled={disabled}
onMouseDown={handleReselectMouseDown}
onMouseUp={handleReselectMouseUp}
tabIndex={-1}
className={clsx(
'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,
'text-tertiary': aboutToSelect,
},
)}
>
<div
className={clsx(
'w-full overflow-hidden min-h-16 relative pt-4 min-h-16 rounded-inherit',
{
'h-4': clientSide,
'h-16': !clientSide,
'resize-y': resize,
'pb-4': clientSide && filesCount > 0,
},
)}
>
{label && (
<label
data-testid="label"
id={labelId}
htmlFor={id}
className={clsx(
'absolute z-[1] w-full top-0.5 left-0 pointer-events-none select-none pl-1 text-xxs font-bold peer-disabled:opacity-50 group-focus-within:text-secondary text-primary leading-none bg-negative',
{
'sr-only': hiddenLabel,
},
)}
>
<div className="w-full whitespace-nowrap h-[1.1em] overflow-hidden text-ellipsis">
{label}
</div>
</label>
)}
{clientSide && filesCount <= 0 && (
<FileSelectBoxActionElementComponent
type="button"
disabled={disabled}
tabIndex={-1}
className={clsx(
'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',
)}
data-testid="clickArea"
onMouseDown={handleReselectMouseDown}
onMouseUp={handleReselectMouseUp}
>
{placeholder}
</FileSelectBoxActionElementComponent>
)}
<FileSelectBoxDerivedElementComponent
{...etcProps}
id={id}
disabled={disabled}
ref={defaultRef}
type="file"
onBlur={handleBlur}
onKeyDown={clientSide ? handleKeyDown : undefined}
onKeyUp={clientSide ? doSetFileList : undefined}
className={clsx(
'peer box-border focus:outline-0 px-4 file:cursor-pointer disabled:file:cursor-pointer block cursor-pointer disabled:cursor-not-allowed file:bg-transparent file:text-primary file:block file:font-bold file:font-semi-expanded file:uppercase file:p-0 file:border-0 group-focus-within:file:text-secondary',
{
'sr-only': clientSide,
'h-full w-full': !clientSide,
},
)}
onChange={clientSide ? handleFileChange : onChange}
onPaste={clientSide ? doSetFileList : onPaste}
multiple={multiple}
data-testid="input"
aria-labelledby={label ? `${labelId}` : undefined}
/>
{filesCount <= 0
&& clientSide
&& hint
&& (
<div
data-testid="hint"
className="w-full h-full pointer-events-none box-border overflow-hidden"
>
<div className="flex items-center justify-center w-full h-full px-4">
{hint}
</div>
</div>
)}
{filesCount > 0
&& clientSide
&& (
<div
key={lastUpdated}
className="flex flex-col h-full gap-4"
>
<div
className="flex-auto pointer-events-none overflow-hidden"
data-testid="preview"
>
{multiple
&& (
<div className="pointer-events-auto w-full h-full overflow-auto px-4 box-border">
<div className="w-full grid gap-2 grid-cols-3">
{fileListArray.map((file, i) => (
<div
key={file.name ?? i}
data-testid="selectedFileItem"
className="w-full p-2 aspect-square rounded overflow-hidden relative before:absolute before:content-[''] before:bg-current before:top-0 before:left-0 before:w-full before:h-full before:opacity-10"
>
<FilePreviewComponent
file={file}
enhanced={clientSide}
disabled={disabled}
mini
/>
</div>
))}
</div>
</div>
)}
{!multiple
&& fileListArray.map((file, i) => (
<div
key={file.name ?? i}
className="pointer-events-auto w-full h-full px-4 box-border"
>
<div
data-testid="selectedFileItem"
className="h-full w-full p-4 box-border rounded overflow-hidden relative before:absolute before:content-[''] before:bg-current before:top-0 before:left-0 before:w-full before:h-full before:opacity-10"
>
<div
className="w-full h-full relative"
>
<FilePreviewComponent
file={file}
enhanced={clientSide}
disabled={disabled}
/>
</div>
</div>
</div>
))}
</div>
<div
data-testid="actions"
className="w-full text-center h-8 flex"
>
<div className="w-0 flex-auto flex flex-col items-center justify-center h-full">
<FileSelectBoxActionElementComponent
type="button"
data-testid="reselect"
disabled={disabled}
onMouseDown={handleReselectMouseDown}
onMouseUp={handleReselectMouseUp}
tabIndex={-1}
className={clsx(
'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,
'text-tertiary': aboutToSelect,
},
)}
>
<span
className="block uppercase font-bold h-[1.1em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded"
>
className="block uppercase font-bold h-[1.1em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded"
>
{reselectLabel}
</span>
</FileSelectBoxActionElementComponent>
</div>
<div className="w-0 flex-auto flex flex-col items-center justify-center h-full">
<FileSelectBoxActionElementComponent
disabled={disabled}
data-testid="clear"
type="button"
name="action"
value="clear"
onMouseDown={handleDeleteMouseDown}
onMouseUp={doSetFileList}
tabIndex={-1}
className={clsx(
'flex w-full h-full bg-negative text-primary disabled:text-primary items-center justify-center leading-none gap-4 select-none focus:outline-0',
{
'group-focus-within:text-secondary group-focus-within:active:text-tertiary': !aboutToClear,
'text-tertiary': aboutToClear,
},
)}
>
</FileSelectBoxActionElementComponent>
</div>
<div className="w-0 flex-auto flex flex-col items-center justify-center h-full">
<FileSelectBoxActionElementComponent
disabled={disabled}
data-testid="clear"
type="button"
name="action"
value="clear"
onMouseDown={handleDeleteMouseDown}
onMouseUp={doSetFileList}
tabIndex={-1}
className={clsx(
'flex w-full h-full bg-negative text-primary disabled:text-primary items-center justify-center leading-none gap-4 select-none focus:outline-0',
{
'group-focus-within:text-secondary group-focus-within:active:text-tertiary': !aboutToClear,
'text-tertiary': aboutToClear,
},
)}
>
<span
className="block uppercase font-bold h-[1.1em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded"
>
className="block uppercase font-bold h-[1.1em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded"
>
{clearLabel}
</span>
</FileSelectBoxActionElementComponent>
</div>
</div>
</React.Fragment>
)}
{border && (
<span
data-testid="border"
className="absolute z-[1] peer-disabled:opacity-50 inset-0 rounded-inherit border-2 border-primary pointer-events-none group-focus-within:border-secondary"
/>
)}
</FileSelectBoxActionElementComponent>
</div>
</div>
</div>
)}
{border && (
<span
data-testid="border"
className="absolute z-[1] peer-disabled:opacity-50 inset-0 rounded-inherit border-2 border-primary pointer-events-none group-focus-within:border-secondary"
/>
)}
</div>
</FileSelectBoxRootElementComponent>
);
});
@@ -599,6 +616,7 @@ FileSelectBox.defaultProps = {
previewComponent: FileSelectBoxDefaultPreviewComponent,
reselectLabel: 'Reselect' as const,
clearLabel: 'Clear' as const,
resize: false as const,
};

FileSelectBoxDefaultPreviewComponent.defaultProps = {


+ 1
- 1
storybook/react/package.json View File

@@ -4,7 +4,7 @@
"description": "",
"main": "index.js",
"scripts": {
"start": "storybook dev -p 6006",
"dev": "storybook dev -p 6006 --no-open",
"build": "storybook build"
},
"keywords": [],


+ 1
- 0
storybook/react/tailwind.config.ts View File

@@ -92,6 +92,7 @@ const config: Config = {
10: '2.5rem',
12: '3rem',
16: '4rem',
32: '8rem',
64: '16rem',
},
strokeWidth: {


Loading…
Cancel
Save