💥(react) upgrade to React 19
https://react.dev/blog/2024/04/25/react-19-upgrade-guide https://react.dev/blog/2024/12/05/react-19
This commit is contained in:
committed by
Jean-Baptiste PENRATH
parent
0f6a8dfa72
commit
56d9ed88f0
@@ -1,6 +1,6 @@
|
||||
import React, {
|
||||
forwardRef,
|
||||
PropsWithChildren,
|
||||
RefAttributes,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useRef,
|
||||
@@ -15,147 +15,141 @@ import {
|
||||
FileUploaderRefType,
|
||||
} from ":/components/Forms/FileUploader/index";
|
||||
|
||||
interface DropZoneProps extends FileUploaderProps, PropsWithChildren {
|
||||
files: File[];
|
||||
}
|
||||
type DropZoneProps = FileUploaderProps &
|
||||
RefAttributes<FileUploaderRefType> &
|
||||
PropsWithChildren<{
|
||||
files: File[];
|
||||
}>;
|
||||
|
||||
export const DropZone = forwardRef<FileUploaderRefType, DropZoneProps>(
|
||||
(
|
||||
{
|
||||
multiple,
|
||||
name,
|
||||
state,
|
||||
icon,
|
||||
animateIcon,
|
||||
successIcon,
|
||||
uploadingIcon,
|
||||
text,
|
||||
bigText,
|
||||
files,
|
||||
onFilesChange,
|
||||
children,
|
||||
...props
|
||||
}: DropZoneProps,
|
||||
ref,
|
||||
) => {
|
||||
const [dragActive, setDragActive] = useState(false);
|
||||
const container = useRef<HTMLLabelElement>(null);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const { t } = useCunningham();
|
||||
export const DropZone = ({
|
||||
multiple,
|
||||
name,
|
||||
state,
|
||||
icon,
|
||||
animateIcon,
|
||||
successIcon,
|
||||
uploadingIcon,
|
||||
text,
|
||||
bigText,
|
||||
files,
|
||||
onFilesChange,
|
||||
children,
|
||||
ref,
|
||||
...props
|
||||
}: DropZoneProps) => {
|
||||
const [dragActive, setDragActive] = useState(false);
|
||||
const container = useRef<HTMLLabelElement>(null);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const { t } = useCunningham();
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
get input() {
|
||||
return inputRef.current;
|
||||
},
|
||||
reset() {
|
||||
onFilesChange?.({ target: { value: [] } });
|
||||
},
|
||||
}));
|
||||
useImperativeHandle(ref, () => ({
|
||||
get input() {
|
||||
return inputRef.current;
|
||||
},
|
||||
reset() {
|
||||
onFilesChange?.({ target: { value: [] } });
|
||||
},
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
if (!inputRef.current) {
|
||||
return;
|
||||
}
|
||||
replaceInputFilters(inputRef.current, files);
|
||||
}, [files]);
|
||||
useEffect(() => {
|
||||
if (!inputRef.current) {
|
||||
return;
|
||||
}
|
||||
replaceInputFilters(inputRef.current, files);
|
||||
}, [files]);
|
||||
|
||||
useEffect(() => {
|
||||
onFilesChange?.({ target: { value: files ?? [] } });
|
||||
}, [files]);
|
||||
useEffect(() => {
|
||||
onFilesChange?.({ target: { value: files ?? [] } });
|
||||
}, [files]);
|
||||
|
||||
const renderIcon = () => {
|
||||
if (state === "success") {
|
||||
return successIcon ?? <span className="material-icons">done</span>;
|
||||
}
|
||||
if (state === "uploading") {
|
||||
return React.cloneElement(uploadingIcon ?? <Loader size="small" />, {
|
||||
"aria-label": t("components.forms.file_uploader.uploading"),
|
||||
});
|
||||
}
|
||||
return icon ?? <span className="material-icons">upload</span>;
|
||||
};
|
||||
|
||||
const renderCaption = () => {
|
||||
if (state === "uploading") {
|
||||
return t("components.forms.file_uploader.uploading");
|
||||
}
|
||||
if (bigText) {
|
||||
return bigText;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{t("components.forms.file_uploader.caption")}
|
||||
<span>{t("components.forms.file_uploader.browse_files")}</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
const renderIcon = () => {
|
||||
if (state === "success") {
|
||||
return successIcon ?? <span className="material-icons">done</span>;
|
||||
}
|
||||
if (state === "uploading") {
|
||||
return React.cloneElement(uploadingIcon ?? <Loader size="small" />, {
|
||||
"aria-label": t("components.forms.file_uploader.uploading"),
|
||||
});
|
||||
}
|
||||
return icon ?? <span className="material-icons">upload</span>;
|
||||
};
|
||||
|
||||
const renderCaption = () => {
|
||||
if (state === "uploading") {
|
||||
return t("components.forms.file_uploader.uploading");
|
||||
}
|
||||
if (bigText) {
|
||||
return bigText;
|
||||
}
|
||||
return (
|
||||
<label
|
||||
className={classNames(
|
||||
"c__file-uploader",
|
||||
"c__file-uploader--" + state,
|
||||
{
|
||||
"c__file-uploader--active": dragActive,
|
||||
"c__file-uploader--animate-icon": animateIcon,
|
||||
},
|
||||
)}
|
||||
onDragEnter={() => {
|
||||
setDragActive(true);
|
||||
}}
|
||||
onDragLeave={(e) => {
|
||||
/**
|
||||
* This condition is important because onDragLeave is called when the cursor goes over
|
||||
* a child of the current node, which is not intuitive. So here we need to make sure that
|
||||
* the relatedTarget is not a child of the current node.
|
||||
*/
|
||||
if (!container.current!.contains(e.relatedTarget as Node)) {
|
||||
setDragActive(false);
|
||||
}
|
||||
}}
|
||||
onDragOver={(e) => {
|
||||
e.preventDefault();
|
||||
}}
|
||||
onDrop={(e) => {
|
||||
// To prevent a new tab to open.
|
||||
e.preventDefault();
|
||||
const newFiles = Array.from(e.dataTransfer.files);
|
||||
if (inputRef.current) {
|
||||
inputRef.current.files = e.dataTransfer.files;
|
||||
onFilesChange?.({ target: { value: [...newFiles] } });
|
||||
}
|
||||
setDragActive(false);
|
||||
}}
|
||||
ref={container}
|
||||
>
|
||||
<div className="c__file-uploader__inner">
|
||||
<div className="c__file-uploader__inner__icon">{renderIcon()}</div>
|
||||
{children ?? (
|
||||
<>
|
||||
<div className="c__file-uploader__inner__caption">
|
||||
{renderCaption()}
|
||||
</div>
|
||||
{text && (
|
||||
<div className="c__file-uploader__inner__text">{text}</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<input
|
||||
type="file"
|
||||
name={name}
|
||||
ref={inputRef}
|
||||
onChange={(e) => {
|
||||
if (e.target.files) {
|
||||
onFilesChange?.({ target: { value: [...e.target.files] } });
|
||||
} else {
|
||||
onFilesChange?.({ target: { value: [] } });
|
||||
}
|
||||
}}
|
||||
multiple={multiple}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
<>
|
||||
{t("components.forms.file_uploader.caption")}
|
||||
<span>{t("components.forms.file_uploader.browse_files")}</span>
|
||||
</>
|
||||
);
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<label
|
||||
className={classNames("c__file-uploader", "c__file-uploader--" + state, {
|
||||
"c__file-uploader--active": dragActive,
|
||||
"c__file-uploader--animate-icon": animateIcon,
|
||||
})}
|
||||
onDragEnter={() => {
|
||||
setDragActive(true);
|
||||
}}
|
||||
onDragLeave={(e) => {
|
||||
/**
|
||||
* This condition is important because onDragLeave is called when the cursor goes over
|
||||
* a child of the current node, which is not intuitive. So here we need to make sure that
|
||||
* the relatedTarget is not a child of the current node.
|
||||
*/
|
||||
if (!container.current!.contains(e.relatedTarget as Node)) {
|
||||
setDragActive(false);
|
||||
}
|
||||
}}
|
||||
onDragOver={(e) => {
|
||||
e.preventDefault();
|
||||
}}
|
||||
onDrop={(e) => {
|
||||
// To prevent a new tab to open.
|
||||
e.preventDefault();
|
||||
const newFiles = Array.from(e.dataTransfer.files);
|
||||
if (inputRef.current) {
|
||||
inputRef.current.files = e.dataTransfer.files;
|
||||
onFilesChange?.({ target: { value: [...newFiles] } });
|
||||
}
|
||||
setDragActive(false);
|
||||
}}
|
||||
ref={container}
|
||||
>
|
||||
<div className="c__file-uploader__inner">
|
||||
<div className="c__file-uploader__inner__icon">{renderIcon()}</div>
|
||||
{children ?? (
|
||||
<>
|
||||
<div className="c__file-uploader__inner__caption">
|
||||
{renderCaption()}
|
||||
</div>
|
||||
{text && (
|
||||
<div className="c__file-uploader__inner__text">{text}</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<input
|
||||
type="file"
|
||||
name={name}
|
||||
ref={inputRef}
|
||||
onChange={(e) => {
|
||||
if (e.target.files) {
|
||||
onFilesChange?.({ target: { value: [...e.target.files] } });
|
||||
} else {
|
||||
onFilesChange?.({ target: { value: [] } });
|
||||
}
|
||||
}}
|
||||
multiple={multiple}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import React, { forwardRef, useEffect, useMemo, useState } from "react";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { useCunningham } from ":/components/Provider";
|
||||
import { Button } from ":/components/Button";
|
||||
import {
|
||||
FileUploaderProps,
|
||||
FileUploaderRefType,
|
||||
} from ":/components/Forms/FileUploader/index";
|
||||
import { FileUploaderProps } from ":/components/Forms/FileUploader/index";
|
||||
import { DropZone } from ":/components/Forms/FileUploader/DropZone";
|
||||
|
||||
export const FileUploaderMono = forwardRef<
|
||||
FileUploaderRefType,
|
||||
FileUploaderProps
|
||||
>(({ fakeDefaultFiles, ...props }, ref) => {
|
||||
export const FileUploaderMono = ({
|
||||
fakeDefaultFiles,
|
||||
ref,
|
||||
...props
|
||||
}: FileUploaderProps) => {
|
||||
const { t } = useCunningham();
|
||||
const [file, setFile] = useState<File | undefined>(
|
||||
fakeDefaultFiles && fakeDefaultFiles.length > 0
|
||||
@@ -91,4 +89,4 @@ export const FileUploaderMono = forwardRef<
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import React, { forwardRef, useEffect, useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useCunningham } from ":/components/Provider";
|
||||
import { formatBytes } from ":/components/Forms/FileUploader/utils";
|
||||
import { Button } from ":/components/Button";
|
||||
import {
|
||||
FileUploaderProps,
|
||||
FileUploaderRefType,
|
||||
} from ":/components/Forms/FileUploader/index";
|
||||
import { FileUploaderProps } from ":/components/Forms/FileUploader/index";
|
||||
import { DropZone } from ":/components/Forms/FileUploader/DropZone";
|
||||
|
||||
export const FileUploaderMulti = forwardRef<
|
||||
FileUploaderRefType,
|
||||
FileUploaderProps
|
||||
>(({ fullWidth, fakeDefaultFiles, ...props }, ref) => {
|
||||
export const FileUploaderMulti = ({
|
||||
fullWidth,
|
||||
fakeDefaultFiles,
|
||||
ref,
|
||||
...props
|
||||
}: FileUploaderProps) => {
|
||||
const { t } = useCunningham();
|
||||
const [files, setFiles] = useState<File[]>(fakeDefaultFiles || []);
|
||||
|
||||
@@ -59,4 +58,4 @@ export const FileUploaderMulti = forwardRef<
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import React, { forwardRef, InputHTMLAttributes, ReactElement } from "react";
|
||||
import React, { InputHTMLAttributes, ReactElement, RefAttributes } from "react";
|
||||
import { Field, FieldProps, FieldState } from ":/components/Forms/Field";
|
||||
import { FileUploaderMulti } from ":/components/Forms/FileUploader/FileUploaderMulti";
|
||||
import { FileUploaderMono } from ":/components/Forms/FileUploader/FileUploaderMono";
|
||||
|
||||
export interface FileUploaderProps
|
||||
extends Omit<FieldProps, "state">,
|
||||
InputHTMLAttributes<HTMLInputElement> {
|
||||
InputHTMLAttributes<HTMLInputElement>,
|
||||
RefAttributes<FileUploaderRefType> {
|
||||
state?: FieldState | "uploading" | undefined;
|
||||
multiple?: boolean;
|
||||
icon?: ReactElement;
|
||||
@@ -25,16 +26,18 @@ export interface FileUploaderRefType {
|
||||
reset: () => void;
|
||||
}
|
||||
|
||||
export const FileUploader = forwardRef<FileUploaderRefType, FileUploaderProps>(
|
||||
({ fullWidth, ...props }, ref) => {
|
||||
return (
|
||||
<Field fullWidth={fullWidth} className={props.className}>
|
||||
{props.multiple ? (
|
||||
<FileUploaderMulti {...props} ref={ref} />
|
||||
) : (
|
||||
<FileUploaderMono {...props} ref={ref} />
|
||||
)}
|
||||
</Field>
|
||||
);
|
||||
},
|
||||
);
|
||||
export const FileUploader = ({
|
||||
fullWidth,
|
||||
ref,
|
||||
...props
|
||||
}: FileUploaderProps) => {
|
||||
return (
|
||||
<Field fullWidth={fullWidth} className={props.className}>
|
||||
{props.multiple ? (
|
||||
<FileUploaderMulti {...props} ref={ref} />
|
||||
) : (
|
||||
<FileUploaderMono {...props} ref={ref} />
|
||||
)}
|
||||
</Field>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user