mirror of
https://github.com/aljazceru/landscape-template.git
synced 2026-01-28 18:54:35 +01:00
feat: file drag input component
This commit is contained in:
17
package-lock.json
generated
17
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
60
src/Components/Inputs/FilesInput/DropInput.jsx
Normal file
60
src/Components/Inputs/FilesInput/DropInput.jsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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: () => { },
|
||||
}
|
||||
84
src/Components/Inputs/FilesInput/FilesDropInput.tsx
Normal file
84
src/Components/Inputs/FilesInput/FilesDropInput.tsx
Normal 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>
|
||||
);
|
||||
@@ -12,4 +12,5 @@ export interface Comment {
|
||||
|
||||
export interface CommentWithReplies extends Comment {
|
||||
replies: CommentWithReplies[]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user