feat: file drag input component

This commit is contained in:
MTG2000
2022-05-11 18:35:53 +03:00
parent ca8eec9605
commit 36790cab5e
6 changed files with 175 additions and 5 deletions

17
package-lock.json generated
View File

@@ -48,6 +48,7 @@
"react-copy-to-clipboard": "^5.1.0",
"react-datepicker": "^4.7.0",
"react-dom": "^18.0.0",
"react-file-drop": "^3.1.4",
"react-hook-form": "^7.30.0",
"react-icons": "^4.3.1",
"react-loader-spinner": "^6.0.0-0",
@@ -58899,6 +58900,14 @@
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
"integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA=="
},
"node_modules/react-file-drop": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/react-file-drop/-/react-file-drop-3.1.4.tgz",
"integrity": "sha512-83633H9CK/IoBXBl77qBS36K0pGb5sQn6c1rc54nxWYkAbM/zo+xuxFnwUDib4Yh+xVmWBZeTAnY5ZSN2PmUCQ==",
"dependencies": {
"prop-types": "^15.7.2"
}
},
"node_modules/react-helmet-async": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz",
@@ -111144,6 +111153,14 @@
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
"integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA=="
},
"react-file-drop": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/react-file-drop/-/react-file-drop-3.1.4.tgz",
"integrity": "sha512-83633H9CK/IoBXBl77qBS36K0pGb5sQn6c1rc54nxWYkAbM/zo+xuxFnwUDib4Yh+xVmWBZeTAnY5ZSN2PmUCQ==",
"requires": {
"prop-types": "^15.7.2"
}
},
"react-helmet-async": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz",

View File

@@ -43,6 +43,7 @@
"react-copy-to-clipboard": "^5.1.0",
"react-datepicker": "^4.7.0",
"react-dom": "^18.0.0",
"react-file-drop": "^3.1.4",
"react-hook-form": "^7.30.0",
"react-icons": "^4.3.1",
"react-loader-spinner": "^6.0.0-0",

View File

@@ -0,0 +1,60 @@
import { useToggle } from "@react-hookz/web";
import React from "react";
import { FileDrop } from "react-file-drop";
export default function DropInput({
value: files,
onChange,
emptyContent,
draggingContent,
hasFilesContent,
height,
multiple = false,
allowedType = "*",
classes = {},
}) {
const [isDragging, toggleDrag] = useToggle(false);
const fileInputRef = React.useRef(null);
const onAddFiles = (_files) => {
onChange(_files);
// do something with your files...
};
const uploadClick = () => {
fileInputRef.current.click();
};
const status = isDragging ? "dragging" : files ? "has-files" : "empty";
return (
<div
style={{
height: height + "px",
}}
>
<FileDrop
onDrop={(files) => onAddFiles(files)}
onTargetClick={uploadClick}
onFrameDragEnter={() => toggleDrag(true)}
onFrameDragLeave={() => toggleDrag(false)}
onFrameDrop={() => toggleDrag(false)}
className={`h-full cursor-pointer`}
targetClassName={`h-full ${classes.wrapper}`}
draggingOverFrameClassName={`${classes.dragging}`}
>
{status === "dragging" && draggingContent}
{status === "empty" && emptyContent}
{status === "has-files" && hasFilesContent}
</FileDrop>
<input
onChange={(e) => onAddFiles(e.target.files)}
ref={fileInputRef}
type="file"
className="hidden"
multiple={multiple}
accept={allowedType}
/>
</div>
);
}

View File

@@ -3,6 +3,7 @@ import { BsImages } from 'react-icons/bs';
import Button from 'src/Components/Button/Button';
import FilesInput from './FilesInput';
import FileDropInput from './FilesDropInput';
export default {
title: 'Shared/Files Input',
@@ -13,12 +14,18 @@ export default {
const Template: ComponentStory<typeof FilesInput> = (args) => <FilesInput {...args} />
export const Default = Template.bind({});
Default.args = {
export const DefaultButton = Template.bind({});
DefaultButton.args = {
}
export const CustomButton = Template.bind({});
CustomButton.args = {
export const CustomizedButton = Template.bind({});
CustomizedButton.args = {
multiple: true,
uploadBtn: <Button color='primary'><span className="align-middle">Drop Images</span> <BsImages className='ml-12 scale-125' /></Button>
}
const DropTemplate: ComponentStory<typeof FileDropInput> = (args) => <div className="max-w-[500px]"><FileDropInput {...args as any} /></div>
export const DropZoneInput = DropTemplate.bind({});
DropZoneInput.args = {
onChange: () => { },
}

View File

@@ -0,0 +1,84 @@
import { FaImage } from "react-icons/fa";
import { UnionToObjectKeys } from "src/utils/types/utils";
import DropInput from "./DropInput";
type Props = {
height?: number
multiple?: boolean;
value?: File[] | string[] | string;
max?: number;
onBlur?: () => void;
onChange?: (files: (File | string)[] | null) => void
uploadBtn?: JSX.Element
uploadText?: string;
allowedType?: 'images';
classes?: Partial<{
wrapper: string,
dragging: string
}>
}
const fileAccept: UnionToObjectKeys<Props, 'allowedType'> = {
images: ".png, .jpg, .jpeg"
} as const;
const fileUrlToObject = async (url: string, fileName: string = 'filename') => {
const res = await fetch(url);
const contentType = res.headers.get('content-type') as string;
const blob = await res.blob()
const file = new File([blob], fileName, { contentType } as any)
return file
}
export default function FilesInput({
height = 200,
multiple,
value,
max = 3,
onBlur,
onChange,
allowedType = 'images',
classes,
...props
}: Props) {
const wrapperClasses = classes?.wrapper ?? 'bg-primary-50 p-32 border border-primary-500 rounded-8 text-center flex flex-col justify-center items-center'
const draggingClasses = classes?.dragging ?? '!bg-primary-500 !text-white'
return (
<DropInput
height={height}
emptyContent={defaultEmptyContent}
draggingContent={defaultDraggingContent}
hasFilesContent={defaultHasFilesContent}
value={value}
onChange={onChange}
multiple={multiple}
allowedType={fileAccept[allowedType]}
classes={{
wrapper: wrapperClasses,
dragging: draggingClasses
}}
/>
)
}
const defaultEmptyContent = (
<>
<div>
<FaImage className="scale-150 mr-8 text-gray-400" />{" "}
<span className="align-middle">Drop your files here</span>
</div>
<p className="mt-4">
or <button className="hover:underline font-bold">Click to Upload</button>{" "}
</p>
</>
);
const defaultDraggingContent = <p className="font-bold text-body2">Drop your files here </p>;
const defaultHasFilesContent = (
<p className="font-bolder">Files Uploaded Successfully!!</p>
);

View File

@@ -12,4 +12,5 @@ export interface Comment {
export interface CommentWithReplies extends Comment {
replies: CommentWithReplies[]
}
}