diff --git a/bugs.md b/bugs.md index 86b72e3c19a29b763e68cab2580d01813cc953ac..1702ce601403e8214da874510587f79f78138114 100644 --- a/bugs.md +++ b/bugs.md @@ -4,21 +4,21 @@ - [SOLVED]on change progress the loader is reredered again and again, I think that the 3 states or layers must be there from the begining "preparing", "uploading", "result" - on cancel, the 2nd layer will dissapear, it will not be any big deal -- After uploading, progress must be reiitialized to 0 +- [SOLVED] After uploading, progress must be reiitialized to 0 - [SOLVED] FileItemMainLayer WORKS STRANGE AT THE TIME NEW fILEiTEM IS ADDED ## Dropzone -- When no validation, upload removes all files. +- [SOLVED] When no validation, upload removes all files. - [SOLVED]: action buttons rendered again and again since they were created again every time "value" prop changed - [SOLVED]: Uploading works in 2 times (first time stops after setting progress = UPLOADING.progrss), but fails to recover from the manager. [UPDATED]: Problem is at `useDropzoneFileListUpdater.ts` file. The problem is that hook for updating when user wants to interrupt preparing, is called at the begining of the upload process, with value of undefined in all files. It is probably the last update on localFiles outside Dropzone component. -- When file is set from preparing to undefined it can be deleted, however, will appear again if onDelete is called. It would be great to add a reconciliation procedure to support different array sizes in updater hook. Or "canceled" upload status could be added and file Item should not show the "X" button when uploading and canceled. After Upload process, all files with "canceled" upload status should be set to "undefined" again. This can be a workaround. +- [DONE] When file is set from preparing to undefined it can be deleted, however, will appear again if onDelete is called. It would be great to add a reconciliation procedure to support different array sizes in updater hook. Or "canceled" upload status could be added and file Item should not show the "X" button when uploading and canceled. After Upload process, all files with "canceled" upload status should be set to "undefined" again. This can be a workaround. (`Using the extraData for keeping track if a Filemosaic was deleted`) -- [SOLVED] Dropzpne is afected by adding more files as children +- [SOLVED] Dropzone is afected by adding more files as children - offset should not be used, instead a padding, given header or footer -- When layer is visible, border in the root container must dissapear +- [SOLVED] When layer is visible, border in the root container must dissapear ## Tooltip diff --git a/features.md b/features.md index 3247d94e5ac452329328d46fb251a3390c6dd4e1..323db2a719516e05a0f03a81d0de32eb6dc17890 100644 --- a/features.md +++ b/features.md @@ -14,6 +14,7 @@ - Validate with accept prop - Validate with maxFileSize prop - Validate with maxFiles prop +- custom validation ## Read @@ -31,6 +32,6 @@ - ImagePreview - VideoPreview - PDFPreview -- JsonPreview fix rc-highlight -- FullScreenPreview +- JsonPreview [DONE]fix rc-highlight +- FullScreenPreview => FullScreen - DropLayer diff --git a/future-features.md b/future-features.md index 4e210cb322c854c32180f820ef18f8ff57f84eff..84361025cd4bc841a7dd12c0da7c7616f118a21a 100644 --- a/future-features.md +++ b/future-features.md @@ -1,12 +1,17 @@ # Future Possible Features +## DROP + +WHen data transfer only text or iimage in string, event +files dissapear in "replace". create a text file in memoory + ## UTILS - custom icons - menu icon for FileItem ( FileOptions (menu collapsed from click in option icon) ) - [DONE]: Outside actions or buttons for Dropzone -- [WIP]:header and footer custom props -- FileItem: checkbox support +- [DONE]:header and footer custom props +- [NOT_YET]: FileItem: checkbox support - [DONE] FileItem: ( smart prop ) detect when width of image is greater than height or viceversa in order to decide the orientation ## Upload diff --git a/ideas.md b/ideas.md index c001002bfddcbad888c35291aa9c6e926e202576..df16a14c4c864045a820295c4d7c14139e55120e 100644 --- a/ideas.md +++ b/ideas.md @@ -8,11 +8,12 @@ ## Phrases -- Stop pain with developing a complex widget, don't need to create a file upload component from scratch -- If you need to do it from scratch, there is an example [show some basic code] Bellow main page component +- Stop pain with developing a complex widget, don't need to create a file upload component from scratch [landingpage_section_3] +- If you need to do it from scratch, there is an example [show some basic code] Bellow main page component[landing_sec_3_pat_2] ## MIgrating from dropzone-ui +[landing_Sec_3_or_4] ## Migrating from react-dropzone diff --git a/src/components/demo-components/desc-paragraph/DescParagraphProps.ts b/src/components/demo-components/desc-paragraph/DescParagraphProps.ts index 7fddad83368aa407606f4b667e4fa696faabd260..2475dd114cb547eb76d86edb6484b887ccc4702e 100644 --- a/src/components/demo-components/desc-paragraph/DescParagraphProps.ts +++ b/src/components/demo-components/desc-paragraph/DescParagraphProps.ts @@ -1,6 +1,6 @@ export type DescParagraphProps = { - content?:string; - children?:React.ReactNode; - margin?:string; - darkMode?:boolean; + content?: string; + children?: React.ReactNode; + margin?: string; + darkMode?: boolean; } \ No newline at end of file diff --git a/src/components/demo-components/dropzone-demo/AdvancedDropzoneDemo.jsx b/src/components/demo-components/dropzone-demo/AdvancedDropzoneDemo.jsx index b9e04b34e6dd754d88ddfe9eac25a9fd9501fb87..56b68a97943b22d0ebe78340bec964534d43f554 100644 --- a/src/components/demo-components/dropzone-demo/AdvancedDropzoneDemo.jsx +++ b/src/components/demo-components/dropzone-demo/AdvancedDropzoneDemo.jsx @@ -6,7 +6,6 @@ import { FullScreen, ImagePreview, VideoPreview, - } from "../../../files-ui"; import { useEffect, useState } from "react"; import axios from "axios"; @@ -20,13 +19,14 @@ export default function AdvancedDropzoneDemo() { const [videoSrc, setVideoSrc] = React.useState(undefined); const updateFiles = (incommingFiles) => { - console.log("incomming extFiles", incommingFiles); - const arrExtFIleInstances = incommingFiles.map( + console.log("incomming extFiles outside", incommingFiles.map(x=>x.uploadStatus)); + + const arrExtFIleInstances = incommingFiles.map( (extFile) => new ExtFileInstance(extFile) ); - + console.log("incomming arrExtFIleInstances", arrExtFIleInstances); - /* const listExtFileObjects = arrExtFIleInstances.map((extFileInstance) => + /* const listExtFileObjects = arrExtFIleInstances.map((extFileInstance) => extFileInstance.toExtFile() ); @@ -40,11 +40,12 @@ export default function AdvancedDropzoneDemo() { const handleSee = (imageSource) => { setImageSrc(imageSource); }; -/* const onClean = () => { - setExtFiles(extFiles.filter((ef) => ef.valid !== false)); - }; */ + + const handleStart = (res) => { + console.log("advanced demo start upload", res); + }; const handleFinish = (res) => { - console.log("finish", res); + console.log("advanced demo finish upload", res); }; const handleWatch = (videoSource) => { console.log( @@ -106,6 +107,7 @@ export default function AdvancedDropzoneDemo() { //url: "http://localhost:2800/file/28048465460", cleanOnUpload: true, }} + onUploadStart={handleStart} onUploadFinish={handleFinish} //fakeUpload actionButtons={{ diff --git a/src/components/demo-components/dropzone-demo/BasicDropzoneCodeJS.jsx b/src/components/demo-components/dropzone-demo/BasicDropzoneCodeJS.jsx index b2da92a5ff0372fd480735eb73b7d588b9c3f3c8..8de8378c08e8d7c284cc5714702c5db0d9fd2f4c 100644 --- a/src/components/demo-components/dropzone-demo/BasicDropzoneCodeJS.jsx +++ b/src/components/demo-components/dropzone-demo/BasicDropzoneCodeJS.jsx @@ -47,7 +47,6 @@ export default function BasicDemoDropzone() { }; return ( <Dropzone - style={{ minWidth: "505px" }} onChange={updateFiles} value={files} > @@ -74,7 +73,6 @@ export default function BasicDemoDropzone() { }; return ( <Dropzone - style={{ minWidth: "505px" }} onChange={updateFiles} value={files} > diff --git a/src/components/demo-components/dropzone-demo/BasicDropzoneDemo.jsx b/src/components/demo-components/dropzone-demo/BasicDropzoneDemo.jsx index b5398a405ee776a870bfee9a3c5f65cb0af84c03..29bd9d13c7c74843ce757b2c85f0a6f2f56bb05a 100644 --- a/src/components/demo-components/dropzone-demo/BasicDropzoneDemo.jsx +++ b/src/components/demo-components/dropzone-demo/BasicDropzoneDemo.jsx @@ -1,5 +1,4 @@ -import { Dropzone } from "../../../files-ui"; -import { FileMosaic } from "../../../files-ui/components/file-mosaic"; +import { Dropzone, FileMosaic } from "../../../files-ui"; import * as React from "react"; export default function BasicDemoDropzone() { const [files, setFiles] = React.useState([]); diff --git a/src/components/demo-components/dropzone-demo/CodeDemoDropzoneActionButtons.jsx b/src/components/demo-components/dropzone-demo/CodeDemoDropzoneActionButtons.jsx new file mode 100644 index 0000000000000000000000000000000000000000..4d2c8ee3da6ba9acc78d6f3326be367fcfa7350a --- /dev/null +++ b/src/components/demo-components/dropzone-demo/CodeDemoDropzoneActionButtons.jsx @@ -0,0 +1,125 @@ +import ShowDemoCode from "../../show-demo-code/ShowDemoCode"; +const CodeDemoDropzoneActionButtons = ({ splittedOnly = false }) => { + return ( + <ShowDemoCode + splittedOnly={splittedOnly} + codeCompleteJS={completeCodeJS} + codeCompleteTS={completeCodeTS} + codeSandboxJS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSandboxTS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSplittedJS={splittedCodeJS} + codeSplittedTS={splittedCodeTS} + /> + ); +}; +export default CodeDemoDropzoneActionButtons; + +const splittedCodeJS = `<Dropzone + onChange={updateFiles} + value={files} + autoClean + uploadConfig={{ url: "https://www.myawsomeserver.com/upload" }} + fakeUpload + actionButtons={{ + position: "bottom", + uploadButton: { style: { textTransform: "uppercase" } }, + abortButton: {}, + cleanButton: {}, + deleteButton: {}, + }} +> + {files.length > 0 && + files.map((file) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info/> + ))} +</Dropzone>`; +const splittedCodeTS = `<Dropzone + onChange={updateFiles} + value={files} + autoClean + uploadConfig={{ url: "https://www.myawsomeserver.com/upload" }} + fakeUpload + actionButtons={{ + position: "bottom", + uploadButton: { style: { textTransform: "uppercase" } }, + abortButton: {}, + cleanButton: {}, + deleteButton: {}, + }} +> + {files.length > 0 && + files.map((file: ExtFile) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info/> + ))} +</Dropzone>`; +const completeCodeJS = `import * as React from "react"; +import { Dropzone, FileMosaic } from "@files-ui/react"; + +export default function App() { + const [files, setFiles] = React.useState([]); + const updateFiles = (incommingFiles) => { + //do something with the files + setFiles(incommingFiles); + //even your own upload implementation + }; + const removeFile = (id) => { + setFiles(files.filter((x) => x.id !== id)); + }; + return ( + <Dropzone + onChange={updateFiles} + value={files} + autoClean + uploadConfig={{ url: "https://www.myawsomeserver.com/upload" }} + fakeUpload + actionButtons={{ + position: "bottom", + uploadButton: { style: { textTransform: "uppercase" } }, + abortButton: {}, + cleanButton: {}, + deleteButton: {}, + }} + > + {files.length > 0 && + files.map((file) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info /> + ))} + </Dropzone> + ); +};`; + +const completeCodeTS = `import * as React from "react"; +import { Dropzone, FileMosaic, ExtFile } from "@files-ui/react"; + +export default function App() { + const [files, setFiles] = React.useState<ExtFile[]>([]); + const updateFiles = (incommingFiles:ExtFile[]) => { + //do something with the files + setFiles(incommingFiles); + //even your own upload implementation + }; + const removeFile = (id: string | number | undefined) => { + setFiles(files.filter((x: ExtFile) => x.id !== id)); + }; + return ( + <Dropzone + onChange={updateFiles} + value={files} + autoClean + uploadConfig={{ url: "https://www.myawsomeserver.com/upload" }} + fakeUpload + actionButtons={{ + position: "bottom", + uploadButton: { style: { textTransform: "uppercase" } }, + abortButton: {}, + cleanButton: {}, + deleteButton: {}, + }} + > + {files.length > 0 && + files.map((file:ExtFile) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info /> + ))} + </Dropzone> + ); +}`; diff --git a/src/components/demo-components/dropzone-demo/CodeDemoDropzoneBehaviour.tsx b/src/components/demo-components/dropzone-demo/CodeDemoDropzoneBehaviour.tsx new file mode 100644 index 0000000000000000000000000000000000000000..c3dd1e8d03253cc7c1473f92ecc5678165936360 --- /dev/null +++ b/src/components/demo-components/dropzone-demo/CodeDemoDropzoneBehaviour.tsx @@ -0,0 +1,193 @@ +import ShowDemoCode from "../../show-demo-code/ShowDemoCode"; +const CodeDemoDropzoneBehaviour = ({ splittedOnly = false }) => { + return ( + <ShowDemoCode + splittedOnly={splittedOnly} + codeCompleteJS={completeCodeJS} + codeCompleteTS={completeCodeTS} + codeSandboxJS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSandboxTS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSplittedJS={splittedCodeJS} + codeSplittedTS={splittedCodeTS} + /> + ); +}; +export default CodeDemoDropzoneBehaviour; + +const splittedCodeJS = `<Dropzone + style={{ width: "min(100% , 420px)" }} + onChange={updateFilesAdd} + value={filesAdd} + label="Dropzone with behaviour=add" + behaviour={"add"} +> + {filesAdd.length > 0 && + filesAdd.map((file) => ( + <FileMosaic + key={file.id} + {...file} + onDelete={removeFileAdd} + info + preview + /> + ))} +</Dropzone> +<Dropzone + style={{ width: "min(100% , 420px)" }} + onChange={updateFilesReplace} + value={filesReplace} + label="Dropzone with behaviour=replace" + behaviour={"replace"} +> + {filesReplace.length > 0 && + filesReplace.map((file) => ( + <FileMosaic + key={file.id} + {...file} + onDelete={removeFileReplace} + info + preview + /> + ))} +</Dropzone>`; + +const splittedCodeTS = splittedCodeJS; +const completeCodeJS = `import * as React from "react"; +import { Dropzone, FileMosaic } from "@files-ui/react"; + +const DemoDropzoneBehaviour = () => { + const [filesAdd, setFilesAdd] = React.useState([]); + const [filesReplace, setFilesReplace] = React.useState([]); + + const updateFilesAdd = (incommingFiles) => { + setFilesAdd(incommingFiles); + }; + const updateFilesReplace = (incommingFiles) => { + setFilesReplace(incommingFiles); + }; + const removeFileAdd = (id) => { + setFilesAdd(filesAdd.filter((x) => x.id !== id)); + }; + const removeFileReplace = (id) => { + setFilesReplace(filesReplace.filter((x) => x.id !== id)); + }; + + return ( + <div + style={{ + display: "flex", + justifyContent: "space-evenly", + gap: "40px", + flexWrap: "wrap", + }} + > + <Dropzone + style={{ width: "min(100% , 420px)" }} + onChange={updateFilesAdd} + value={filesAdd} + label="Dropzone with behaviour=add" + behaviour={"add"} + > + {filesAdd.length > 0 && + filesAdd.map((file) => ( + <FileMosaic + key={file.id} + {...file} + onDelete={removeFileAdd} + info + preview + /> + ))} + </Dropzone> + <Dropzone + style={{ width: "min(100% , 420px)" }} + onChange={updateFilesReplace} + value={filesReplace} + label="Dropzone with behaviour=replace" + behaviour={"replace"} + > + {filesReplace.length > 0 && + filesReplace.map((file) => ( + <FileMosaic + key={file.id} + {...file} + onDelete={removeFileReplace} + info + preview + /> + ))} + </Dropzone> + </div> + ); + }; + export default DemoDropzoneBehaviour;`; + +const completeCodeTS = `import * as React from "react"; +import { Dropzone, ExtFile, FileMosaic, FileMosaicProps } from "@files-ui/react"; + +const DemoDropzoneBehaviour = () => { + const [filesAdd, setFilesAdd] = React.useState<ExtFile[]>([]); + const [filesReplace, setFilesReplace] = React.useState<ExtFile[]>([]); + + const updateFilesAdd = (incommingFiles:ExtFile[]) => { + setFilesAdd(incommingFiles); + }; + const updateFilesReplace = (incommingFiles:ExtFile[]) => { + setFilesReplace(incommingFiles); + }; + const removeFileAdd = (id:FileMosaicProps["id"]) => { + setFilesAdd(filesAdd.filter((x) => x.id !== id)); + }; + const removeFileReplace = (id:FileMosaicProps["id"]) => { + setFilesReplace(filesReplace.filter((x) => x.id !== id)); + }; + + return ( + <div + style={{ + display: "flex", + justifyContent: "space-evenly", + gap: "40px", + flexWrap: "wrap", + }} + > + <Dropzone + style={{ width: "min(100% , 420px)" }} + onChange={updateFilesAdd} + value={filesAdd} + label="Dropzone with behaviour=add" + behaviour={"add"} + > + {filesAdd.length > 0 && + filesAdd.map((file) => ( + <FileMosaic + key={file.id} + {...file} + onDelete={removeFileAdd} + info + preview + /> + ))} + </Dropzone> + <Dropzone + style={{ width: "min(100% , 420px)" }} + onChange={updateFilesReplace} + value={filesReplace} + label="Dropzone with behaviour=replace" + behaviour={"replace"} + > + {filesReplace.length > 0 && + filesReplace.map((file) => ( + <FileMosaic + key={file.id} + {...file} + onDelete={removeFileReplace} + info + preview + /> + ))} + </Dropzone> + </div> + ); + }; + export default DemoDropzoneBehaviour;`; diff --git a/src/components/demo-components/dropzone-demo/CodeDemoDropzoneClickable.tsx b/src/components/demo-components/dropzone-demo/CodeDemoDropzoneClickable.tsx new file mode 100644 index 0000000000000000000000000000000000000000..db3c823a30aa8f5cd3374f30dc6ad39f573e026d --- /dev/null +++ b/src/components/demo-components/dropzone-demo/CodeDemoDropzoneClickable.tsx @@ -0,0 +1,45 @@ +import ShowDemoCode from "../../show-demo-code/ShowDemoCode"; +const CodeDemoDropzoneClickable = ({ splittedOnly = false }) => { + return ( + <ShowDemoCode + splittedOnly={splittedOnly} + codeCompleteJS={completeCodeJS} + codeCompleteTS={completeCodeTS} + codeSandboxJS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSandboxTS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSplittedJS={splittedCodeJS} + codeSplittedTS={splittedCodeTS} + /> + ); +}; +export default CodeDemoDropzoneClickable; + +const splittedCodeJS = `<Dropzone style={{ width: "300px" }}>{/**Files */}</Dropzone> +<Dropzone style={{ width: "300px" }} clickable={false}> + {/**Files */} +</Dropzone>`; + +const splittedCodeTS = splittedCodeJS; +const completeCodeJS = `import * as React from "react"; +import { Dropzone } from "@files-ui/react"; + +const DemoDropzoneRipple = () => { + return ( + <div + style={{ + display: "flex", + justifyContent: "space-evenly", + gap: "40px", + flexWrap: "wrap", + }} + > + <Dropzone style={{ width: "300px" }}>{/**Files */}</Dropzone> + <Dropzone style={{ width: "300px" }} clickable={false}> + {/**Files */} + </Dropzone> + </div> + ); + }; + export default DemoDropzoneRipple;`; + +const completeCodeTS = completeCodeJS; diff --git a/src/components/demo-components/dropzone-demo/CodeDemoDropzoneCustomValidation.jsx b/src/components/demo-components/dropzone-demo/CodeDemoDropzoneCustomValidation.jsx new file mode 100644 index 0000000000000000000000000000000000000000..4f87e1cd073b6c9af9237f24e355b3ccf10142a6 --- /dev/null +++ b/src/components/demo-components/dropzone-demo/CodeDemoDropzoneCustomValidation.jsx @@ -0,0 +1,99 @@ +import ShowDemoCode from "../../show-demo-code/ShowDemoCode"; +const CodeDemoDropzoneCustomValidation = ({ splittedOnly = false }) => { + return ( + <ShowDemoCode + splittedOnly={splittedOnly} + codeCompleteJS={completeCodeJS} + codeCompleteTS={completeCodeTS} + codeSandboxJS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSandboxTS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSplittedJS={splittedCodeJS} + codeSplittedTS={splittedCodeTS} + /> + ); +}; +export default CodeDemoDropzoneCustomValidation; + +const splittedCodeJS = ``; +const splittedCodeTS = ``; +const completeCodeJS = `import { Dropzone, FileMosaic } from "@files-ui/react"; +import * as React from "react"; +//validate files +// file name must start with the following prefix: "test_file" +// (e.g. a valid file name could be "test_file_photo.png") +const myOwnValidation = (file) => { + let errorList= []; + let validResult = true; + const regExPrefix = /\\btest_file\\w+/; + if (!file.name.match(regExPrefix)) { + validResult = false; + errorList.push('Prefix "test_file" was not present in the file name'); + } + return { valid: validResult, errors: errorList }; +}; +export default function App() { + const [files, setFiles] = React.useState([]); + const updateFiles = (incommingFiles) => { + //do something with the files + setFiles(incommingFiles); + //even your own upload implementation + }; + const removeFile = (id) => { + setFiles(files.filter((x) => x.id !== id)); + }; + return ( + <Dropzone + onChange={updateFiles} + value={files} + cleanFiles + validator={myOwnValidation} + > + {files.length > 0 && + files.map((file) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info /> + ))} + </Dropzone> + ); +}`; + +const completeCodeTS = `import { Dropzone, FileMosaic, ExtFile, CustomValidateFileResponse } from "@files-ui/react"; +import * as React from "react"; + +//validate files +// file name must start with the following prefix: "test_file" +// (e.g. a valid file name could be "test_file_photo.png") +const myOwnValidation = (file: File): CustomValidateFileResponse => { + let errorList: string[] = []; + let validResult: boolean = true; + const regExPrefix: RegExp = /\btest_file\w+/; + if (!file.name.match(regExPrefix)) { + validResult = false; + errorList.push('Prefix "test_file" was not present in the file name'); + } + return { valid: validResult, errors: errorList }; +}; + +export default function App() { + const [files, setFiles] = React.useState<ExtFile[]>([]); + const updateFiles = (incommingFiles:ExtFile[]) => { + //do something with the files + setFiles(incommingFiles); + //even your own upload implementation + }; + const removeFile = (id: string | number | undefined) => { + setFiles(files.filter((x: ExtFile) => x.id !== id)); + }; + return ( + <Dropzone + onChange={updateFiles} + value={files} + cleanFiles + validator={myOwnValidation} + > + {files.length > 0 && + files.map((file:ExtFile) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info={true} /> + ))} + </Dropzone> + ); +}`; diff --git a/src/components/demo-components/dropzone-demo/CodeDemoDropzoneDisabled.tsx b/src/components/demo-components/dropzone-demo/CodeDemoDropzoneDisabled.tsx new file mode 100644 index 0000000000000000000000000000000000000000..49b3fc3ffc08447d32719c54c2f71e84867dc8d1 --- /dev/null +++ b/src/components/demo-components/dropzone-demo/CodeDemoDropzoneDisabled.tsx @@ -0,0 +1,47 @@ +import ShowDemoCode from "../../show-demo-code/ShowDemoCode"; +const CodeDemoDropzoneDisabled = ({ splittedOnly = false }) => { + return ( + <ShowDemoCode + splittedOnly={splittedOnly} + codeCompleteJS={completeCodeJS} + codeCompleteTS={completeCodeTS} + codeSandboxJS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSandboxTS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSplittedJS={splittedCodeJS} + codeSplittedTS={splittedCodeTS} + /> + ); +}; +export default CodeDemoDropzoneDisabled; + +const splittedCodeJS = `<Dropzone style={{ width: "300px" }}>{/**Files */}</Dropzone> +<Dropzone style={{ width: "300px" }} disabled> + {"Dropzone is disabled"} + {/**Files */} +</Dropzone>`; + +const splittedCodeTS = splittedCodeJS; +const completeCodeJS = `import * as React from "react"; +import { Dropzone } from "@files-ui/react"; + +const DemoDropzoneDisabled = () => { + return ( + <div + style={{ + display: "flex", + justifyContent: "space-evenly", + gap: "40px", + flexWrap: "wrap", + }} + > + <Dropzone style={{ width: "300px" }}>{/**Files */}</Dropzone> + <Dropzone style={{ width: "300px" }} disabled> + {"Dropzone is disabled"} + {/**Files */} + </Dropzone> + </div> + ); + }; + export default DemoDropzoneDisabled;`; + +const completeCodeTS = completeCodeJS; diff --git a/src/components/demo-components/dropzone-demo/CodeDemoDropzoneDropLayer.tsx b/src/components/demo-components/dropzone-demo/CodeDemoDropzoneDropLayer.tsx new file mode 100644 index 0000000000000000000000000000000000000000..1a9fff8892afba49bf04cc16b6d2651d83030a18 --- /dev/null +++ b/src/components/demo-components/dropzone-demo/CodeDemoDropzoneDropLayer.tsx @@ -0,0 +1,45 @@ +import ShowDemoCode from "../../show-demo-code/ShowDemoCode"; +const CodeDemoDropzoneDropLayer = ({ splittedOnly = false }) => { + return ( + <ShowDemoCode + splittedOnly={splittedOnly} + codeCompleteJS={completeCodeJS} + codeCompleteTS={completeCodeTS} + codeSandboxJS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSandboxTS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSplittedJS={splittedCodeJS} + codeSplittedTS={splittedCodeTS} + /> + ); +}; +export default CodeDemoDropzoneDropLayer; + +const splittedCodeJS = `<Dropzone style={{ width: "300px" }}>{/**Files */}</Dropzone> +<Dropzone style={{ width: "300px" }} dropOnLayer={false}> + {/**Files */} +</Dropzone>`; + +const splittedCodeTS = splittedCodeJS; +const completeCodeJS = `import * as React from "react"; +import { Dropzone } from "@files-ui/react"; + +const DemoDropzoneRipple = () => { + return ( + <div + style={{ + display: "flex", + justifyContent: "space-evenly", + gap: "40px", + flexWrap: "wrap", + }} + > + <Dropzone style={{ width: "300px" }}>{/**Files */}</Dropzone> + <Dropzone style={{ width: "300px" }} dropOnLayer={false}> + {/**Files */} + </Dropzone> + </div> + ); + }; + export default DemoDropzoneRipple;`; + +const completeCodeTS = completeCodeJS; diff --git a/src/components/demo-components/dropzone-demo/CodeDemoDropzoneFooterConfig.jsx b/src/components/demo-components/dropzone-demo/CodeDemoDropzoneFooterConfig.jsx new file mode 100644 index 0000000000000000000000000000000000000000..41275fa56707e2ed37950ee08aa5d14cc62c5ba6 --- /dev/null +++ b/src/components/demo-components/dropzone-demo/CodeDemoDropzoneFooterConfig.jsx @@ -0,0 +1,89 @@ +import ShowDemoCode from "../../show-demo-code/ShowDemoCode"; +const CodeDemoDropzoneFooterConfig = ({ splittedOnly = false }) => { + return ( + <ShowDemoCode + splittedOnly={splittedOnly} + codeCompleteJS={completeCodeJS} + codeCompleteTS={completeCodeTS} + codeSandboxJS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSandboxTS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSplittedJS={splittedCodeJS} + codeSplittedTS={splittedCodeTS} + /> + ); +}; +export default CodeDemoDropzoneFooterConfig; + +const splittedCodeJS = `<Dropzone + onChange={updateFiles} + value={files} + footerConfig={{ customMessage: "This is a custom message..." }} +> + {files.length > 0 && + files.map((file) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info/> + ))} +</Dropzone>`; +const splittedCodeTS = `<Dropzone + onChange={updateFiles} + value={files} + footerConfig={{ customMessage: "This is a custom message..." }} +> + {files.length > 0 && + files.map((file: ExtFile) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info/> + ))} +</Dropzone>`; +const completeCodeJS = `import * as React from "react"; +import { Dropzone, FileMosaic } from "@files-ui/react"; + +export default function App() { + const [files, setFiles] = React.useState([]); + const updateFiles = (incommingFiles) => { + //do something with the files + setFiles(incommingFiles); + //even your own upload implementation + }; + const removeFile = (id) => { + setFiles(files.filter((x) => x.id !== id)); + }; + return ( + <Dropzone + onChange={updateFiles} + value={files} + footerConfig={{ customMessage: "This is a custom message..." }} + > + {files.length > 0 && + files.map((file) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info /> + ))} + </Dropzone> + ); +};`; + +const completeCodeTS = `import * as React from "react"; +import { Dropzone, FileMosaic, ExtFile } from "@files-ui/react"; + +export default function App() { + const [files, setFiles] = React.useState<ExtFile[]>([]); + const updateFiles = (incommingFiles:ExtFile[]) => { + //do something with the files + setFiles(incommingFiles); + //even your own upload implementation + }; + const removeFile = (id: string | number | undefined) => { + setFiles(files.filter((x: ExtFile) => x.id !== id)); + }; + return ( + <Dropzone + onChange={updateFiles} + value={files} + footerConfig={{ customMessage: "This is a custom message..." }} + > + {files.length > 0 && + files.map((file:ExtFile) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info /> + ))} + </Dropzone> + ); +}`; diff --git a/src/components/demo-components/dropzone-demo/CodeDemoDropzoneHeaderConfig.jsx b/src/components/demo-components/dropzone-demo/CodeDemoDropzoneHeaderConfig.jsx new file mode 100644 index 0000000000000000000000000000000000000000..a65773083478e83b8e021cab045d93b68e3acb9f --- /dev/null +++ b/src/components/demo-components/dropzone-demo/CodeDemoDropzoneHeaderConfig.jsx @@ -0,0 +1,141 @@ +import ShowDemoCode from "../../show-demo-code/ShowDemoCode"; +const CodeDemoDropzoneHeaderConfig = ({ splittedOnly = false }) => { + return ( + <ShowDemoCode + splittedOnly={splittedOnly} + codeCompleteJS={completeCodeJS} + codeCompleteTS={completeCodeTS} + codeSandboxJS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSandboxTS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSplittedJS={splittedCodeJS} + codeSplittedTS={splittedCodeTS} + /> + ); +}; +export default CodeDemoDropzoneHeaderConfig; + +const splittedCodeJS = `<Dropzone + onChange={updateFiles} + value={files} + headerConfig={{ + customHeader: ( + <div style={{ display: "flex", justifyContent: "flex-end" }}> + <button + style={{ backgroundColor: "teal", color: "white" }} + onClick={removeAllFiles} + > + delete files + </button> + </div> + ), + }} +> + {files.length > 0 && + files.map((file) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info/> + ))} +</Dropzone>`; +const splittedCodeTS = `<Dropzone + onChange={updateFiles} + value={files} + headerConfig={{ + customHeader: ( + <div style={{ display: "flex", justifyContent: "flex-end" }}> + <button + style={{ backgroundColor: "teal", color: "white" }} + onClick={removeAllFiles} + > + delete files + </button> + </div> + ), + }} +> + {files.length > 0 && + files.map((file: ExtFile) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info/> + ))} +</Dropzone>`; +const completeCodeJS = `import * as React from "react"; +import { Dropzone, FileMosaic } from "@files-ui/react"; + +export default function App() { + const [files, setFiles] = React.useState([]); + const updateFiles = (incommingFiles) => { + //do something with the files + setFiles(incommingFiles); + //even your own upload implementation + }; + const removeFile = (id) => { + setFiles(files.filter((x) => x.id !== id)); + }; + const removeAllFiles = (evt) => { + evt.stopPropagation(); + setFiles([]); + }; + return ( + <Dropzone + onChange={updateFiles} + value={files} + headerConfig={{ + customHeader: ( + <div style={{ display: "flex", justifyContent: "flex-end" }}> + <button + style={{ backgroundColor: "teal", color: "white" }} + onClick={removeAllFiles} + > + delete files + </button> + </div> + ), + }} + > + {files.length > 0 && + files.map((file) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info /> + ))} + </Dropzone> + ); +};`; + +const completeCodeTS = `import * as React from "react"; +import { Dropzone, FileMosaic, ExtFile } from "@files-ui/react"; + +export default function App() { + const [files, setFiles] = React.useState<ExtFile[]>([]); + const updateFiles = (incommingFiles:ExtFile[]) => { + //do something with the files + setFiles(incommingFiles); + //even your own upload implementation + }; + const removeFile = (id: string | number | undefined) => { + setFiles(files.filter((x: ExtFile) => x.id !== id)); + }; + const removeAllFiles = (evt) => { + evt.stopPropagation(); + setFiles([]); + }; + return ( + <Dropzone + onChange={updateFiles} + value={files} + headerConfig={{ + customHeader: ( + <div style={{ display: "flex", justifyContent: "flex-end" }}> + <button + style={{ backgroundColor: "teal", color: "white" }} + onClick={removeAllFiles} + > + delete files + </button> + </div> + ), + }} + > + {files.length > 0 && + files.map((file:ExtFile) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info /> + ))} + </Dropzone> + ); +}`; diff --git a/src/components/demo-components/dropzone-demo/CodeDemoDropzoneRipple.jsx b/src/components/demo-components/dropzone-demo/CodeDemoDropzoneRipple.jsx new file mode 100644 index 0000000000000000000000000000000000000000..a464d5eba9bdc30aea4481a422f12d18b7238ac9 --- /dev/null +++ b/src/components/demo-components/dropzone-demo/CodeDemoDropzoneRipple.jsx @@ -0,0 +1,45 @@ +import ShowDemoCode from "../../show-demo-code/ShowDemoCode"; +const CodeDemoDropzoneRipple = ({ splittedOnly = false }) => { + return ( + <ShowDemoCode + splittedOnly={splittedOnly} + codeCompleteJS={completeCodeJS} + codeCompleteTS={completeCodeTS} + codeSandboxJS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSandboxTS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSplittedJS={splittedCodeJS} + codeSplittedTS={splittedCodeTS} + /> + ); +}; +export default CodeDemoDropzoneRipple; + +const splittedCodeJS = `<Dropzone style={{ width: "300px" }}>{"ripple enabled"}</Dropzone> +<Dropzone style={{ width: "300px" }} disableRipple> + {"ripple disabled"} +</Dropzone>`; + +const splittedCodeTS = splittedCodeJS; +const completeCodeJS = `import * as React from "react"; +import { Dropzone } from "@files-ui/react"; + +const DemoDropzoneRipple = () => { + return ( + <div + style={{ + display: "flex", + justifyContent: "space-evenly", + gap: "40px", + flexWrap: "wrap", + }} + > + <Dropzone style={{ width: "300px" }}>{"ripple enabled"}</Dropzone> + <Dropzone style={{ width: "300px" }} disableRipple> + {"ripple disabled"} + </Dropzone> + </div> + ); + }; + export default DemoDropzoneRipple;`; + +const completeCodeTS = completeCodeJS; diff --git a/src/components/demo-components/dropzone-demo/CodeDemoDropzoneUploading.jsx b/src/components/demo-components/dropzone-demo/CodeDemoDropzoneUploading.jsx new file mode 100644 index 0000000000000000000000000000000000000000..ce53cfa9e72b2220744e097348b8ff6c51c59a9f --- /dev/null +++ b/src/components/demo-components/dropzone-demo/CodeDemoDropzoneUploading.jsx @@ -0,0 +1,141 @@ +import ShowDemoCode from "../../show-demo-code/ShowDemoCode"; +const CodeDemoDropzoneUploading = ({ splittedOnly = false }) => { + return ( + <ShowDemoCode + splittedOnly={splittedOnly} + codeCompleteJS={completeCodeJS} + codeCompleteTS={completeCodeTS} + codeSandboxJS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSandboxTS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSplittedJS={splittedCodeJS} + codeSplittedTS={splittedCodeTS} + /> + ); +}; +export default CodeDemoDropzoneUploading; + +const splittedCodeJS = `<Dropzone + onChange={updateFiles} + value={files} + accept={"image/*"} + maxFileSize={28 * 1024*1024} + maxFiles={2} + actionButtons={{ position: "bottom", uploadButton: {}, abortButton: {} }} + uploadConfig={{ + url: "https://www.myawsomeserver.com/upload", + method: "POST", + headers: { + Authorization: + "bearer HTIBI/IBYG/&GU&/GV%&G/&IC%&V/Ibi76bfh8g67gg68g67i6g7G&58768&/(&/(FR&G/&H%&/", + }, + cleanOnUpload: true, + }} + fakeUpload +> + {files.length > 0 && + files.map((file) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info/> + ))} +</Dropzone>`; +const splittedCodeTS = `<Dropzone + onChange={updateFiles} + value={files} + accept={"image/*"} + maxFileSize={28 * 1024*1024} + maxFiles={2} + actionButtons={{ position: "bottom", uploadButton: {}, abortButton: {} }} + uploadConfig={{ + url: "https://www.myawsomeserver.com/upload", + method: "POST", + headers: { + Authorization: + "bearer HTIBI/IBYG/&GU&/GV%&G/&IC%&V/Ibi76bfh8g67gg68g67i6g7G&58768&/(&/(FR&G/&H%&/", + }, + cleanOnUpload: true, + }} + fakeUpload +> + {files.length > 0 && + files.map((file: ExtFile) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info/> + ))} +</Dropzone>`; +const completeCodeJS = `import { Dropzone,FileMosaic } from "@files-ui/react"; +import * as React from "react"; + +export default function App() { + const [files, setFiles] = React.useState([]); + const updateFiles = (incommingFiles) => { + //do something with the files + setFiles(incommingFiles); + //even your own upload implementation + }; + const removeFile = (id) => { + setFiles(files.filter((x) => x.id !== id)); + }; + return ( + <Dropzone + onChange={updateFiles} + value={files} + accept={"image/*"} + maxFileSize={28 * 1024*1024} + maxFiles={2} + actionButtons={{ position: "bottom", uploadButton: {}, abortButton: {} }} + uploadConfig={{ + url: "https://www.myawsomeserver.com/upload", + method: "POST", + headers: { + Authorization: + "bearer HTIBI/IBYG/&GU&/GV%&G/&IC%&V/Ibi76bfh8g67gg68g67i6g7G&58768&/(&/(FR&G/&H%&/", + }, + cleanOnUpload: true, + }} + fakeUpload + > + {files.length > 0 && + files.map((file) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info /> + ))} + </Dropzone> + ); +}`; + +const completeCodeTS = `import { Dropzone, FileMosaic, ExtFile } from "@files-ui/react"; +import * as React from "react"; + +export default function App() { + const [files, setFiles] = React.useState<ExtFile[]>([]); + const updateFiles = (incommingFiles:ExtFile[]) => { + //do something with the files + setFiles(incommingFiles); + //even your own upload implementation + }; + const removeFile = (id: string | number | undefined) => { + setFiles(files.filter((x: ExtFile) => x.id !== id)); + }; + return ( + <Dropzone + onChange={updateFiles} + value={files} + accept={"image/*"} + maxFileSize={28 * 1024*1024} + maxFiles={2} + actionButtons={{ position: "bottom", uploadButton: {}, abortButton: {} }} + uploadConfig={{ + url: "https://www.myawsomeserver.com/upload", + method: "POST", + headers: { + Authorization: + "bearer HTIBI/IBYG/&GU&/GV%&G/&IC%&V/Ibi76bfh8g67gg68g67i6g7G&58768&/(&/(FR&G/&H%&/", + }, + cleanOnUpload: true, + }} + fakeUpload + > + {files.length > 0 && + files.map((file:ExtFile) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info /> + ))} + </Dropzone> + ); +}`; diff --git a/src/components/demo-components/dropzone-demo/CodeDemoDropzoneValidation.jsx b/src/components/demo-components/dropzone-demo/CodeDemoDropzoneValidation.jsx new file mode 100644 index 0000000000000000000000000000000000000000..358fc77154953491638518771eb7cb994818880a --- /dev/null +++ b/src/components/demo-components/dropzone-demo/CodeDemoDropzoneValidation.jsx @@ -0,0 +1,105 @@ +import ShowDemoCode from "../../show-demo-code/ShowDemoCode"; +const CodeDemoDropzoneValidation = ({ splittedOnly = false }) => { + return ( + <ShowDemoCode + splittedOnly={splittedOnly} + codeCompleteJS={completeCodeJS} + codeCompleteTS={completeCodeTS} + codeSandboxJS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSandboxTS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSplittedJS={splittedCodeJS} + codeSplittedTS={splittedCodeTS} + /> + ); +}; +export default CodeDemoDropzoneValidation; + +const splittedCodeJS = `<Dropzone + onChange={updateFiles} + value={files} + accept="image/*" + maxFileSize={28 * 1024} + maxFiles={2} + //cleanFiles + actionButtons={{ position: "bottom", cleanButton: {} }} +> + {files.length > 0 && + files.map((file) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info/> + ))} +</Dropzone>`; +const splittedCodeTS = `<Dropzone + onChange={updateFiles} + value={files} + accept="image/*" + maxFileSize={28 * 1024} + maxFiles={2} + //cleanFiles + actionButtons={{ position: "bottom", cleanButton: {} }} +> + {files.length > 0 && + files.map((file: ExtFile) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info={true}/> + ))} +</Dropzone>`; +const completeCodeJS = `import { Dropzone,FileMosaic } from "@files-ui/react"; +import * as React from "react"; + +export default function App() { + const [files, setFiles] = React.useState([]); + const updateFiles = (incommingFiles) => { + //do something with the files + setFiles(incommingFiles); + //even your own upload implementation + }; + const removeFile = (id) => { + setFiles(files.filter((x) => x.id !== id)); + }; + return ( + <Dropzone + onChange={updateFiles} + value={files} + accept="image/*" + maxFileSize={28 * 1024} + maxFiles={2} + //cleanFiles + actionButtons={{ position: "bottom", cleanButton: {} }} + > + {files.length > 0 && + files.map((file) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info /> + ))} + </Dropzone> + ); +}`; + +const completeCodeTS = `import { Dropzone, FileMosaic, ExtFile } from "@files-ui/react"; +import * as React from "react"; + +export default function App() { + const [files, setFiles] = React.useState<ExtFile[]>([]); + const updateFiles = (incommingFiles:ExtFile[]) => { + //do something with the files + setFiles(incommingFiles); + //even your own upload implementation + }; + const removeFile = (id: string | number | undefined) => { + setFiles(files.filter((x: ExtFile) => x.id !== id)); + }; + return ( + <Dropzone + onChange={updateFiles} + value={files} + accept="image/*" + maxFileSize={28 * 1024} + maxFiles={2} + //cleanFiles + actionButtons={{ position: "bottom", cleanButton: {} }} + > + {files.length > 0 && + files.map((file:ExtFile) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info={true} /> + ))} + </Dropzone> + ); +}`; diff --git a/src/components/demo-components/dropzone-demo/CodeDropzoneDemoLabel.tsx b/src/components/demo-components/dropzone-demo/CodeDropzoneDemoLabel.tsx new file mode 100644 index 0000000000000000000000000000000000000000..3de75d3b21bede2731ff9d1509d0be88df6ef2a8 --- /dev/null +++ b/src/components/demo-components/dropzone-demo/CodeDropzoneDemoLabel.tsx @@ -0,0 +1,52 @@ +import ShowDemoCode from "../../show-demo-code/ShowDemoCode"; +const CodeDemoDropzoneLabel = ({ splittedOnly = false }) => { + return ( + <ShowDemoCode + splittedOnly={splittedOnly} + codeCompleteJS={completeCodeJS} + codeCompleteTS={completeCodeTS} + codeSandboxJS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSandboxTS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSplittedJS={splittedCodeJS} + codeSplittedTS={splittedCodeTS} + /> + ); +}; +export default CodeDemoDropzoneLabel; + + +const splittedCodeJS = `<Dropzone style={{ width: "300px" }}>{/**Files */}</Dropzone> +<Dropzone + style={{ width: "300px" }} + label={"Files ui â¤ï¸"} +> + {/**Files */} +</Dropzone>`; +const splittedCodeTS = splittedCodeJS; +const completeCodeJS = `import * as React from "react"; +import { Dropzone } from "@files-ui/react"; + +const DemoDropzoneLabel = () => { + return ( + <div + style={{ + display: "flex", + justifyContent: "space-evenly", + gap: "40px", + flexWrap: "wrap", + }} + > + <Dropzone style={{ width: "300px" }}>{/**Files */}</Dropzone> + <Dropzone + style={{ width: "300px" }} + label={"Files ui â¤ï¸"} + > + {/**Files */} + </Dropzone> + </div> + ); + }; + export default DemoDropzoneLabel;`; + +const completeCodeTS = completeCodeJS; + diff --git a/src/components/demo-components/dropzone-demo/CodeDropzoneDemoStyling.jsx b/src/components/demo-components/dropzone-demo/CodeDropzoneDemoStyling.jsx new file mode 100644 index 0000000000000000000000000000000000000000..5b25be4af4d7d04384236d00429959a67b9b2d4d --- /dev/null +++ b/src/components/demo-components/dropzone-demo/CodeDropzoneDemoStyling.jsx @@ -0,0 +1,130 @@ +import ShowDemoCode from "../../show-demo-code/ShowDemoCode"; +const CodeDemoDropzoneStyling = ({ splittedOnly = false }) => { + return ( + <ShowDemoCode + splittedOnly={splittedOnly} + codeCompleteJS={completeCodeJS} + codeCompleteTS={completeCodeTS} + codeSandboxJS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSandboxTS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSplittedJS={splittedCodeJS} + codeSplittedTS={splittedCodeTS} + /> + ); +}; +export default CodeDemoDropzoneStyling; + +const splittedCodeJS = `<Dropzone color="#6200EE">{/** files */}</Dropzone> +<Dropzone + minHeight="120px" + header={false} + footer={false} +> + {/** files */} +</Dropzone> +<Dropzone + background="radial-gradient(circle at 18.7% 37.8%, rgb(250, 250, 250) 0%, rgb(225, 234, 238) 90%);" +> + {/** files */} +</Dropzone>`; +const splittedCodeTS = `<Dropzone color="#6200EE">{/** files */}</Dropzone> +<Dropzone + minHeight="120px" + header={false} + footer={false} +> + {/** files */} +</Dropzone> +<Dropzone + background="radial-gradient(circle at 18.7% 37.8%, rgb(250, 250, 250) 0%, rgb(225, 234, 238) 90%);" +> + {/** files */} +</Dropzone>`; +const completeCodeJS = `import * as React from "react"; +import { Dropzone } from "@files-ui/react"; + +export default function App() { + return ( + <div + style={{ + display: "flex", + justifyContent: "space-evenly", + gap: "20px", + flexWrap: "wrap", + alignItems:"flex-start" + }} + > + <Dropzone + style={{ width: "300px" }} + onChange={updateFiles} + value={files} + color="#6200EE" + > + {/** Files*/} + </Dropzone> + <Dropzone + style={{ width: "300px" }} + minHeight="120px" + header={false} + footer={false} + > + {/** Files*/} + </Dropzone> + <Dropzone + style={{ width: "300px" }} + background="radial-gradient(circle at 18.7% 37.8%, rgb(250, 250, 250) 0%, rgb(225, 234, 238) 90%);" + > + {/** Files*/} + </Dropzone> + </div> + ); +};`; + +const completeCodeTS = `import * as React from "react"; +import { Dropzone, FileMosaic, ExtFile } from "@files-ui/react"; + +export default function App() { + const [files, setFiles] = React.useState<ExtFile[]>([]); + const updateFiles = (incommingFiles:ExtFile[]) => { + //do something with the files + setFiles(incommingFiles); + //even your own upload implementation + }; + const removeFile = (id: string | number | undefined) => { + setFiles(files.filter((x: ExtFile) => x.id !== id)); + }; + return ( + <div + style={{ + display: "flex", + justifyContent: "space-evenly", + gap: "20px", + flexWrap: "wrap", + alignItems:"flex-start" + }} + > + <Dropzone + style={{ width: "300px" }} + color="#6200EE" + > + {/** Files*/} + </Dropzone> + <Dropzone + style={{ width: "300px" }} + minHeight="120px" + header={false} + footer={false} + > + {/** Files*/} + </Dropzone> + <Dropzone + style={{ width: "300px" }} + onChange={updateFiles} + value={files} + background="radial-gradient(circle at 18.7% 37.8%, rgb(250, 250, 250) 0%, rgb(225, 234, 238) 90%);" + > + {/** Files*/} + </Dropzone> + </div> + ); +}`; diff --git a/src/components/demo-components/dropzone-demo/DemoDropzoneActionButtons.jsx b/src/components/demo-components/dropzone-demo/DemoDropzoneActionButtons.jsx new file mode 100644 index 0000000000000000000000000000000000000000..955d11b99a07be54c33ab668b0aa1ea5c085e9b9 --- /dev/null +++ b/src/components/demo-components/dropzone-demo/DemoDropzoneActionButtons.jsx @@ -0,0 +1,36 @@ +import * as React from "react"; +import { Dropzone, FileMosaic } from "../../../files-ui"; + +const DemoDropzoneActionButtons = (props) => { + const [files, setFiles] = React.useState([]); + const updateFiles = (incommingFiles) => { + //do something with the files + setFiles(incommingFiles); + //even your own upload implementation + }; + const removeFile = (id) => { + setFiles(files.filter((x) => x.id !== id)); + }; + return ( + <Dropzone + onChange={updateFiles} + value={files} + autoClean + uploadConfig={{ url: "https://www.myawsomeserver.com/upload" }} + fakeUpload + actionButtons={{ + position: "bottom", + uploadButton: { style: { textTransform: "uppercase" } }, + abortButton: {}, + cleanButton: {}, + deleteButton: {}, + }} + > + {files.length > 0 && + files.map((file) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info /> + ))} + </Dropzone> + ); +}; +export default DemoDropzoneActionButtons; diff --git a/src/components/demo-components/dropzone-demo/DemoDropzoneBehaviour.tsx b/src/components/demo-components/dropzone-demo/DemoDropzoneBehaviour.tsx new file mode 100644 index 0000000000000000000000000000000000000000..f14444f89586f1e42b6965f5e32d850b7a03e246 --- /dev/null +++ b/src/components/demo-components/dropzone-demo/DemoDropzoneBehaviour.tsx @@ -0,0 +1,69 @@ +import * as React from "react"; +import { Dropzone, ExtFile, FileMosaic, FileMosaicProps } from "../../../files-ui"; + +const DemoDropzoneBehaviour = () => { + const [filesAdd, setFilesAdd] = React.useState<ExtFile[]>([]); + const [filesReplace, setFilesReplace] = React.useState<ExtFile[]>([]); + + const updateFilesAdd = (incommingFiles:ExtFile[]) => { + setFilesAdd(incommingFiles); + }; + const updateFilesReplace = (incommingFiles:ExtFile[]) => { + setFilesReplace(incommingFiles); + }; + const removeFileAdd = (id:FileMosaicProps["id"]) => { + setFilesAdd(filesAdd.filter((x) => x.id !== id)); + }; + const removeFileReplace = (id:FileMosaicProps["id"]) => { + setFilesReplace(filesReplace.filter((x) => x.id !== id)); + }; + return ( + <div + style={{ + display: "flex", + justifyContent: "space-evenly", + gap: "40px", + flexWrap: "wrap", + width: "100%", + }} + > + <Dropzone + style={{ width: "min(100% , 420px)" }} + onChange={updateFilesAdd} + value={filesAdd} + label="Dropzone with behaviour=add" + behaviour={"add"} + > + {filesAdd.length > 0 && + filesAdd.map((file) => ( + <FileMosaic + key={file.id} + {...file} + onDelete={removeFileAdd} + info + preview + /> + ))} + </Dropzone> + <Dropzone + style={{ width: "min(100% , 420px)" }} + onChange={updateFilesReplace} + value={filesReplace} + label="Dropzone with behaviour=replace" + behaviour={"replace"} + > + {filesReplace.length > 0 && + filesReplace.map((file) => ( + <FileMosaic + key={file.id} + {...file} + onDelete={removeFileReplace} + info + preview + /> + ))} + </Dropzone> + </div> + ); +}; +export default DemoDropzoneBehaviour; diff --git a/src/components/demo-components/dropzone-demo/DemoDropzoneClickable.jsx b/src/components/demo-components/dropzone-demo/DemoDropzoneClickable.jsx new file mode 100644 index 0000000000000000000000000000000000000000..4037a97d7d8751f753165eddec07b888d6e178cd --- /dev/null +++ b/src/components/demo-components/dropzone-demo/DemoDropzoneClickable.jsx @@ -0,0 +1,21 @@ +import * as React from "react"; +import { Dropzone } from "../../../files-ui"; + +const DemoDropzoneClickable = (props) => { + return ( + <div + style={{ + display: "flex", + justifyContent: "space-evenly", + gap: "40px", + flexWrap: "wrap", + }} + > + <Dropzone style={{ width: "300px" }}>{/**Files */}</Dropzone> + <Dropzone style={{ width: "300px" }} clickable={false}> + {/**Files */} + </Dropzone> + </div> + ); +}; +export default DemoDropzoneClickable; diff --git a/src/components/demo-components/dropzone-demo/DemoDropzoneCustomValidation.tsx b/src/components/demo-components/dropzone-demo/DemoDropzoneCustomValidation.tsx new file mode 100644 index 0000000000000000000000000000000000000000..a41bf399f006d660529d5c3929af30fd57ab3e63 --- /dev/null +++ b/src/components/demo-components/dropzone-demo/DemoDropzoneCustomValidation.tsx @@ -0,0 +1,50 @@ +import * as React from "react"; +import { + Dropzone, + ExtFile, + FileMosaic, + FileMosaicProps, + CustomValidateFileResponse, +} from "../../../files-ui"; + +//validate files +// file name must start with the following prefix: "test_file" +// (e.g. a valid file name could be "test_file_photo.png") +const myOwnValidation = (file: File): CustomValidateFileResponse => { + let errorList: string[] = []; + let validResult: boolean = true; + const regExPrefix: RegExp = /\btest_file\w+/; + if (!file.name.match(regExPrefix)) { + validResult = false; + errorList.push('Prefix "test_file" was not present in the file name'); + } + return { valid: validResult, errors: errorList }; +}; +const DemoDropzoneCustomValidation = () => { + const [files, setFiles] = React.useState<ExtFile[]>([]); + const updateFiles = (incommingFiles: ExtFile[]) => { + //do something with the files + setFiles(incommingFiles); + //even your own upload implementation + }; + const removeFile = (id: FileMosaicProps["id"]) => { + setFiles(files.filter((x) => x.id !== id)); + }; + return ( + <Dropzone + onChange={updateFiles} + value={files} + accept={"image/*"} + maxFileSize={28 * 1024} + maxFiles={2} + cleanFiles + validator={myOwnValidation} + > + {files.length > 0 && + files.map((file: ExtFile) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info /> + ))} + </Dropzone> + ); +}; +export default DemoDropzoneCustomValidation; diff --git a/src/components/demo-components/dropzone-demo/DemoDropzoneDisabled.jsx b/src/components/demo-components/dropzone-demo/DemoDropzoneDisabled.jsx new file mode 100644 index 0000000000000000000000000000000000000000..bfcc6b357940f80406dde44c21c54e0a3b79f490 --- /dev/null +++ b/src/components/demo-components/dropzone-demo/DemoDropzoneDisabled.jsx @@ -0,0 +1,22 @@ +import * as React from "react"; +import { Dropzone } from "../../../files-ui"; + +const DemoDropzoneDisabled = () => { + return ( + <div + style={{ + display: "flex", + justifyContent: "space-evenly", + gap: "40px", + flexWrap: "wrap", + }} + > + <Dropzone style={{ width: "300px" }}>{/**Files */}</Dropzone> + <Dropzone style={{ width: "300px" }} disabled> + {"Dropzone is disabled"} + {/**Files */} + </Dropzone> + </div> + ); +}; +export default DemoDropzoneDisabled; diff --git a/src/components/demo-components/dropzone-demo/DemoDropzoneDropLayer.jsx b/src/components/demo-components/dropzone-demo/DemoDropzoneDropLayer.jsx new file mode 100644 index 0000000000000000000000000000000000000000..3a6edfc3d4defd3b18aff2a0cf5fc8e33731b721 --- /dev/null +++ b/src/components/demo-components/dropzone-demo/DemoDropzoneDropLayer.jsx @@ -0,0 +1,19 @@ +import * as React from "react"; +import { Dropzone } from "../../../files-ui"; + +const DemoDropzoneDropLayer = () => { + return ( + <div style={{ + display: "flex", + justifyContent: "space-evenly", + gap: "40px", + flexWrap: "wrap", + }}> + <Dropzone style={{ width: "300px" }}>{/**Files */}</Dropzone> + <Dropzone style={{ width: "300px" }} dropOnLayer={false}> + {/**Files */} + </Dropzone> + </div> + ); +}; +export default DemoDropzoneDropLayer; diff --git a/src/components/demo-components/dropzone-demo/DemoDropzoneFooterConfig.jsx b/src/components/demo-components/dropzone-demo/DemoDropzoneFooterConfig.jsx new file mode 100644 index 0000000000000000000000000000000000000000..90a0912cd0d1407684283b1f822800c6ba8845cf --- /dev/null +++ b/src/components/demo-components/dropzone-demo/DemoDropzoneFooterConfig.jsx @@ -0,0 +1,27 @@ +import * as React from "react"; +import { Dropzone, FileMosaic } from "../../../files-ui"; + +const DemoDropzoneFooterConfig = (props) => { + const [files, setFiles] = React.useState([]); + const updateFiles = (incommingFiles) => { + //do something with the files + setFiles(incommingFiles); + //even your own upload implementation + }; + const removeFile = (id) => { + setFiles(files.filter((x) => x.id !== id)); + }; + return ( + <Dropzone + onChange={updateFiles} + value={files} + footerConfig={{ customMessage: "This is a custom message..." }} + > + {files.length > 0 && + files.map((file) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info /> + ))} + </Dropzone> + ); +}; +export default DemoDropzoneFooterConfig; diff --git a/src/components/demo-components/dropzone-demo/DemoDropzoneHeaderConfig.jsx b/src/components/demo-components/dropzone-demo/DemoDropzoneHeaderConfig.jsx new file mode 100644 index 0000000000000000000000000000000000000000..44f44bc58ac9973eb86a12206962caa3adf8bedc --- /dev/null +++ b/src/components/demo-components/dropzone-demo/DemoDropzoneHeaderConfig.jsx @@ -0,0 +1,42 @@ +import * as React from "react"; +import { Dropzone, FileMosaic } from "../../../files-ui"; + +const DemoDropzoneHeaderConfig = (props) => { + const [files, setFiles] = React.useState([]); + const updateFiles = (incommingFiles) => { + //do something with the files + setFiles(incommingFiles); + //even your own upload implementation + }; + const removeFile = (id) => { + setFiles(files.filter((x) => x.id !== id)); + }; + const removeAllFiles = (evt) => { + evt.stopPropagation(); + setFiles([]); + }; + return ( + <Dropzone + onChange={updateFiles} + value={files} + headerConfig={{ + customHeader: ( + <div style={{ display: "flex", justifyContent: "flex-end" }}> + <button + style={{ backgroundColor: "teal", color: "white" }} + onClick={removeAllFiles} + > + delete files + </button> + </div> + ), + }} + > + {files.length > 0 && + files.map((file) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info /> + ))} + </Dropzone> + ); +}; +export default DemoDropzoneHeaderConfig; diff --git a/src/components/demo-components/dropzone-demo/DemoDropzoneLabel.jsx b/src/components/demo-components/dropzone-demo/DemoDropzoneLabel.jsx new file mode 100644 index 0000000000000000000000000000000000000000..a5a44461032a3af8f49e8c8fb56368079dfef756 --- /dev/null +++ b/src/components/demo-components/dropzone-demo/DemoDropzoneLabel.jsx @@ -0,0 +1,24 @@ +import * as React from "react"; +import { Dropzone } from "../../../files-ui"; + +const DemoDropzoneLabel = () => { + return ( + <div + style={{ + display: "flex", + justifyContent: "space-evenly", + gap: "40px", + flexWrap: "wrap", + }} + > + <Dropzone style={{ width: "300px" }}>{/**Files */}</Dropzone> + <Dropzone + style={{ width: "300px" }} + label={"Files ui â¤ï¸"} + > + {/**Files */} + </Dropzone> + </div> + ); +}; +export default DemoDropzoneLabel; diff --git a/src/components/demo-components/dropzone-demo/DemoDropzoneRipple.tsx b/src/components/demo-components/dropzone-demo/DemoDropzoneRipple.tsx new file mode 100644 index 0000000000000000000000000000000000000000..a0fed19d059bf93eb6fa79fc98598824bb15bee3 --- /dev/null +++ b/src/components/demo-components/dropzone-demo/DemoDropzoneRipple.tsx @@ -0,0 +1,21 @@ +import * as React from "react"; +import { Dropzone } from "../../../files-ui"; + +const DemoDropzoneRipple = () => { + return ( + <div + style={{ + display: "flex", + justifyContent: "space-evenly", + gap: "40px", + flexWrap: "wrap", + }} + > + <Dropzone style={{ width: "300px" }}>ripple enabled </Dropzone> + <Dropzone style={{ width: "300px" }} disableRipple> + ripple disabled + </Dropzone> + </div> + ); +}; +export default DemoDropzoneRipple; diff --git a/src/components/demo-components/dropzone-demo/DemoDropzoneStyling.jsx b/src/components/demo-components/dropzone-demo/DemoDropzoneStyling.jsx new file mode 100644 index 0000000000000000000000000000000000000000..0a2658c50edb10e0725ad614a0f788eec159974b --- /dev/null +++ b/src/components/demo-components/dropzone-demo/DemoDropzoneStyling.jsx @@ -0,0 +1,35 @@ +import * as React from "react"; +import { Dropzone } from "../../../files-ui"; + +const DemoDropzoneStyling = () => { + return ( + <div + style={{ + display: "flex", + justifyContent: "space-evenly", + gap: "20px", + flexWrap: "wrap", + alignItems: "flex-start", + }} + > + <Dropzone style={{ width: "300px" }} color="#6200EE"> + {/**Files */} + </Dropzone> + <Dropzone + style={{ width: "300px" }} + minHeight="120px" + header={false} + footer={false} + > + {/**Files */} + </Dropzone> + <Dropzone + style={{ width: "300px" }} + background="radial-gradient(circle at 18.7% 37.8%, rgb(250, 250, 250) 0%, rgb(225, 234, 238) 90%);" + > + {/**Files */} + </Dropzone> + </div> + ); +}; +export default DemoDropzoneStyling; diff --git a/src/components/demo-components/dropzone-demo/DemoDropzoneUploading.jsx b/src/components/demo-components/dropzone-demo/DemoDropzoneUploading.jsx new file mode 100644 index 0000000000000000000000000000000000000000..47757694c38db7e4f50d05aa3217d6bafdb69bd0 --- /dev/null +++ b/src/components/demo-components/dropzone-demo/DemoDropzoneUploading.jsx @@ -0,0 +1,40 @@ +import * as React from "react"; +import { Dropzone, FileMosaic } from "../../../files-ui"; + +const DemoDropzoneUploading = (props) => { + const [files, setFiles] = React.useState([]); + const updateFiles = (incommingFiles) => { + //do something with the files + setFiles(incommingFiles); + //even your own upload implementation + }; + const removeFile = (id) => { + setFiles(files.filter((x) => x.id !== id)); + }; + return ( + <Dropzone + onChange={updateFiles} + value={files} + accept={"image/*"} + maxFileSize={28 * 1024*1024} + maxFiles={2} + actionButtons={{ position: "bottom", uploadButton: {}, abortButton: {} }} + uploadConfig={{ + url: "https://www.myawsomeserver.com/upload", + method: "POST", + headers: { + Authorization: + "bearer HTIBI/IBYG/&GU&/GV%&G/&IC%&V/Ibi76bfh8g67gg68g67i6g7G&58768&/(&/(FR&G/&H%&/", + }, + cleanOnUpload: true, + }} + fakeUpload + > + {files.length > 0 && + files.map((file) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info /> + ))} + </Dropzone> + ); +}; +export default DemoDropzoneUploading; diff --git a/src/components/demo-components/dropzone-demo/DemoDropzoneValidation.jsx b/src/components/demo-components/dropzone-demo/DemoDropzoneValidation.jsx new file mode 100644 index 0000000000000000000000000000000000000000..5a20526c7dd50a10894828dba55c714e70ff031f --- /dev/null +++ b/src/components/demo-components/dropzone-demo/DemoDropzoneValidation.jsx @@ -0,0 +1,31 @@ +import * as React from "react"; +import { Dropzone, FileMosaic } from "../../../files-ui"; + +const DemoDropzoneValidation = (props) => { + const [files, setFiles] = React.useState([]); + const updateFiles = (incommingFiles) => { + //do something with the files + setFiles(incommingFiles); + //even your own upload implementation + }; + const removeFile = (id) => { + setFiles(files.filter((x) => x.id !== id)); + }; + return ( + <Dropzone + onChange={updateFiles} + value={files} + accept={"image/*"} + maxFileSize={28 * 1024} + maxFiles={2} + //cleanFiles + actionButtons={{ position: "bottom", cleanButton: {} }} + > + {files.length > 0 && + files.map((file) => ( + <FileMosaic key={file.id} {...file} onDelete={removeFile} info /> + ))} + </Dropzone> + ); +}; +export default DemoDropzoneValidation; diff --git a/src/files-ui/components/drop-layer/hooks/useDropLayerClassName.ts b/src/files-ui/components/drop-layer/hooks/useDropLayerClassName.ts index d933627c6fa1469144d722126b8e2859befe8cf5..2a83c6ecfa6ece0a393d2dee2d4617394020b6f4 100644 --- a/src/files-ui/components/drop-layer/hooks/useDropLayerClassName.ts +++ b/src/files-ui/components/drop-layer/hooks/useDropLayerClassName.ts @@ -1,8 +1,10 @@ import { DynamicSheet, DynamiCSS } from "@dynamicss/dynamicss"; import * as React from "react"; -import { dropLayerDynamicStyle } from "../utils/dropLayerDynamicStyle"; +import { makeDropLayerDynamicStyle } from "../utils/dropLayerDynamicStyle"; + +const BASE_DROP_LAYER_STYLE: string = "files-ui-styles-drop-layer"; + -const DROP_LAYER_STYLE_ID: string = "files-ui-styles-drop-layer"; /** * * @param color @@ -11,26 +13,48 @@ const DROP_LAYER_STYLE_ID: string = "files-ui-styles-drop-layer"; * @returns the classname for layer */ const useDropLayerClassName = ( + dropzoneId: string, color?: string, - isDragging?: boolean, + //isDragging?: boolean, makeClassName?: boolean ): string => { const [idStyles, setIdStyles] = React.useState<string>(""); const [styleInjected, setStyleInjected] = React.useState<boolean>(false); const [classNameCreated, setClassNameCreated] = React.useState<string>(""); + const finalDropzoneId: string = (color === undefined) ? "default" : dropzoneId.replaceAll(":", "_"); + + React.useEffect(() => { //console.log("useDropLayerClassName", color, isDragging, makeClassName); const handleInserStyle = ( - color: string, - isDragging?: boolean + color?: string, + //isDragging?: boolean ) => { let finalClassName: string = ""; - let styleSheet: DynamicSheet = dropLayerDynamicStyle(DROP_LAYER_STYLE_ID, color); + let styleSheet: DynamicSheet = makeDropLayerDynamicStyle(finalDropzoneId, color + //isDragging + ); let idStyle: string = ""; - if (!styleInjected) { + console.log("useDropLayerClassName handleInserStyle", color, styleSheet); + + + if (finalDropzoneId === "default" && !styleInjected) { + //check if already inserted + if (DynamiCSS.existStyleSheet(finalDropzoneId)) { + setStyleInjected(true); + setIdStyles(idStyle); + + } else { + idStyle = DynamiCSS.insertStyleSheet(styleSheet); + setIdStyles(idStyle); + if (idStyle !== "") { + setStyleInjected(true); + } + } + } else if (!styleInjected) { idStyle = DynamiCSS.insertStyleSheet(styleSheet); setIdStyles(idStyle); if (idStyle !== "") { @@ -40,22 +64,26 @@ const useDropLayerClassName = ( //already a stylesheet associated DynamiCSS.editStyleSheet(idStyles, styleSheet.sheetRules || []); } - finalClassName += `dropzone-ui-layer`; + finalClassName += `dropzone-layer-${finalDropzoneId}`; - if (isDragging) { - finalClassName += ` dui-layer-drag`; - } + /* if (isDragging) { + finalClassName += ` dropzone-layer-drag`; + } */ setClassNameCreated(finalClassName); }; //console.log("=>", isDragging); if (makeClassName) { - handleInserStyle(color as string, isDragging); + handleInserStyle(color, + // isDragging + ); } // eslint-disable-next-line - }, [color, isDragging, makeClassName]); + }, [color, + // isDragging, + makeClassName]); return classNameCreated; } diff --git a/src/files-ui/components/drop-layer/utils/dropLayerDynamicStyle.ts b/src/files-ui/components/drop-layer/utils/dropLayerDynamicStyle.ts index b62a64666873a2603a1d1c37754554ff2fa6712c..3a1cabd7df57153f7459b0cdb100b0653d3ae68a 100644 --- a/src/files-ui/components/drop-layer/utils/dropLayerDynamicStyle.ts +++ b/src/files-ui/components/drop-layer/utils/dropLayerDynamicStyle.ts @@ -1,34 +1,33 @@ -import { asureColor, colourNameToHex, hexColorToRGB } from "../../../core"; - -export const dropLayerDynamicStyle = (styleId:string, color: string | undefined) => { +import { completeAsureColor } from "../../../core"; +import { DEFAULT_BORDER_RADIUS } from "../../dropzone/components/dropzone/DropzoneProps"; +export const makeDropLayerDynamicStyle = ( + dropzoneId: string, + color: string | undefined, +) => { return { - id: "files-ui-styles-drop-layer", + id: "files-ui-drop-layer-style-id-" + dropzoneId, sheetRules: [ { - className: `dropzone-ui-layer`, + className: `dropzone-layer-${dropzoneId}`, rules: { - backgroundColor: hexColorToRGB( - asureColor(colourNameToHex(color)), - 0.4 - ), + backgroundColor: completeAsureColor(color, 0.4), + borderRadius: DEFAULT_BORDER_RADIUS, position: "absolute", left: 0, top: 0, width: "0%", height: "0%", - border: `2px dashed ${hexColorToRGB( - asureColor(colourNameToHex(color)), - 1 - )}`, zIndex: 20, + border: `0px dashed ${completeAsureColor(color)}` }, }, { - className: `dui-layer-drag`, + className: `dropzone-layer-drag`, rules: { width: "100%", height: "100%", + borderWidth:"2px" }, } ], diff --git a/src/files-ui/components/dropzone/DropzoneRipple.scss b/src/files-ui/components/dropzone/DropzoneRipple.scss index af9b6cd6b76afd662161ec85805a0019ae5d9700..0ade0878568a159af43d5aa2505981d4fffc013f 100644 --- a/src/files-ui/components/dropzone/DropzoneRipple.scss +++ b/src/files-ui/components/dropzone/DropzoneRipple.scss @@ -1,35 +1,37 @@ - // RIPPLE - .dropzone-ui-base-ripple-absolute { - position: absolute; - display: none; +// RIPPLE +.dropzone-ui-base-ripple-absolute { + position: absolute; + display: none; + width: 100%; + height: 100%; + top: 0; + left: 0; + box-sizing: border-box; + border-radius: 8px; + overflow: hidden; + .dropzone-ui-base-ripple-relative { width: 100%; height: 100%; - top: 0; - left: 0; + position: relative; + overflow: hidden; + display: flex; + justify-content: center; + align-items: center; box-sizing: border-box; - .dropzone-ui-base-ripple-relative { - width: 100%; - height: 100%; - position: relative; - overflow: hidden; - display: flex; - justify-content: center; - align-items: center; - box-sizing: border-box; - - span.ripple { - position: absolute; - border-radius: 50%; - transform: scale(0); - animation: ripple 500ms linear; - background-color: rgba(255, 255, 255, 0.7); - } + // + span.ripple { + position: absolute; + border-radius: 50%; + transform: scale(0); + animation: ripple 500ms linear; + background-color: rgba(255, 255, 255, 0.7); } + } - @keyframes ripple { - to { - transform: scale(4); - opacity: 0; - } + @keyframes ripple { + to { + transform: scale(4); + opacity: 0; } - } \ No newline at end of file + } +} diff --git a/src/files-ui/components/dropzone/components/DropzoneButtons/DropzoneButtons.scss b/src/files-ui/components/dropzone/components/DropzoneButtons/DropzoneButtons.scss index 4c00f2ac660f04be1b549ddafc4099a17b2858b6..491b334f6f5abe3094adc0c7f2a304e4e2cba1bb 100644 --- a/src/files-ui/components/dropzone/components/DropzoneButtons/DropzoneButtons.scss +++ b/src/files-ui/components/dropzone/components/DropzoneButtons/DropzoneButtons.scss @@ -7,6 +7,13 @@ align-items: center; justify-content: flex-end; box-sizing: border-box; - padding: 10px 0; + //padding: 10px 0; gap: 10px; + + &.top { + padding-bottom: 10px; + } + &.bottom { + padding-top: 10px; + } } diff --git a/src/files-ui/components/dropzone/components/DropzoneButtons/DropzoneButtons.tsx b/src/files-ui/components/dropzone/components/DropzoneButtons/DropzoneButtons.tsx index 0493ac2fd0df500c75c4690dd75579a6111f31f0..4fc768150e5e4301e5143eeb147c7000c56ba8f2 100644 --- a/src/files-ui/components/dropzone/components/DropzoneButtons/DropzoneButtons.tsx +++ b/src/files-ui/components/dropzone/components/DropzoneButtons/DropzoneButtons.tsx @@ -6,12 +6,14 @@ import { DropzoneActions, } from "../dropzone/DropzoneProps"; import "./DropzoneButtons.scss"; + interface DropzoneButtonsProps extends DropzoneActions { localization?: Localization; onAbort?: Function; onDelete?: Function; onUpload?: Function; onClean?: Function; + top?: boolean; } const DropzoneButtons: React.FC<DropzoneButtonsProps> = ( @@ -29,6 +31,7 @@ const DropzoneButtons: React.FC<DropzoneButtonsProps> = ( onClean, onDelete, onUpload, + top } = props; const actionButtonsList: DropzoneActionButton[] = [ @@ -49,11 +52,10 @@ const DropzoneButtons: React.FC<DropzoneButtonsProps> = ( ) as DropzoneActionButton[]; const finalClassName = addClassName( - "files-ui-buttons-container", + "files-ui-buttons-container" + `${top ? " top" : " bottom"}`, containerClassName ); - return ( <div className={finalClassName} style={containerStyle}> {actionButtonsList.map( diff --git a/src/files-ui/components/dropzone/components/DropzoneChildren/DropzoneChildren.scss b/src/files-ui/components/dropzone/components/DropzoneChildren/DropzoneChildren.scss new file mode 100644 index 0000000000000000000000000000000000000000..000b97d9ef25ec3820a5a026405d5a99b5e22f37 --- /dev/null +++ b/src/files-ui/components/dropzone/components/DropzoneChildren/DropzoneChildren.scss @@ -0,0 +1,11 @@ +.files-ui-dropzone-children-container { + width: 100%; + flex-grow: 1; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + flex-wrap: wrap; + padding: 5px 0; + //background-color: brown; +} diff --git a/src/files-ui/components/dropzone/DropzoneChildren.tsx b/src/files-ui/components/dropzone/components/DropzoneChildren/DropzoneChildren.tsx similarity index 74% rename from src/files-ui/components/dropzone/DropzoneChildren.tsx rename to src/files-ui/components/dropzone/components/DropzoneChildren/DropzoneChildren.tsx index 48d9b2052fbd0eb1b3bad374ace2c7c9aefe015c..64407590eef2e73ead5e70f38c39b89520bc88c3 100644 --- a/src/files-ui/components/dropzone/DropzoneChildren.tsx +++ b/src/files-ui/components/dropzone/components/DropzoneChildren/DropzoneChildren.tsx @@ -3,7 +3,8 @@ import { DropzoneLocalizerSelector, Localization, LocalLabels, -} from "../../core"; +} from "../../../../core"; +import "./DropzoneChildren.scss"; declare type DropzoneChildrenProps = { children?: React.ReactNode | []; @@ -20,12 +21,14 @@ const DropzoneChildren: React.FC<DropzoneChildrenProps> = ( //children will be always consider as more important if (children) { - return <React.Fragment>{children}</React.Fragment>; + return ( + <div className="files-ui-dropzone-children-container">{children}</div> + ); } else return ( - <React.Fragment> + <div className="files-ui-dropzone-children-container"> <label> {label || (DropzoneLocalizer.defaultLabel as string)}</label> - </React.Fragment> + </div> ); }; export default DropzoneChildren; diff --git a/src/files-ui/components/dropzone/components/DropzoneDisabledLayer/DropzoneDisabledLayer.tsx b/src/files-ui/components/dropzone/components/DropzoneDisabledLayer/DropzoneDisabledLayer.tsx index 54e8b531831907f6f7b7dc92eab19a4ce16d23b4..16677b38cf79f7e83c6485b704925ea8e2cfbcf9 100644 --- a/src/files-ui/components/dropzone/components/DropzoneDisabledLayer/DropzoneDisabledLayer.tsx +++ b/src/files-ui/components/dropzone/components/DropzoneDisabledLayer/DropzoneDisabledLayer.tsx @@ -1,13 +1,18 @@ import * as React from "react"; -import { handleClickUtil, handleDragUtil } from "../../../../core"; +import { addClassName, handleClickUtil, handleDragUtil, handleDropUtil } from "../../../../core"; import "./DropzoneDisabledLayer.scss"; export declare type DropzoneDisabledLayerProps = { open?: boolean; + className?: string; + style?: React.CSSProperties; + //TO-DO: aloow to se a custom children + //for disabled layer and also drop layer }; const DropzoneDisabledLayer: React.FC<DropzoneDisabledLayerProps> = ( props: DropzoneDisabledLayerProps ) => { - const { open } = props; + const { open, className, style } = props; + function handleClick<T extends HTMLDivElement>( evt: React.MouseEvent<T, MouseEvent> ): void { @@ -18,12 +23,20 @@ const DropzoneDisabledLayer: React.FC<DropzoneDisabledLayerProps> = ( ) => { handleDragUtil(evt); }; + + const handleDrop : React.DragEventHandler<HTMLDivElement> = async ( + evt: React.DragEvent<HTMLDivElement> + ): Promise<void> => { + handleDropUtil(evt); + } + const finalDisabledLayerClassName:string = addClassName("dropzone-ui-disabled-root",className); if (open) { return ( <div - className="dropzone-ui-disabled-root" - onDrop={handleDrag} - onDrag={handleDrag} + style={style} + className={finalDisabledLayerClassName} + onDrop={handleDrop} + onDragOver={handleDrag} onClick={handleClick} ></div> ); diff --git a/src/files-ui/components/dropzone/components/DropzoneFooter/DropzoneFooter.tsx b/src/files-ui/components/dropzone/components/DropzoneFooter/DropzoneFooter.tsx index a1f0dc5d732c9e2fd80536aff1e038307e901c26..ccf25a75c1c262d679e30f265a37d4b9ef668aa2 100644 --- a/src/files-ui/components/dropzone/components/DropzoneFooter/DropzoneFooter.tsx +++ b/src/files-ui/components/dropzone/components/DropzoneFooter/DropzoneFooter.tsx @@ -1,42 +1,79 @@ import * as React from "react"; import { + addClassName, DropzoneLocalizerSelector, FunctionLabel, handleClickUtil, Localization, LocalLabels, } from "../../../../core"; +import { FooterConfig } from "../dropzone/DropzoneProps"; -export interface DropzoneFooterNeoProps { +export interface DropzoneFooterProps extends FooterConfig { + firstClassName?: string; accept?: string; message?: string; localization?: Localization; + borderRadius?: string | number; + style?: React.CSSProperties; + className?: string; + resetStyles?: boolean; } -const DropzoneFooter: React.FC<DropzoneFooterNeoProps> = ( - props: DropzoneFooterNeoProps +const DropzoneFooter: React.FC<DropzoneFooterProps> = ( + props: DropzoneFooterProps ) => { - const { accept, message, localization } = props; + const { + accept, + message, + localization, + borderRadius, + style, + className = "", + resetStyles = false, + allowedTypesLabel = true, + customMessage = undefined, + firstClassName = "", + } = props; const DropzoneFooterLocalizer: LocalLabels = DropzoneLocalizerSelector( localization ).footer as LocalLabels; const accepCustomMessenger: FunctionLabel = DropzoneFooterLocalizer.acceptCustom as FunctionLabel; - + function handleClick<T extends HTMLDivElement>( evt: React.MouseEvent<T, MouseEvent> ): void { handleClickUtil(evt); } + const finalClassName = resetStyles + ? className + : addClassName("files-ui-footer" + " " + firstClassName, className); + + const finalStyle = resetStyles + ? style + : { + ...style, + borderBotomLeftRadius: borderRadius, + borderBotomRightRadius: borderRadius, + }; + console.log("files-ui-footer", finalStyle); + return ( - <div className="files-ui-footer" onClick={handleClick}> - <> - {message - ? message - : !accept - ? DropzoneFooterLocalizer.acceptAll - : accepCustomMessenger(accept)} - </> + <div className={finalClassName} onClick={handleClick} style={finalStyle}> + {customMessage ? ( + <>{customMessage}</> + ) : ( + <> + {message + ? message + : !accept + ? allowedTypesLabel + ? DropzoneFooterLocalizer.acceptAll + : undefined + : accepCustomMessenger(accept)} + </> + )} </div> ); }; diff --git a/src/files-ui/components/dropzone/components/DropzoneHeader/DropzoneHeader.tsx b/src/files-ui/components/dropzone/components/DropzoneHeader/DropzoneHeader.tsx index 919b0750b56964dc630560c3e979ff4976d6e264..dc0d040e7a73370fbe5ab4aefdc945a74cf40318 100644 --- a/src/files-ui/components/dropzone/components/DropzoneHeader/DropzoneHeader.tsx +++ b/src/files-ui/components/dropzone/components/DropzoneHeader/DropzoneHeader.tsx @@ -1,5 +1,6 @@ import * as React from "react"; import { + addClassName, DropzoneLocalizerSelector, fileSizeFormater, FunctionLabel, @@ -8,7 +9,8 @@ import { } from "../../../../core"; import { UploadingProcess, Clean, Cancel, Upload } from "../../../icons"; -export interface DropzoneHeaderProps { +export type DropzoneHeaderProps = { + firstClassName?: string; maxFileSize?: number; numberOfValidFiles?: number; maxFiles?: number; @@ -17,13 +19,13 @@ export interface DropzoneHeaderProps { urlPresent?: boolean; onClean?: Function; isUploading?: boolean; - /** - * language to be used - * for now - * only English and Spanish is supported - */ localization?: Localization; -} + borderRadius?: string | number; + style?: React.CSSProperties; + className?: string; + resetStyles?: boolean; + color?: string; +}; const DropzoneHeader: React.FC<DropzoneHeaderProps> = ( props: DropzoneHeaderProps @@ -38,6 +40,12 @@ const DropzoneHeader: React.FC<DropzoneHeaderProps> = ( isUploading, urlPresent, localization, + borderRadius, + style, + className = "", + resetStyles, + color, + firstClassName="", } = props; const DropzoneHeaderLocalizer: LocalLabels = DropzoneLocalizerSelector( @@ -55,29 +63,22 @@ const DropzoneHeader: React.FC<DropzoneHeaderProps> = ( if (onUploadStart && urlPresent && numberOfValidFiles) { if (isUploading) { - result.push(<UploadingProcess spin={true} color="#646c7f" />); + result.push(<UploadingProcess spin={true} color={color} />); } else { result.push( <React.Fragment> <>{DropzoneHeaderLocalizer.uploadFilesMessage}</> - <Upload color="#646c7f" onClick={handleStartUploading} /> + <Upload color={color} onClick={handleStartUploading} /> </React.Fragment> ); } - result.push(<React.Fragment>{","} </React.Fragment>); } const maxFileSizeMessenger: FunctionLabel = DropzoneHeaderLocalizer.maxSizeMessage as FunctionLabel; if (maxFileSize) { - result.push( - maxFileSizeMessenger(fileSizeFormater(maxFileSize)) - - /* localization === "ES-es" - ? `Tam. máximo de archivo ${fileSizeFormater(maxFileSize)} | ` - : `Max File size: ${fileSizeFormater(maxFileSize)} | `, */ - ); + result.push(maxFileSizeMessenger(fileSizeFormater(maxFileSize))); result.push(<React.Fragment>{","} </React.Fragment>); } const validFileSizeMessenger: FunctionLabel = @@ -86,36 +87,48 @@ const DropzoneHeader: React.FC<DropzoneHeaderProps> = ( if (maxFiles) { result.push( validFileSizeMessenger(numberOfValidFiles as number, maxFiles) - /* localization === "ES-es" - ? `Archivos ${numberOfValidFiles}/${maxFiles} | Válidos: ${numberOfValidFiles} | ` - : `Files ${numberOfValidFiles}/${maxFiles} | Valid: ${numberOfValidFiles} | `, */ ); result.push(<React.Fragment>{","} </React.Fragment>); } //clean not valid files on click if (onClean) { result.push( - <Clean color="#646c7f" onClick={handleClean} size="semi-medium" /> + <Clean color={color} onClick={handleClean} size="semi-medium" /> ); } if (onReset) { result.push( <Cancel - color="#646c7f" + color={color} onClick={() => onReset?.()} - // colorFill="rgba(255,255,255,0.8)" + // colorFill="rgba(255,255,255,0.8)" /> ); } return result; }; + function handleClick<T extends HTMLDivElement>( + evt: React.MouseEvent<T, MouseEvent> + ): void { + evt.stopPropagation(); + } + + const finalClassName = resetStyles + ? className + : addClassName("files-ui-header" + " " + firstClassName, className); + const finalStyle = resetStyles + ? style + : { + ...style, + borderTopLeftRadius: borderRadius, + borderTopRightRadius: borderRadius, + }; + console.log("headerx resetStyles", resetStyles); + console.log("headerx style", style); + + console.log("headerx finalStyle", finalStyle); return ( - <div - className="files-ui-header" - onClick={(e) => { - e.stopPropagation(); - }} - > + <div className={finalClassName} onClick={handleClick} style={finalStyle}> {makeHeader().map((HeaderItem, index) => ( <span key={index} style={{ display: "flex" }}> {HeaderItem} diff --git a/src/files-ui/components/dropzone/components/dropzone/Dropzone.scss b/src/files-ui/components/dropzone/components/dropzone/Dropzone.scss index a923cdd50f7911ca034b6313e6bb0d11d3f787b2..a062c77d3936aa934897b30c067b2125e0309778 100644 --- a/src/files-ui/components/dropzone/components/dropzone/Dropzone.scss +++ b/src/files-ui/components/dropzone/components/dropzone/Dropzone.scss @@ -1,23 +1,23 @@ @import url(https://fonts.googleapis.com/css?family=Poppins:300,400,500,600,700,900); .fui-dropzone-root { - min-height: 180px; width: 100%; min-width: 150px; + min-height: 180px; position: relative; // DISPLAY display: flex; - flex-wrap: wrap; - flex-direction: row; + //flex-wrap: wrap; + flex-direction: column; gap: 0 8px; - justify-content: center; - align-items: center; - padding: 23px 0px; + //justify-content: center; + //align-items: center; + //padding: 23px 0px; /// label - color: #646c7f; + //color: #646c7f; text-rendering: optimizeLegibility; font-size: 1.5em; font-family: inherit; @@ -48,10 +48,14 @@ } .files-ui-header { - height: 22px; - position: absolute; - cursor: text; + min-height: 23px; + //-color: brown; + //box-sizing: border-box; + /* height: 22px; + position: absolute; top: 0; + */ + cursor: text; display: flex; //width: calc(100% - 10px); width: 100%; @@ -60,32 +64,33 @@ justify-content: flex-end; //font-family: "Poppins", sans-serif; font-family: inherit; - padding-right: 10px; + //padding-right: 10px; font-size: 1rem; + //background-color: rgba(0, 128, 128, 0.249); @media (max-width: 960px) { - width: calc(100% - 1px); - padding-right: 1px; + //width: calc(100% - 1px); + //padding-right: 1px; font-size: 0.8rem; } } .files-ui-footer { - border-bottom-left-radius: 8px; - border-bottom-right-radius: 8px; +/* border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; */ box-sizing: border-box; - height: 23px; - position: absolute; cursor: text; + /* height: 23px; + position: absolute; bottom: 0; - left: 0; + left: 0; */ // width: calc(100% - 10px); width: 100%; display: flex; flex-direction: column; align-items: flex-start; justify-content: center; - border-top: 1px dotted grey; - background-color: #80808021; + //border-top: 1px dotted grey; + //background-color: rgba(128, 128, 128, 0.129); font-family: "Poppins", sans-serif; font-family: inherit; padding-left: 10px; @@ -98,8 +103,12 @@ line-clamp: 1; -webkit-box-orient: vertical; + + //background-color: rgba(0, 128, 128, 0.249); + + @media (max-width: 960px) { - width: calc(100% - 1px); + //width: calc(100% - 1px); padding-left: 1px; font-size: 0.9rem; } diff --git a/src/files-ui/components/dropzone/components/dropzone/Dropzone.tsx b/src/files-ui/components/dropzone/components/dropzone/Dropzone.tsx index 7ff135c6dc25a4dc345b8e7d614c993d2e93b790..1cbafa207111ee455695b4500ab601aa7d4344a3 100644 --- a/src/files-ui/components/dropzone/components/dropzone/Dropzone.tsx +++ b/src/files-ui/components/dropzone/components/dropzone/Dropzone.tsx @@ -16,7 +16,7 @@ import { FunctionLabel, ExtFileManager, sleepPreparing, - UploadResponse, + //UploadResponse, instantPreparingToUploadOne, fakeFuiUpload, uploadExtFile, @@ -27,16 +27,20 @@ import { sanitizeArrExtFile, unexpectedErrorUploadResult, getRandomInt, + addClassName, } from "../../../../core"; import { mergeProps } from "../../../overridable"; import InputHidden from "../../../input-hidden/InputHidden"; import { defaultDrozoneProps, + // DEFAULT_BORDER_RADIUS, //DropzoneActionButton, DropzoneActions, DropzoneProps, + FooterConfig, + HeaderConfig, } from "./DropzoneProps"; -import DropzoneChildren from "./../../DropzoneChildren"; +import DropzoneChildren from "../DropzoneChildren/DropzoneChildren"; import useDropzoneClassName from "./../../useDropzoneClassName"; import DropzoneDisabledLayer from "../DropzoneDisabledLayer/DropzoneDisabledLayer"; @@ -48,50 +52,65 @@ import useDropzoneFileListUpdater from "../../../../hooks/useDropzoneFileUpdater import DropzoneHeader from "../DropzoneHeader/DropzoneHeader"; import DropzoneFooter from "../DropzoneFooter/DropzoneFooter"; import DropzoneButtons from "../DropzoneButtons/DropzoneButtons"; +import { completeAsureColor } from "../../../../core"; //import { print_manager } from "../../../../../utils"; const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => { const { + //basic + onChange, + value = [], + //validation accept, - behaviour, - children, - className, - clickable, - color, - disabled, - dropOnLayer, - label, - localization, maxFileSize, maxFiles, - onChange, - onDragEnter, - onDragLeave, - style, - textColor, validator, - value = [], - uploadConfig, - backgroundColor, - disableRipple, - fakeUpload, - footer, - header, - minHeight, cleanFiles, onClean, autoClean, + //uploading + uploadConfig, + fakeUpload, onUploadStart, onUploadFinish, + //styling + background, + minHeight, + color, + style, + textColor, + className, + //label + label, + //localization + localization, + //ripple + disableRipple, + //drag operations + onDragEnter, + onDragLeave, + //action butotns actionButtons, - headerConfig, - footerConfg, + //drop layer + dropOnLayer, + //header and footer + header, + footer, + headerConfig = {}, + footerConfig = {}, + //disabled + disabled, + //open file dialog + clickable, + //add or replace + behaviour, + //content + children, //advancedConfig, ...rest } = mergeProps(props, defaultDrozoneProps); - console.log("Dropzone props", children); - console.log("Dropzone value", value); + const { url, method, @@ -111,6 +130,27 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => { style: containerStyle, className: containerClassName, } = actionButtons as DropzoneActions; + const styleBorderRadius: string | number | undefined = style?.borderRadius; + + const { + cleanFiles: cleanFilesHeader = true, + deleteFiles: deleteFilesHeader = true, + maxFileSize: maxFileSizeHeader = true, + uploadFiles: uploadFilesHeader = true, + uploadingIcon: uploadingIconHedaer = true, + validFilesCount: validFilesCountHeader = true, + customHeader, + className: classNameHeader, + resetStyles: resetStylesHeader = false, + style: styleHeader, + }: HeaderConfig = headerConfig; + + const { + customFooter, + noMissingFilesLabel = true, + uploadProgressMessage = true, + uploadResultMessage = true, + }: FooterConfig = footerConfig; //console.log("Dropzone props", dropOnLayer); //localizers const DropzoneLocalizer: LocalLabels = @@ -179,17 +219,29 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => { * @returns nothing */ const uploadfiles = async (localFiles: ExtFile[]): Promise<void> => { + console.log( + "incomming extfiles uploadfiles localFiles", + localFiles.map((x) => x.uploadStatus) + ); //set uploading flag to true setIsUploading(true); //avoid to call upload if not allowed // flag is already true or there isnt files //url was not provided + if (isUploading || localFiles.length === 0 || !url) { setIsUploading(false); return; } + if (localFiles.length === 0) { + setLocalMessage(DropzoneLocalizer.noFilesMessage as string); + setTimeout(() => { + setIsUploading(false); + }, 1500); + return; + } // initialize a new list of ExtFileInstances let arrOfExtFilesInstances: ExtFileInstance[] = []; @@ -211,16 +263,22 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => { //no missing to upload if (!(missingUpload > 0)) { console.log("upload start: noFilesMessage", missingUpload); + setTimeout(() => { + if (noMissingFilesLabel) + setLocalMessage(DropzoneLocalizer.noFilesMessage as string); + + setIsUploading(false); + }, 1500); - setLocalMessage(DropzoneLocalizer.noFilesMessage as string); - setIsUploading(false); return; } - setLocalMessage(uploadingMessenger(`${missingUpload}/${totalNumber}`)); + if (uploadProgressMessage) + setLocalMessage(uploadingMessenger(`${missingUpload}/${totalNumber}`)); // setIsUploading(true); //PREPARING stage console.log("validateFilesFlag", validateFilesFlag); + onUploadStart?.(localFiles); arrOfExtFilesInstances = ExtFileManager.setFileListMapPreparing( @@ -251,8 +309,7 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => { console.log("FileManagerLog after sleep", arrOfExtFilesInstances); //return; - let serverResponses: Array<UploadResponse> = []; - + let serverResponses: Array<ExtFile> = []; //Uplad files one by one for (let i = 0; i < arrOfExtFilesInstances.length; i++) { const currentExtFileInstance: ExtFileInstance = arrOfExtFilesInstances[i]; @@ -273,22 +330,24 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => { instantPreparingToUploadOne(currentExtFileInstance); //messge in footer - setLocalMessage( - uploadingMessenger(`${++currentCountUpload}/${missingUpload}`) - ); + if (uploadProgressMessage) + setLocalMessage( + uploadingMessenger(`${++currentCountUpload}/${missingUpload}`) + ); //CHANGE FILES handleFilesChange(sanitizeArrExtFile(arrOfExtFilesInstances), true); //UPLOADING => UPLOAD() //upload one file and notify about change - let uploadResponse: UploadResponse; + let uploadResponse: ExtFile; if (fakeUpload) { uploadResponse = await fakeFuiUpload( currentExtFileInstance, DropzoneLocalizer ); + let fakeProgress = 0; while (fakeProgress < 100) { fakeProgress += getRandomInt(21, 35); @@ -313,40 +372,17 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => { } } - const { uploadedFile } = uploadResponse; + const uploadedFile = uploadResponse; console.log("fake uploadResponse uploadedFile", uploadedFile); //update instances currentExtFileInstance.uploadStatus = uploadedFile.uploadStatus; currentExtFileInstance.uploadMessage = uploadedFile.uploadMessage; - console.log( - "fake uploadResponse currentExtFileInstance", - currentExtFileInstance - ); - console.log( - "fake uploadResponse currentExtFileInstance", - currentExtFileInstance.uploadStatus - ); - console.log( - "fake uploadResponse currentExtFileInstance", - currentExtFileInstance.uploadMessage - ); - - console.log( - "pre sanitizeArrExtFile", - arrOfExtFilesInstances.map((F) => {return{status:F.uploadStatus,message:F.uploadMessage}}) - ); - - //CHANGE if (!(currentExtFileInstance.uploadStatus === "aborted")) await sleepTransition(); - console.log( - "pre sanitizeArrExtFile", - arrOfExtFilesInstances.map((F) => {return{status:F.uploadStatus,message:F.uploadMessage}}) - ); handleFilesChange(sanitizeArrExtFile(arrOfExtFilesInstances), true); if (uploadedFile.uploadStatus === "error") { @@ -359,17 +395,20 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => { } } - handleFilesChange(sanitizeArrExtFile(arrOfExtFilesInstances), true); + setLocalFiles(sanitizeArrExtFile(arrOfExtFilesInstances)); // upload group finished :D onUploadFinish?.(serverResponses); const finishUploadMessenger: FunctionLabel = DropzoneLocalizer.uploadFinished as FunctionLabel; - setLocalMessage( - finishUploadMessenger(missingUpload - totalRejected, totalRejected) - ); - setIsUploading(false); + if (uploadResultMessage) + setLocalMessage( + finishUploadMessenger(missingUpload - totalRejected, totalRejected) + ); + setTimeout(() => { + setIsUploading(false); + }, 2000); }; const handleAbortUpload = () => { @@ -392,19 +431,32 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => { }; //the final className - const dropzoneClassName: string | undefined = useDropzoneClassName( + const [ + dropzoneClassName, + headerClassName, + footerClassName, + disabledLayerClassName, + ]: [ + string | undefined, + string | undefined, + string | undefined, + string | undefined + ] = useDropzoneClassName( + dropzoneId, className, - isDragging, - header, - footer, + //isDragging && Boolean(dropOnLayer), + // header, + // footer, color, - backgroundColor, + //style?.borderRadius || borderRadius, + background, minHeight ); const dropLayerClassName: string = useDropLayerClassName( + dropzoneId, color as string, - isDragging, + // isDragging, !onDragEnter && !onDragLeave ); @@ -526,12 +578,23 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => { evt: React.DragEvent<HTMLDivElement> ) => { handleDragUtil(evt); + if (disabled) return; + setIsDragging(true); + /* setTimeout(() => { + setIsDragging((_isDragging) => { + //if (_isDragging) { + return false; + //} + //return true; + }); + }, 2000); */ //console.log("handleDragEnter"); }; const handleDragLeave: React.DragEventHandler<HTMLDivElement> = ( evt: React.DragEvent<HTMLDivElement> ) => { + if (disabled) return; handleDragUtil(evt); setIsDragging(false); }; @@ -560,7 +623,11 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => { evt: React.DragEvent<HTMLDivElement> ): Promise<void> => { handleDropUtil(evt); + + if (disabled) return; + if (!disableRipple) makeRipple(); + setIsDragging(false); if (isUploading) return; @@ -603,30 +670,23 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => { setLocalFiles(localFiles.filter((f) => f.valid)); } }; + console.log("Dropzone styleHeader", styleHeader); + + const finalDropzoneClassNameBorder: string | undefined = !dropzoneClassName + ? undefined + : (isDragging && dropOnLayer) || disabled + ? addClassName(dropzoneClassName, "files-ui-root-border-hide") + : dropzoneClassName; + + const finalDropzoneClassNameBorderClickable: string | undefined = + !finalDropzoneClassNameBorder + ? undefined + : clickable && !disabled + ? addClassName(finalDropzoneClassNameBorder, "clickable") + : finalDropzoneClassNameBorder; + + if (!finalDropzoneClassNameBorderClickable) return <></>; - const DropzoneActionButtons = ({ visible = true }) => { - if (!visible) return <></>; - else - return ( - <DropzoneButtons - abortButton={isUploading ? abortButton : undefined} - onAbort={handleAbortUpload} - deleteButton={deleteButton} - onDelete={!isUploading ? handleReset : undefined} - uploadButton={!isUploading && !autoUpload ? uploadButton : undefined} - onUpload={!autoUpload ? () => uploadfiles(localFiles) : undefined} - cleanButton={ - validateFilesFlag && !isUploading && !autoClean - ? cleanButton - : undefined - } - onClean={handleClean} - style={containerStyle} - className={containerClassName} - /> - ); - }; - if (!dropzoneClassName) return <></>; return ( <React.Fragment> {actionButtonsPosition === "top" && ( @@ -645,11 +705,12 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => { onClean={handleClean} style={containerStyle} className={containerClassName} + top={true} /> )} <div style={style} - className={dropzoneClassName} + className={finalDropzoneClassNameBorderClickable} {...rest} onClick={handleClick} onDragOver={handleDragEnter} @@ -660,6 +721,7 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => { <div ref={fuiRippleRefAbs} className="dropzone-ui-base-ripple-absolute" + style={{ borderRadius: style?.borderRadius }} > <div ref={fuiRippleRefRel} @@ -667,49 +729,87 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => { ></div> </div> )} - - {header && ( - <DropzoneHeader - onReset={!isUploading ? handleReset : undefined} - maxFileSize={maxFileSize} - maxFiles={maxFiles} - localization={localization} - urlPresent={url !== undefined} - onUploadStart={ - !autoUpload && !uploadButton - ? () => uploadfiles(localFiles) - : undefined - } - numberOfValidFiles={numberOfValidFiles} - //onClean={autoClean ? undefined : cleanButton ? undefined : onClean} - onClean={ - isUploading || cleanButton || autoClean - ? undefined - : (cleanFiles || onClean) && validateFilesFlag - ? handleClean - : undefined - } - /> - )} + <React.Fragment> + {header ? ( + <> + {customHeader ? ( + <>{customHeader}</> + ) : ( + <DropzoneHeader + firstClassName={headerClassName} + color={completeAsureColor(color)} + style={styleHeader} + className={classNameHeader} + resetStyles={resetStylesHeader} + borderRadius={styleBorderRadius} + isUploading={isUploading && uploadingIconHedaer} + onReset={ + !isUploading && deleteFilesHeader ? handleReset : undefined + } + maxFileSize={ + maxFileSize && maxFileSizeHeader ? maxFileSize : undefined + } + maxFiles={ + maxFiles && validFilesCountHeader ? maxFiles : undefined + } + localization={localization} + urlPresent={url !== undefined && uploadFilesHeader} + onUploadStart={ + !autoUpload && !uploadButton + ? () => uploadfiles(localFiles) + : undefined + } + numberOfValidFiles={numberOfValidFiles} + //onClean={autoClean ? undefined : cleanButton ? undefined : onClean} + onClean={ + !cleanFilesHeader || isUploading || cleanButton || autoClean + ? undefined + : (cleanFiles || onClean) && validateFilesFlag + ? handleClean + : undefined + } + /> + )} + </> + ) : ( + <></> + )} + </React.Fragment> <DropzoneChildren label={label} localization={localization}> {children} </DropzoneChildren> - {footer && ( - <DropzoneFooter - accept={accept} - message={isUploading ? localMessage : undefined} - localization={localization} - /> - )} + <React.Fragment> + {customFooter ? ( + <>{customFooter}</> + ) : ( + <> + {footer && ( + <DropzoneFooter + firstClassName={footerClassName} + borderRadius={styleBorderRadius} + accept={accept} + message={isUploading ? localMessage : undefined} + localization={localization} + {...footerConfig} + /> + )} + </> + )} + </React.Fragment> {dropOnLayer && ( <DropLayer open={isDragging} - className={dropLayerClassName} + className={ + !isDragging + ? dropLayerClassName + : `${dropLayerClassName} dropzone-layer-drag` + } onDragLeave={handleDragLeave} onDrop={kamui} + style={{ borderRadius: style?.borderRadius }} /> )} @@ -720,7 +820,10 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => { onChange={handleChangeInput} /> - <DropzoneDisabledLayer open={disabled} /> + <DropzoneDisabledLayer + open={disabled} + className={disabledLayerClassName} + /> </div> {actionButtonsPosition === "bottom" && ( @@ -739,6 +842,7 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => { onClean={handleClean} style={containerStyle} className={containerClassName} + top={false} /> )} </React.Fragment> diff --git a/src/files-ui/components/dropzone/components/dropzone/DropzoneProps.ts b/src/files-ui/components/dropzone/components/dropzone/DropzoneProps.ts index bba187d8ece51787e9865ca9a54691ae258267e3..05b7d5187f8f09fe638ce0081b146e5251803d45 100644 --- a/src/files-ui/components/dropzone/components/dropzone/DropzoneProps.ts +++ b/src/files-ui/components/dropzone/components/dropzone/DropzoneProps.ts @@ -1,4 +1,4 @@ -import { ExtFile, CustomValidateFileResponse, Localization, UploadConfig, UploadResponse } from "../../../../core"; +import { ExtFile, CustomValidateFileResponse, Localization, UploadConfig } from "../../../../core"; import * as React from "react"; import { OverridableComponentProps } from "../../../overridable"; @@ -31,51 +31,6 @@ export interface DropzoneFullProps extends OverridableComponentProps { */ value?: ExtFile[]; - /////////////// STYLING /////////// - /** - * The background color for dropzone container, - * @default 'transparent' - */ - backgroundColor?: string; - /** - * The min height of the container in string format - * If the value is given un number format "px" will be assumed - * @default "190px" - * - * examples: - * "50vh" - * "20%" - * "40em" - * "1rem" - */ - minHeight?: string | number; - /////////////// DISPLAY SETTINGS /////////// - /** - * If false, hides the dropzone footer - * By default is true - */ - footer?: boolean; - /** - * If false, hides the dropzone header - * By default is true - */ - header?: boolean; - /** - * The label to place when no files were selected - */ - label?: string; - /** - * The language to be used in Dropzone labels - * Currently only English, French , Portuguese, Chinnese (traditional and simplyfied), Russian and Spanish are supported - * @default "EN-en" - */ - localization?: Localization; - /** - * If true, will show a ripple everytime - * the user drops files or selects files - */ - disableRipple?: boolean; - /////////////// VALIDATION STAGE /////////////// /** * The max file size allowed in bytes @@ -127,37 +82,6 @@ export interface DropzoneFullProps extends OverridableComponentProps { * This flag will only work if validation is active. */ cleanFiles?: boolean; - ///////// INPUT - /** - * If true, the dropzone component itself will be clickable - * If false, the file dialog will not be opened - * @default true - */ - clickable?: boolean; - /** - * The behaviour when new files are selected or dropped - * @default 'add' - */ - behaviour?: 'add' | 'replace'; - /** - * If `true`, the component is disabled. - * @default false - */ - disabled?: boolean; - /** - * If `true`, the drop operation will be performed in a layer that covers the complete component container. - * @default true - */ - dropOnLayer?: boolean; - /** - * Method for performing specific tasks on drag enter operations - */ - onDragEnter?: (evt: React.DragEvent<HTMLDivElement>) => void; - /** - * Method for performing specific tasks on drag leave operations - */ - onDragLeave?: (evt: React.DragEvent<HTMLDivElement>) => void; - /////////////// UPLOAD STAGE /////////////// /** @@ -180,16 +104,68 @@ export interface DropzoneFullProps extends OverridableComponentProps { * Unlike Onchange, onUploadStart will only return a list of files thta are candidates to be uploaded, * in case they are valid or upload status is "error" */ - onUploadStart?: (files: ExtFile[]) => void; + onUploadStart?: (extFiles: ExtFile[]) => void; /** * This event returns as first aparameter the list of responses for each file following the structure: * responses = [ * {id: <the file id>, serverResponse: the server response} * ] */ - onUploadFinish?: (responses: UploadResponse[]) => void; + onUploadFinish?: (extFiles: ExtFile[]) => void; + /////////////// STYLING /////////// + //borderRadius?: string | number; + /** + * The background color for dropzone container, + * @default 'transparent' + */ + background?: string; + /** + * The min height of the container in string format + * If the value is given un number format "px" will be assumed + * @default "180px" + * + * examples: + * "50vh" + * "20%" + * "40em" + * "1rem" + */ + minHeight?: string | number; + + ///////////////// LABEL /////////////// + /** + * The label to place when no files were selected + */ + label?: string; + + //LOCALIZATION + /** + * The language to be used in Dropzone labels + * @default "EN-en" + */ + localization?: Localization; + + //RIPPLE + /** + * If true, will not show a ripple effect everytime + * the user drops files or clicks the dropzone for selecting files + * @default false + */ + disableRipple?: boolean; + + + /** + * Method for performing specific tasks on drag enter operations + */ + onDragEnter?: (evt: React.DragEvent<HTMLDivElement>) => void; + /** + * Method for performing specific tasks on drag leave operations + */ + onDragLeave?: (evt: React.DragEvent<HTMLDivElement>) => void; + + //ACTION BUTTONS /** * The configuration needed for uploading the files. * When "uploadConfig" is not given or uploadConfig.url is undefined @@ -198,35 +174,70 @@ export interface DropzoneFullProps extends OverridableComponentProps { */ actionButtons?: DropzoneActions; - //advancedConfig?: DropzoneAdvancedConfig; + ///////// DROP LAYER + /** + * If `true`, the drop operation will be performed in a layer that covers the complete component container. + * @default true + */ + dropOnLayer?: boolean; + // HEADER AND FOOTER + /** + * If false, hides the dropzone footer + * @default true + */ + footer?: boolean; + /** + * If false, hides the dropzone header + * @default true + */ + header?: boolean; + /** + * Configuration related to the dropzone header + */ headerConfig?: HeaderConfig; + /** + * Configuration related to the dropzone footer + */ + footerConfig?: FooterConfig; + + //DISABLED + /** + * If `true`, the component is disabled. + * @default false + */ + disabled?: boolean; + //CLICKABLE + /** + * If true, the dropzone component itself will be clickable + * If false, the file dialog will not be opened + * @default true + */ + clickable?: boolean; - footerConfg?: FooterConfig; + // ADD OR REPLACE + /** + * The behaviour when new files are selected or dropped + * @default 'add' + */ + behaviour?: 'add' | 'replace'; } -export type HeaderItems = { +export type HeaderConfig = { + customHeader?: JSX.Element; deleteFiles?: boolean; cleanFiles?: boolean; uploadFiles?: boolean; - uploading?: boolean; + uploadingIcon?: boolean; maxFileSize?: boolean; validFilesCount?: boolean; + style?: React.CSSProperties; + className?: string; + resetStyles?: boolean; } -export interface HeaderConfigMap extends OverridableComponentProps { - customHeader?: JSX.Element; -} -export type HeaderConfig = - { - [P in keyof HeaderConfigMap]: HeaderConfigMap[P] - } & { - [H in keyof HeaderItems]: HeaderItems[H] - } -export interface FooterConfigMap extends OverridableComponentProps { - customFooter?: JSX.Element; -} -export type FooterItems = { + +export type FooterConfig = { /** * Allowed types: .png,image/* */ @@ -243,22 +254,19 @@ export type FooterItems = { * */ noMissingFilesLabel?: boolean; -} -export type FooterConfig = - { - [P in keyof FooterConfigMap]: FooterConfigMap[P] - } & { - [H in keyof FooterItems]: FooterItems[H] - } + customMessage?: JSX.Element; + + customFooter?: JSX.Element; +} export type DropzoneActionButton = { - children: React.ReactNode; - label: string; + children?: JSX.Element; + label?: string; style?: React.CSSProperties; className?: string; - resetStyles: boolean; + resetStyles?: boolean; onClick?: Function; } @@ -273,19 +281,6 @@ export interface DropzoneActions { } -export interface AdvancedConfigItem { - style?: React.CSSProperties; - className?: string; -} - -export type DropzoneAdvancedConfig = { - dropzoneLayer: any; - dropzoneContainer: any; - dropzoneLabel: any; -} - - - type DefDivProps = React.HTMLProps<HTMLDivElement>; type DivPropsOmitDropzoneFullProps = Omit<DefDivProps, keyof DropzoneFullProps>; @@ -307,4 +302,21 @@ export const defaultDrozoneProps: DropzoneProps = header: true, footer: true, value: [], -} \ No newline at end of file + //borderRadius: "8px" +} + +export const DEFAULT_BORDER_RADIUS = "8px"; + + +/* +export interface AdvancedConfigItem { + style?: React.CSSProperties; + className?: string; +} + +export type DropzoneAdvancedConfig = { + dropzoneLayer: any; + dropzoneContainer: any; + dropzoneLabel: any; +} + */ \ No newline at end of file diff --git a/src/files-ui/components/dropzone/useDropzoneClassName.ts b/src/files-ui/components/dropzone/useDropzoneClassName.ts index 9e1dad21605547605eba2dcb991f58d2598b9868..fdc340d67a46f6526b99a74995aff39c1f5f7c5d 100644 --- a/src/files-ui/components/dropzone/useDropzoneClassName.ts +++ b/src/files-ui/components/dropzone/useDropzoneClassName.ts @@ -1,42 +1,59 @@ -import { DynamicSheet, DynamiCSS } from "@dynamicss/dynamicss"; +import { DynamicSheet, DynamicSheetRule, DynamiCSS } from "@dynamicss/dynamicss"; import * as React from "react"; -import { asureColor, colourNameToHex, hexColorToRGB } from "../../core"; +import { completeAsureColor } from "../../core"; +import { DEFAULT_BORDER_RADIUS } from "./components/dropzone/DropzoneProps"; export default function useDropzoneClassName( + dropzoneId: string, className: string | undefined, - isDragging: boolean, - header: boolean | undefined = false, - footer: boolean | undefined = false, + //isDragging: boolean, + //header: boolean | undefined = false, + //footer: boolean | undefined = false, color: string | undefined, - backgroundColor: string | undefined, + //borderRadius: string | number | undefined, + background: string | undefined, minHeight: string | number | undefined -): string | undefined { - //console.log("useDropzoneClassName", className, isDragging, header, footer, color, backgroundColor, minHeight); +): [string | undefined, string | undefined, string | undefined, string | undefined] { + //console.log("useDropzoneClassName", className, isDragging, header, footer, color, background, minHeight); + const finalDropzoneId: string = (color === undefined && background === undefined && minHeight === undefined) ? "default" : dropzoneId.replaceAll(":", "_"); + const baseClassName: string = "fui-dropzone-root fui-dropzone-border"; + const [idStyles, setIdStyles] = React.useState<string>(""); const [styleInjected, setStyleInjected] = React.useState<boolean>(false); const [finalClassName, setFinalClassName] = React.useState<string | undefined>(undefined); + const [finalClassNameHeader, setFinalClassNameHeader] = React.useState<string | undefined>(undefined); + const [finalClassNameFooter, setFinalClassNameFooter] = React.useState<string | undefined>(undefined); + const [finalClassNameDisabled, setFinalClassNameDisabled] = React.useState<string | undefined>(undefined); - const [offset, setOffset] = React.useState<number>(0); + //const [offset, setOffset] = React.useState<number>(0); const makeClassName = ( className: string | undefined, - isDragging: boolean, + //isDragging: boolean, // offset: number, color: string | undefined, - backgroundColor: string | undefined, + //borderRadius: string | number | undefined, + background: string | undefined, minHeight: string | number | undefined ) => { let finalClassName: string = baseClassName; + // better to come back to the custom stylesheet for each dropzone with the unique id + let styleSheet: DynamicSheet = makeDynamicDropzoneStyleSheet( + finalDropzoneId, // offset, + //isDragging, color, - backgroundColor, - minHeight + background, + minHeight, + //borderRadius ); let idStyle: string = ""; + + if (!styleInjected) { idStyle = DynamiCSS.insertStyleSheet(styleSheet); setIdStyles(idStyle); @@ -47,57 +64,105 @@ export default function useDropzoneClassName( DynamiCSS.editStyleSheet(idStyles, styleSheet.sheetRules || []); } - finalClassName += ` files-ui-dropzone-extra`; + finalClassName += ` files-ui-dropzone-extra-${finalDropzoneId}`; + if (className) { finalClassName = `${finalClassName} ${className}`; } - if (isDragging) { + /* if (isDragging) { finalClassName = `${finalClassName} fui-hide-border`; - } + } */ + setFinalClassName(finalClassName); + setFinalClassNameHeader(`files-ui-header-border-rd-${finalDropzoneId}`); + setFinalClassNameFooter(`files-ui-footer-border-rd-top-bg-color-${finalDropzoneId}`); + setFinalClassNameDisabled(`files-ui-disabled-layer-color-${finalDropzoneId}`); } React.useEffect(() => { - makeClassName(className, isDragging, + makeClassName(className, + //isDragging, //offset, - color, backgroundColor, minHeight); + color, + // borderRadius, + background, minHeight); // eslint-disable-next-line - }, [className, isDragging, + }, [className, + //isDragging, // offset, - color, backgroundColor, minHeight]); - - React.useEffect(() => { - setOffset(header && footer ? 50 : !header && footer ? 23 : header && !footer ? 22 : 0); - }, [header, footer]); - - return finalClassName; + color, + //borderRadius, + background, minHeight]); + return [finalClassName, finalClassNameHeader, finalClassNameFooter, finalClassNameDisabled]; } const makeDynamicDropzoneStyleSheet = ( + dropzoneId: string, // offset: number, + //isDragging: boolean, color: string | undefined, - backgroundColor: string | undefined, - minHeight: string | number | undefined + background: string | undefined, + minHeight: string | number | undefined, + // borderRadius: string | number | undefined, ): DynamicSheet => { + + const rootColorBorderStyle: DynamicSheetRule = { + className: `files-ui-dropzone-extra-${dropzoneId}`, + rules: { + color: completeAsureColor(color), + border: `1px dashed ${completeAsureColor(color)}`, + borderRadius: DEFAULT_BORDER_RADIUS, + background: background, + minHeight: typeof minHeight === "number" ? `${minHeight}px` : minHeight, + }, + }; + + const rootColorBorderStyleHideBorder: DynamicSheetRule = { + className: `files-ui-root-border-hide`, + rules: { + borderColor: "transparent", + }, + } + const headerBorderStyle: DynamicSheetRule = { + className: `files-ui-header-border-rd-${dropzoneId}`, + rules: { + "border-top-left-radius": DEFAULT_BORDER_RADIUS, + "border-top-right-radius": DEFAULT_BORDER_RADIUS, + }, + }; + const footerBorderStyle: DynamicSheetRule = { + className: `files-ui-footer-border-rd-top-bg-color-${dropzoneId}`, + rules: { + "border-bottom-left-radius": DEFAULT_BORDER_RADIUS, + "border-bottom-right-radius": DEFAULT_BORDER_RADIUS, + background: completeAsureColor(color, 0.129), + borderTop: `1px dotted ${completeAsureColor(color)}` + + }, + }; + const disabledLayerStyle: DynamicSheetRule = { + className: `files-ui-disabled-layer-color-${dropzoneId}`, + rules: { + borderRadius: DEFAULT_BORDER_RADIUS, + background: completeAsureColor(color, 0.38), + } + } + const sheetRules: DynamicSheetRule[] = + [ + rootColorBorderStyle, + rootColorBorderStyleHideBorder, + headerBorderStyle, + footerBorderStyle, + disabledLayerStyle + ]; + + return { - id: "files-dropzone-ui-style-id", - sheetRules: [ - { - className: `files-ui-dropzone-extra`, - rules: { - border: `1px dashed ${hexColorToRGB( - asureColor(colourNameToHex(color)), - 1 - )}`, - borderRadius: "8px", - backgroundColor: backgroundColor, - minHeight: typeof minHeight === "number" ? `${minHeight}px` : minHeight, - }, - } - ], + id: "files-dropzone-ui-style-id-" + dropzoneId, + sheetRules: sheetRules } } \ No newline at end of file diff --git a/src/files-ui/components/file-input-button/FileInputButton.tsx b/src/files-ui/components/file-input-button/FileInputButton.tsx index efc624b4cedba0abc07b40b019f2e53b5b16c23e..8f3b1d1639fb39dd30d8716a6075aee2b402abc0 100644 --- a/src/files-ui/components/file-input-button/FileInputButton.tsx +++ b/src/files-ui/components/file-input-button/FileInputButton.tsx @@ -9,14 +9,18 @@ import { fakeFuiUpload, fileListToExtFileArray, FileValidatorProps, + getRandomInt, //FunctionLabel, handleClickInput, instantPreparingToUploadOne, + isUploadAbleExtFile, isValidateActive, LocalLabels, + sanitizeArrExtFile, sleepPreparing, sleepTransition, toUploadableExtFileList, + unexpectedErrorUploadResult, UploadConfig, uploadExtFile, UploadResponse, @@ -134,36 +138,48 @@ const FileInputButton: React.FC<FileInputButtonProps> = ( * @returns nothing */ const uploadfiles = async (localFiles: ExtFile[]): Promise<void> => { - if (!url) return; - + //set uploading flag to true setIsUploading(true); - print_manager(localFiles, "start"); - // set flag to true - // recieve on the new list - // initialize new list of ExtFileInstances - let arrOfExtFilesInstances: ExtFileInstance[] = []; + //avoid to call upload if not allowed - if (isUploading || localFiles.length === 0 || !arrOfExtFilesInstances) { + // flag is already true or there isnt files + //url was not provided + if (isUploading || localFiles.length === 0 || !url) { + setIsUploading(false); return; } + // initialize a new list of ExtFileInstances + let arrOfExtFilesInstances: ExtFileInstance[] = []; const totalNumber: number = localFiles.length; - const missingUpload: number = localFiles.filter((x: ExtFile) => { - return ( - (!validateFilesFlag || (validateFilesFlag && x.valid)) && - x.uploadStatus !== "success" - ); - }).length; + console.log("upload start: totalNumber", totalNumber); + + const missingUpload: number = localFiles.filter((extFile: ExtFile) => + isUploadAbleExtFile(extFile, validateFilesFlag) + ).length; + + console.log("upload start: missingUpload", missingUpload); let totalRejected: number = 0; let currentCountUpload: number = 0; + /* const uploadingMessenger: FunctionLabel = + DropzoneLocalizer.uploadingMessage as FunctionLabel; */ + + //no missing to upload + if (!(missingUpload > 0)) { + console.log("upload start: noFilesMessage", missingUpload); + + //setLocalMessage(DropzoneLocalizer.noFilesMessage as string); + setIsUploading(false); + return; + } + + //setLocalMessage(uploadingMessenger(`${missingUpload}/${totalNumber}`)); // setIsUploading(true); //PREPARING stage - //use methods to update on static class - //obtain a fresher dui file list - console.log("***** before setFileListMapPreparing"); - console.table(localFiles); + console.log("validateFilesFlag", validateFilesFlag); + arrOfExtFilesInstances = ExtFileManager.setFileListMapPreparing( inputButtonId, @@ -172,8 +188,6 @@ const FileInputButton: React.FC<FileInputButtonProps> = ( cleanOnUpload as boolean ) || []; - console.log("***** FileManagerLog setFileListMapPreparing"); - console.table(arrOfExtFilesInstances); const newExtFileLocal: ExtFile[] = [...arrOfExtFilesInstances].map((x) => x.toExtFile() ); @@ -191,100 +205,135 @@ const FileInputButton: React.FC<FileInputButtonProps> = ( //AWAIT when preparing time is given //general sleep for all files await sleepPreparing(preparingTime); - // workaround for preventing getting the uploadStatus as undefined - /* arrOfExtFilesInstances.forEach((F) => { - F.uploadStatus = "preparing"; - }); */ - //variable for storing responses - //console.log("uploadfiles after sleep response",response); + console.log("FileManagerLog after sleep", arrOfExtFilesInstances); //return; - let serverResponses: Array<UploadResponse> = []; + let serverResponses: Array<ExtFile> = []; for (let i = 0; i < arrOfExtFilesInstances.length; i++) { const currentExtFileInstance: ExtFileInstance = arrOfExtFilesInstances[i]; + console.log( "FileManagerLog currentExtFileInstance " + i, currentExtFileInstance ); - if (currentExtFileInstance.uploadStatus === "preparing") { + if ( + currentExtFileInstance.uploadStatus === "preparing" && + !currentExtFileInstance.extraData?.deleted + ) { //set stage to "uploading" in one file and notify change // PREPARING => UPLOADING + await sleepTransition(); + instantPreparingToUploadOne(currentExtFileInstance); - /* setLocalMessage( - uploadingMessenger(`${++currentCountUpload}/${missingUpload}`) - ); */ - //CHANGE - handleFilesChange([...arrOfExtFilesInstances], true); + + //messge in footer + /* setLocalMessage( + uploadingMessenger(`${++currentCountUpload}/${missingUpload}`) + ); */ + + //CHANGE FILES + handleFilesChange(sanitizeArrExtFile(arrOfExtFilesInstances), true); //UPLOADING => UPLOAD() //upload one file and notify about change - let uploadResponse: UploadResponse; - try { - uploadResponse = fakeUpload - ? await fakeFuiUpload(currentExtFileInstance, DropzoneLocalizer) - : await uploadExtFile( - currentExtFileInstance, - url, - method, - headers, - uploadLabel - ); - } catch (error) { - uploadResponse = { - id: currentExtFileInstance.id, - serverResponse: { - success: false, - message: "Error on upload: unexpected error " + error, - payload: {}, - }, - uploadedFile: { ...currentExtFileInstance }, - }; + let uploadResponse: ExtFile; + + if (fakeUpload) { + uploadResponse = await fakeFuiUpload( + currentExtFileInstance, + DropzoneLocalizer + ); + + let fakeProgress = 0; + while (fakeProgress < 100) { + fakeProgress += getRandomInt(21, 35); + currentExtFileInstance.progress = + fakeProgress > 100 ? 100 : fakeProgress; + await sleepTransition(1000); + handleFilesChange(sanitizeArrExtFile(arrOfExtFilesInstances), true); + } + } else { + try { + uploadResponse = await uploadExtFile( + currentExtFileInstance, + url, + method, + headers, + uploadLabel + ); + } catch (error) { + uploadResponse = unexpectedErrorUploadResult( + currentExtFileInstance.toExtFile() + ); + } } - const { uploadedFile } = uploadResponse; + const uploadedFile = uploadResponse; + console.log("fake uploadResponse uploadedFile", uploadedFile); + //update instances currentExtFileInstance.uploadStatus = uploadedFile.uploadStatus; currentExtFileInstance.uploadMessage = uploadedFile.uploadMessage; - //add fake progress only on fakeupload - if (fakeUpload) { - console.log( - "Adding fake progress", - fakeUpload, - uploadedFile.progress - ); - currentExtFileInstance.progress = uploadedFile.progress; - } + console.log( + "fake uploadResponse currentExtFileInstance", + currentExtFileInstance + ); + console.log( + "fake uploadResponse currentExtFileInstance", + currentExtFileInstance.uploadStatus + ); + console.log( + "fake uploadResponse currentExtFileInstance", + currentExtFileInstance.uploadMessage + ); + + console.log( + "pre sanitizeArrExtFile", + arrOfExtFilesInstances.map((F) => { + return { status: F.uploadStatus, message: F.uploadMessage }; + }) + ); + //CHANGE if (!(currentExtFileInstance.uploadStatus === "aborted")) await sleepTransition(); - handleFilesChange( - arrOfExtFilesInstances.map((x: ExtFileInstance) => x.toExtFile()), - true + console.log( + "pre sanitizeArrExtFile", + arrOfExtFilesInstances.map((F) => { + return { status: F.uploadStatus, message: F.uploadMessage }; + }) ); + handleFilesChange(sanitizeArrExtFile(arrOfExtFilesInstances), true); + if (uploadedFile.uploadStatus === "error") { totalRejected++; } serverResponses.push(uploadResponse); + } else { + handleFilesChange(sanitizeArrExtFile(arrOfExtFilesInstances), true); } } + handleFilesChange(sanitizeArrExtFile(arrOfExtFilesInstances), true); + // upload group finished :D onUploadFinish?.(serverResponses); - - /* const finishUploadMessenger: FunctionLabel = + /* + const finishUploadMessenger: FunctionLabel = DropzoneLocalizer.uploadFinished as FunctionLabel; - - setLocalMessage( + setLocalMessage( finishUploadMessenger(missingUpload - totalRejected, totalRejected) ); */ - setIsUploading(false); + setTimeout(() => { + setIsUploading(false); + }, 2000); }; /** * Performs the changes in the FuiFile list. diff --git a/src/files-ui/components/file-input-button/InputButtonProps.ts b/src/files-ui/components/file-input-button/InputButtonProps.ts index fd729e59784273c8159c229f971653e987c53895..fe4b1e7166b9550bbf9e1d0b4e7d33f153f1c268 100644 --- a/src/files-ui/components/file-input-button/InputButtonProps.ts +++ b/src/files-ui/components/file-input-button/InputButtonProps.ts @@ -44,8 +44,8 @@ interface InputButtonFullProps { /////////////// VALIDATION STAGE /////////////// /** - * The max file size allowed in bytes - */ + * The max file size allowed in bytes + */ maxFileSize?: number; /** * The max number of files to be accepted. @@ -74,24 +74,21 @@ interface InputButtonFullProps { * ``` */ validator?: (f: File) => CustomValidateFileResponse; + /** - * Flag that indicates that dropzone will automatically remove non valid files. - * This will happen every time user drops files or selects files from file dialog. - * This flag will only work if validation is active. - */ + * Flag that indicates that dropzone will automatically remove non valid files. + * This will happen every time user drops files or selects files from file dialog. + * This flag will only work if validation is active. + */ autoClean?: boolean; - /** - * The behaviour when new files are selected or dropped - * @default 'add' - */ - behaviour?: 'add' | 'replace'; + /////////////// UPLOAD STAGE /////////////// /** - * The configuration needed for uploading the files. - * When "uploadConfig" is not given or uploadConfig.url is undefined - * the upload button will not be visible - * and uploadOnDrop flag will not work - */ + * The configuration needed for uploading the files. + * When "uploadConfig" is not given or uploadConfig.url is undefined + * the upload button will not be visible + * and uploadOnDrop flag will not work + */ uploadConfig?: UploadConfig; /** * Flag that indicates Dropzone to perform a fake upload process. @@ -101,29 +98,34 @@ interface InputButtonFullProps { */ fakeUpload?: boolean; /** - * This event is triggered when upload process starts - * also returns the list of files that will be uploaded, - * Unlike Onchange, onUploadStart will only return a list of files thta are candidates to be uploaded, - * in case they are valid or upload status is "error" - */ - onUploadStart?: (files: ExtFile[]) => void; + * This event is triggered when upload process starts + * also returns the list of files that will be uploaded, + * Unlike Onchange, onUploadStart will only return a list of files thta are candidates to be uploaded, + * in case they are valid or upload status is "error" + */ + onUploadStart?: (extFiles: ExtFile[]) => void; /** * This event returns as first aparameter the list of responses for each file following the structure: * responses = [ * {id: <the file id>, serverResponse: the server response} * ] */ - onUploadFinish?: (responses: UploadResponse[]) => void; - - + onUploadFinish?: (extFiles: ExtFile[]) => void; - /** - * The configuration needed for uploading the files. - * When "uploadConfig" is not given or uploadConfig.url is undefined - * the upload button will not be visible - * and uploadOnDrop flag will not work - */ - actionButtons?: DropzoneActions; + //ACTION BUTTONS + /** + * The configuration needed for uploading the files. + * When "uploadConfig" is not given or uploadConfig.url is undefined + * the upload button will not be visible + * and uploadOnDrop flag will not work + */ + actionButtons?: DropzoneActions; + // ADD OR REPLACE + /** + * The behaviour when new files are selected or dropped + * @default 'add' + */ + behaviour?: 'add' | 'replace'; } type MaterialButtonPropsOmitInputButtonFullProps = Omit<MaterialButtonProps, keyof InputButtonFullProps>; diff --git a/src/files-ui/components/loader/DefaultLoader/DefaultLoader.tsx b/src/files-ui/components/loader/DefaultLoader/DefaultLoader.tsx index 891190f30e43bbe475b8583fe79fa6ad2503f794..b9668fd5bb691feef4068079c18c18a487795db5 100644 --- a/src/files-ui/components/loader/DefaultLoader/DefaultLoader.tsx +++ b/src/files-ui/components/loader/DefaultLoader/DefaultLoader.tsx @@ -1,19 +1,17 @@ - - import * as React from "react"; -import { asureColor, colourNameToHex, hexColorToRGB } from "../../../core/color"; +import { completeAsureColor } from "../../../core"; import "./DefaultLoader.scss"; interface DefaultLoaderNeoProps { color?: string; label?: string; } const makeDefaultLoaderNeoColor = (color?: string): string => { - return hexColorToRGB(asureColor(colourNameToHex(color)), 0.5); + return completeAsureColor(color, 0.5); }; const DefaultLoader: React.FC<DefaultLoaderNeoProps> = ( props: DefaultLoaderNeoProps ) => { - const { color = "#8b6b10",label } = props; + const { color = "#8b6b10", label } = props; //console.log("color label loader", color,label); return ( <svg @@ -130,7 +128,7 @@ const DefaultLoader: React.FC<DefaultLoaderNeoProps> = ( y={`90`} fill={`${color}`} > - {label ||`loading`} + {label || `loading`} </text> </svg> ); diff --git a/src/files-ui/components/loader/DynamicLoader/DynamicLoader.tsx b/src/files-ui/components/loader/DynamicLoader/DynamicLoader.tsx index 3a3d2109e55d2714c656bd89549c08ec1151e6d9..91c3282d1384c340a4997a062aa54615216e9dfd 100644 --- a/src/files-ui/components/loader/DynamicLoader/DynamicLoader.tsx +++ b/src/files-ui/components/loader/DynamicLoader/DynamicLoader.tsx @@ -75,59 +75,61 @@ const DynamicLoader: React.FC<DynamicLoaderProps> = ( if (percentage !== undefined) return ( <LoaderContainer size={size}> - <svg - className="dui_svg_circle_loader" - xmlns="http://www.w3.org/2000/svg" - xmlnsXlink="http://www.w3.org/1999/xlink" - width={`${finalSize}px`} - height={`${finalSize}px`} - style={finalStyle} - > - <circle - style={{ - transform: "rotate(-90deg)", - transformOrigin: "center", - }} - stroke={color || "#14ff00"} - cx={`${finalX}`} - cy={`${finalY}`} - r={`${finalRadius}`} - strokeWidth={`${width || 8}px`} - //className="circle_loader" - id="circle" - ref={circleRef} - fill="none" - ></circle> + <> + <svg + className="dui_svg_circle_loader" + xmlns="http://www.w3.org/2000/svg" + xmlnsXlink="http://www.w3.org/1999/xlink" + width={`${finalSize}px`} + height={`${finalSize}px`} + style={finalStyle} + > + <circle + style={{ + transform: "rotate(-90deg)", + transformOrigin: "center", + }} + stroke={color || "#14ff00"} + cx={`${finalX}`} + cy={`${finalY}`} + r={`${finalRadius}`} + strokeWidth={`${width || 8}px`} + //className="circle_loader" + id="circle" + ref={circleRef} + fill="none" + ></circle> - {!hidePerncentage && percentage !== undefined && ( - <text - className="dui-text-dynamic-loader" - x={`${finalX}`} - y={`${(finalX * 7) / 6}`} + {!hidePerncentage && percentage !== undefined && ( + <text + className="dui-text-dynamic-loader" + x={`${finalX}`} + y={`${(finalX * 7) / 6}`} + > + {`${percentage.toFixed(0)} %`} + </text> + )} + </svg> + {onClick && ( + <div + style={{ + position: "absolute", + width: "100%", + height: "100%", + display: "flex", + alignItems: "center", + justifyContent: "center", + }} > - {`${percentage.toFixed(0)} %`} - </text> + <Clear + color={"rgba(255,255,255,0.75)"} + size={45} + onClick={onClick} + //colorFill="transparent" + /> + </div> )} - </svg> - {onClick && ( - <div - style={{ - position: "absolute", - width: "100%", - height: "100%", - display: "flex", - alignItems: "center", - justifyContent: "center", - }} - > - <Clear - color={"rgba(255,255,255,0.75)"} - size={45} - onClick={onClick} - //colorFill="transparent" - /> - </div> - )} + </> </LoaderContainer> ); else { diff --git a/src/files-ui/components/loader/InfiniteLoader/InfiniteLoader.tsx b/src/files-ui/components/loader/InfiniteLoader/InfiniteLoader.tsx index 876172fb5b421ca0ffaf6df1b8aa6652294b7729..bafb3ca9570bb60ae86a85ea73ad5eb8bce7c07e 100644 --- a/src/files-ui/components/loader/InfiniteLoader/InfiniteLoader.tsx +++ b/src/files-ui/components/loader/InfiniteLoader/InfiniteLoader.tsx @@ -14,26 +14,28 @@ const InfiniteLoader: React.FC<InfiniteLoaderProps> = ( } = props; return ( <LoaderContainer onClick={onClick} size={size}> - <BasePreparingLoader size={size} /> - <div - style={{ - position: "absolute", - width: "100%", - height: "100%", - display: "flex", - alignItems: "center", - justifyContent: "center", - }} - > - {onClick && ( - <Clear - color={"rgba(255,255,255,0.75)"} - size={45} - onClick={onClick} - //colorFill="transparent" - /> - )} - </div> + <> + <BasePreparingLoader size={size} /> + <div + style={{ + position: "absolute", + width: "100%", + height: "100%", + display: "flex", + alignItems: "center", + justifyContent: "center", + }} + > + {onClick && ( + <Clear + color={"rgba(255,255,255,0.75)"} + size={45} + onClick={onClick} + //colorFill="transparent" + /> + )} + </div> + </> </LoaderContainer> ); }; diff --git a/src/files-ui/components/material-button/utils/MaterialButtonStyleManager.ts b/src/files-ui/components/material-button/utils/MaterialButtonStyleManager.ts index 0114833574e9c9f76098297d7d300795f77b768c..c9c8876a3b95db0e33e301986539ed57481db5da 100644 --- a/src/files-ui/components/material-button/utils/MaterialButtonStyleManager.ts +++ b/src/files-ui/components/material-button/utils/MaterialButtonStyleManager.ts @@ -1,4 +1,4 @@ -import { asureColor, colourNameToHex, darkerColor, hexColorToRGB } from "../../../core"; +import { asureColor, colourNameToHex, completeAsureColor, darkerColor } from "../../../core"; import { DynamicSheet, DynamicSheetRule } from "@dynamicss/dynamicss"; export default class MaterialButtonStyleManager { @@ -55,58 +55,40 @@ export default class MaterialButtonStyleManager { case "contained": sheetRules[0].rules = { color: asureColor(colourNameToHex(textColor)), - backgroundColor: hexColorToRGB( - asureColor(colourNameToHex(color)), - 1 - ), + backgroundColor: completeAsureColor(color), textDecoration: textDecoration }; sheetRules[1].rules = { ":hover": { backgroundColor: darkerColor( - hexColorToRGB( - asureColor(colourNameToHex(color)), - 1 - ) + completeAsureColor(color) ), }, }; break; case "outlined": sheetRules[0].rules = { - border: `1px solid ${hexColorToRGB( - asureColor(colourNameToHex(color)), - 0.5 - )}`, - color: asureColor(colourNameToHex(color)), + border: `1px solid ${completeAsureColor(color, 0.5)}`, + color: completeAsureColor(color), backgroundColor: "transparent", textDecoration: textDecoration }; sheetRules[1].rules = { ":hover": { - border: `1px solid ${hexColorToRGB( - asureColor(colourNameToHex(color)), - 1 - )}`, - backgroundColor: hexColorToRGB( - asureColor(colourNameToHex(color)), - 0.085 - ), + border: `1px solid ${completeAsureColor(color, 1)}`, + backgroundColor: completeAsureColor(color, 0.085), }, }; break; case "text": sheetRules[0].rules = { - color: asureColor(colourNameToHex(color)), + color: completeAsureColor(color), backgroundColor: "transparent", textDecoration: textDecoration }; sheetRules[1].rules = { ":hover": { - backgroundColor: hexColorToRGB( - asureColor(colourNameToHex(color)), - 0.085 - ), + backgroundColor: completeAsureColor(color, 0.085), }, }; break; diff --git a/src/files-ui/components/previews/FullScreen/FullScreen.tsx b/src/files-ui/components/previews/FullScreen/FullScreen.tsx index 07e6dedb4966291888825cdc969f19c96d7942e7..f30d5ff9f4960134f0fc4e9446ddd6efec2447db 100644 --- a/src/files-ui/components/previews/FullScreen/FullScreen.tsx +++ b/src/files-ui/components/previews/FullScreen/FullScreen.tsx @@ -17,13 +17,12 @@ const FullScreen: React.FC<FullScreenProps> = (props: FullScreenProps) => { if (evt.key === "Escape") onClose?.(); }; console.log("adding listener"); - document.addEventListener("keydown", handleCloseEsc); - return () => { console.log("removing listener"); document.removeEventListener("keydown", handleCloseEsc); }; + // eslint-disable-next-line }, []); return ( diff --git a/src/files-ui/core/color/colors.ts b/src/files-ui/core/color/colors.ts index 738dfbaa51bfa21e3d1dca845602bcd497dfa596..c0714f1b0d7e56ab4125a0d08e1c27321cd3b3aa 100644 --- a/src/files-ui/core/color/colors.ts +++ b/src/files-ui/core/color/colors.ts @@ -5,7 +5,7 @@ import { NAMED_COLORS } from "./namedColors"; * @param colorInput * @returns the darked color in */ - export const darkerColor = (colorInput: string, percentage = 25): string => { +export const darkerColor = (colorInput: string, percentage = 25): string => { let darkedColor = ""; const reduce = (100 - percentage) / 100; let component1: number = 0; @@ -206,11 +206,26 @@ export const hexTodec = (letter: string): number => { * @param color param color given by user * @returns returns the same color */ - export const asureColor = (color?: string): string => { +export const asureColor = (color?: string): string => { if (color !== undefined && color !== "") { return color; } else { - return "#646c7f"; + return DEFAULT_FONT_COLOR; } } +/** + * Asure a base color. When not given or when given an incorrect color format + * default color is this kind of grey #5d6475 + * + * @param color param color given by user + * @returns returns the same color + */ +export const completeAsureColor = (color?: string, perc = 1): string => { + + return hexColorToRGB(asureColor(colourNameToHex(color)), perc); + +} + + +export const DEFAULT_FONT_COLOR = "#646c7f"; \ No newline at end of file diff --git a/src/files-ui/core/color/index.ts b/src/files-ui/core/color/index.ts index e7974729faf961a211c7b8939a3da9f60c19d043..6b33c40aefc4bddf0d41bb7d19e768b27b46be09 100644 --- a/src/files-ui/core/color/index.ts +++ b/src/files-ui/core/color/index.ts @@ -6,5 +6,6 @@ export { darkerColor, hexColorToRGB, hexTodec, - isHexColor + isHexColor, + completeAsureColor } from "./colors"; \ No newline at end of file diff --git a/src/files-ui/core/index.ts b/src/files-ui/core/index.ts index 0c918711726ce820aca4736f3bf15383de4348f8..32369769054827152abe0cb596200a6893c46d51 100644 --- a/src/files-ui/core/index.ts +++ b/src/files-ui/core/index.ts @@ -163,7 +163,8 @@ export { hexColorToRGB, hexTodec, isHexColor, - NAMED_COLORS + NAMED_COLORS, + completeAsureColor } from "./color"; export { diff --git a/src/files-ui/core/ripple/ripple.ts b/src/files-ui/core/ripple/ripple.ts index d0a24a2787d55c1b952b53b00f43f84b2653f8c8..aa52e4177c1f209af8d7c165c3adb825466cbb47 100644 --- a/src/files-ui/core/ripple/ripple.ts +++ b/src/files-ui/core/ripple/ripple.ts @@ -1,8 +1,7 @@ -import { asureColor, colourNameToHex, hexColorToRGB } from "../color"; +import { completeAsureColor, hexColorToRGB } from "../color"; const asureRippleColor = (color: string): string => { - return hexColorToRGB( - asureColor(colourNameToHex(color)), + return completeAsureColor(color, 0.4 ); } @@ -40,10 +39,10 @@ export function createFuiRippleFromDiv fuiContainerRel.appendChild(circle); //remove trash - /* setTimeout(() => { + setTimeout(() => { fuiContainerAbs.style.display = "none"; circle?.remove(); - }, 501); */ + }, 501); } @@ -69,10 +68,7 @@ export function createRippleButton< circle.classList.add("ripple"); if (variant !== "contained") { - circle.style.backgroundColor = hexColorToRGB( - asureColor(colourNameToHex(color)), - 0.4 - ); + circle.style.backgroundColor = asureRippleColor(color); } else { circle.style.backgroundColor = hexColorToRGB("#ffffff", 0.4); diff --git a/src/files-ui/core/types/ExtFile.ts b/src/files-ui/core/types/ExtFile.ts index 2e840ac26bc270a831cb36312d1f954b790c60c3..73a4cf44b5a80a8be253e0ef345254004d4bbb86 100644 --- a/src/files-ui/core/types/ExtFile.ts +++ b/src/files-ui/core/types/ExtFile.ts @@ -29,7 +29,9 @@ export declare type ExtFile = { * The size of the file. Used mostly for displaying file data from server. */ size?: number; - + /** + * Link, URI or string representation of an image + */ imageUrl?: string; /** * a flag that determines whether the file is valid, not valid or it is not validated. diff --git a/src/files-ui/core/types/uploadTypes.ts b/src/files-ui/core/types/uploadTypes.ts index 82a709dae94e017ca9bb724be21eecd304d58870..4c5f4b2482d015e187d630e9c62dc572ebcadf49 100644 --- a/src/files-ui/core/types/uploadTypes.ts +++ b/src/files-ui/core/types/uploadTypes.ts @@ -18,7 +18,7 @@ export declare type UploadResponse = { } export type ServerResponse = { success: boolean; - message: string; - payload: any; + message?: string; + payload?: any; } diff --git a/src/files-ui/core/upload/errors.upload.ts b/src/files-ui/core/upload/errors.upload.ts index d4b3b00b7dfe16cc22d9acf106a3ea1a635ac6e4..fa7da54598ebfbb1e9f169e09bfe13eebbe5855f 100644 --- a/src/files-ui/core/upload/errors.upload.ts +++ b/src/files-ui/core/upload/errors.upload.ts @@ -22,16 +22,13 @@ export const UNEXPECTED_ERROR_RESPONSE = { payload: {} } -export const NO_XHR_PROVIDED_ERROR = (extFile: ExtFile): UploadResponse => { +export const NO_XHR_PROVIDED_ERROR = (extFile: ExtFile): ExtFile => { return { - uploadedFile: - { - ...extFile, - uploadMessage: "Unable to upload. xhr object was not provided", - uploadStatus: "error" - }, - id: extFile.id, - serverResponse: {} + ...extFile, + uploadMessage: "Unable to upload. xhr object was not provided", + uploadStatus: "error", + + serverResponse: { success: false, } } } \ No newline at end of file diff --git a/src/files-ui/core/upload/response.upload.ts b/src/files-ui/core/upload/response.upload.ts index 4d7eee8eeea7422163956a3220231dad735cd3bf..32707c4cbb64d443e652cdb4753fb738c6407eb3 100644 --- a/src/files-ui/core/upload/response.upload.ts +++ b/src/files-ui/core/upload/response.upload.ts @@ -29,16 +29,13 @@ export const JsonParseResponse = (xhr: XMLHttpRequest): ServerResponse => { export const makeSuccessUploadResponse = ( extFile: ExtFile, responseFui: ServerResponse -): UploadResponse => { +): ExtFile => { return { - id: extFile.id, + ...extFile, serverResponse: responseFui, - uploadedFile: - { - ...extFile, - uploadMessage: responseFui.message, - uploadStatus: "success" - }, + uploadMessage: responseFui.message, + uploadStatus: "success" + } } @@ -46,17 +43,13 @@ export const makeSuccessUploadResponse = ( export const makeErrorUploadResponse = ( extFile: ExtFile, responseFui: ServerResponse -): UploadResponse => { +): ExtFile => { console.log("makeErrorUploadResponse", extFile, responseFui); return { - id: extFile.id, + ...extFile, + uploadMessage: responseFui.message, + uploadStatus: "error", serverResponse: responseFui, - uploadedFile: - { - ...extFile, - uploadMessage: responseFui.message, - uploadStatus: "error" - }, } } diff --git a/src/files-ui/core/upload/upload.ts b/src/files-ui/core/upload/upload.ts index 236c2fbf019f4e983ee8a79ce3f8663f3f5bf249..10627038cb4af3c0c5eaf9a4d960a0b6ff55e7dd 100644 --- a/src/files-ui/core/upload/upload.ts +++ b/src/files-ui/core/upload/upload.ts @@ -100,7 +100,7 @@ export const uploadExtFile = async ( method?: Method, headers?: Record<string, string>, uploadLabel?: string, -): Promise<UploadResponse> => { +): Promise<ExtFile> => { return new Promise(async (resolve, reject) => { try { const uploader: XMLHttpRequest | undefined = extFile.xhr; diff --git a/src/files-ui/core/upload/utils.upload.ts b/src/files-ui/core/upload/utils.upload.ts index 316ab7f810b118085dca7b860dcc21a4a61c0e49..727caa94639d86df6f81e4354cad186f9c788b8b 100644 --- a/src/files-ui/core/upload/utils.upload.ts +++ b/src/files-ui/core/upload/utils.upload.ts @@ -1,15 +1,11 @@ import { ExtFile, ExtFileInstance, ServerResponse, UploadResponse, UPLOADSTATUS } from "../types" -export const unexpectedErrorUploadResult = (extFile: ExtFile): UploadResponse => { +export const unexpectedErrorUploadResult = (extFile: ExtFile): ExtFile => { return { - id: extFile.id, - uploadedFile: - { - ...extFile, - uploadMessage: "Unexpected error", - uploadStatus: "error" - }, + ...extFile, + uploadMessage: "Unexpected error", + uploadStatus: "error", serverResponse: { success: false, message: "Error on upload: unexpected error ", diff --git a/src/files-ui/core/utils/addClassName.ts b/src/files-ui/core/utils/addClassName.ts index 9e3b35af67a686768a38a64ba2fd69955fb3a053..972878e5a005f6cb2a03aa78b3a592673aca3aaf 100644 --- a/src/files-ui/core/utils/addClassName.ts +++ b/src/files-ui/core/utils/addClassName.ts @@ -8,9 +8,7 @@ export const addClassName = ( baseClassName: string, className: string | undefined ): string => { - let result: string = baseClassName; - if (className) { - result = `${result} ${className}`; - } - return result; + if (className) + return `${baseClassName} ${className}`; + return baseClassName; } \ No newline at end of file diff --git a/src/files-ui/core/utils/dragdrop.utils.ts b/src/files-ui/core/utils/dragdrop.utils.ts index 731bcbbcd90753871e48c15e524edfee8cc3e4b7..9a04abbf7d2f10d496447768ed64c7b553f71d51 100644 --- a/src/files-ui/core/utils/dragdrop.utils.ts +++ b/src/files-ui/core/utils/dragdrop.utils.ts @@ -2,20 +2,20 @@ * Performs stopPropagation and preventDefault functions on an drop event instance * @param evt drag event handler object */ - export const handleDropUtil: React.DragEventHandler<HTMLDivElement> = ( - evt: React.DragEvent<HTMLDivElement> - ) => { - evt.stopPropagation(); - evt.preventDefault(); - }; - /** - * Performs stopPropagation and preventDefault functions on an drop event instance - * and also specifies that the drop effect is link - * @param evt drag event handler object - */ - export const handleDragUtil: React.DragEventHandler<HTMLDivElement> = ( - evt: React.DragEvent<HTMLDivElement> - ) => { - handleDropUtil(evt); - evt.dataTransfer.dropEffect = "link"; - }; \ No newline at end of file +export const handleDropUtil: React.DragEventHandler<HTMLDivElement> = ( + evt: React.DragEvent<HTMLDivElement> +) => { + evt.stopPropagation(); + evt.preventDefault(); +}; +/** + * Performs stopPropagation and preventDefault functions on an drop event instance + * and also specifies that the drop effect is link + * @param evt drag event handler object + */ +export const handleDragUtil: React.DragEventHandler<HTMLDivElement> = ( + evt: React.DragEvent<HTMLDivElement> +) => { + + evt.dataTransfer.dropEffect = "link"; handleDropUtil(evt); +}; \ No newline at end of file diff --git a/src/files-ui/core/utils/fakeupload.utils.ts b/src/files-ui/core/utils/fakeupload.utils.ts index 54d20f3934178967237087a3cb09bb90efa4f8a4..13468845d5df878277419789d543d55149003639 100644 --- a/src/files-ui/core/utils/fakeupload.utils.ts +++ b/src/files-ui/core/utils/fakeupload.utils.ts @@ -108,7 +108,51 @@ export const uploadOneExtFile = ( export const fakeFuiUpload = ( extFileInstance: ExtFileInstance, DropzoneLocalizer = DropzoneLocalizerSelector("EN-en") +): Promise<ExtFile> => { + + const extFile:ExtFile = extFileInstance.toExtFile(); + + return new Promise((resolve, reject) => { + setTimeout(() => { + const randomNumber: number = Math.floor(Math.random() * 10); + if (randomNumber % 2 === 0) { + const success = true; + const message = DropzoneLocalizer.fakeuploadsuccess as string; + const payload = { url: "" }; + resolve({ + ...extFile, + serverResponse: { success, message, payload }, + uploadStatus: "success", + uploadMessage: message, + + }); + } else { + const success = false; + const message = DropzoneLocalizer.fakeUploadError as string; + const payload = {}; + resolve({ + ...extFile, + serverResponse: { success, message, payload }, + uploadStatus: "error", + uploadMessage: message, + }); + } + }, 1700); + }); +}; + + +/** + * + * @param extFile the extFile to upload + * @param DropzoneLocalizer the localization + * @returns a duiUploadResponse object that describes the result + */ +export const fakeFuiUploadExtFile = ( + extFileInstance: ExtFileInstance, + DropzoneLocalizer = DropzoneLocalizerSelector("EN-en") ): Promise<UploadResponse> => { + const extFile:ExtFile = extFileInstance.toExtFile(); return new Promise((resolve, reject) => { @@ -118,6 +162,7 @@ export const fakeFuiUpload = ( const status = true; const message = DropzoneLocalizer.fakeuploadsuccess as string; const payload = { url: "" }; + resolve({ id: extFile.id, serverResponse: { status, message, payload }, @@ -147,4 +192,3 @@ export const fakeFuiUpload = ( }, 1700); }); }; - diff --git a/src/files-ui/core/validation/fileValidator.ts b/src/files-ui/core/validation/fileValidator.ts index 23fff3869f21c0bf8cb7f26f3e23e1ab525c6ca7..1fe580c4f5b03cc474786d51a5b7e67fb9021002 100644 --- a/src/files-ui/core/validation/fileValidator.ts +++ b/src/files-ui/core/validation/fileValidator.ts @@ -119,16 +119,26 @@ export const validateExtFile = ( ): ExtFile => { let extFileResult: ExtFile = { ...extFile }; let errors: string[] = []; + //TO-DO: Add extra validation for individual props even if FIle object was not given if (!extFile.file) { return { ...extFileResult } } + + //TO-DO: add "overrideValidation" prop to ignore the rest of validators like accept and maxFileSize if (validator) { - return { ...extFileResult, ...validator(extFileResult.file as File) }; + const resultCustomValidation: CustomValidateFileResponse = validator(extFileResult.file as File); + const { errors: errorsResult } = resultCustomValidation; + if (errorsResult) + errors.push(...errorsResult) + //return { ...extFileResult, ...validator(extFileResult.file as File) }; } + const { maxFileSize, accept } = validatorProps; console.log("Validation", maxFileSize, accept); //check file size const file: File = extFile.file; + + if (maxFileSize && file.size > maxFileSize) { const maxFileSizeErrorMessenger: FunctionLabel = localErrors.maxSizeError as FunctionLabel; @@ -142,7 +152,7 @@ export const validateExtFile = ( } const isValid: boolean = errors.length === 0; extFileResult = { ...extFileResult, valid: isValid, errors: !isValid ? errors : undefined }; - console.log("validation extFileResult",extFileResult); + console.log("validation extFileResult", extFileResult); return extFileResult; } diff --git a/src/files-ui/hooks/useDropzoneFileUpdater.ts b/src/files-ui/hooks/useDropzoneFileUpdater.ts index a776c6630bc01ea3972e2ed109244afe3ede255c..267cbd301079d933653d1544da7464a3c238f4c8 100644 --- a/src/files-ui/hooks/useDropzoneFileUpdater.ts +++ b/src/files-ui/hooks/useDropzoneFileUpdater.ts @@ -25,8 +25,9 @@ const useDropzoneFileListUpdater = ( localization?: Localization, validateFilesFlag?: boolean ): [ExtFile[], number, React.Dispatch<React.SetStateAction<ExtFile[]>>] => { - - console.log("FileListUpdater",dropzoneId, value, isUploading, maxFileSize, accept, maxFiles, validateFilesFlag); + console.log("incomming extfiles fileupdater value", value.map(x => x.uploadStatus)); + + console.log("FileListUpdater", dropzoneId, value, isUploading, maxFileSize, accept, maxFiles, validateFilesFlag); //state for managing the files locally const [localFiles, setLocalFiles] = React.useState<ExtFile[]>([]); diff --git a/src/files-ui/index.ts b/src/files-ui/index.ts index ed04b69303497448d9adb23d2ef53945757f26b8..b37d1a73ef7926d7bf5557a6ce2fd981fcd663d0 100644 --- a/src/files-ui/index.ts +++ b/src/files-ui/index.ts @@ -31,4 +31,4 @@ export { useFakeProgress } from "./hooks"; export { createListOfMultiTypeFile, uploadFile, uploadFormData, uploadExtFile, ExtFileInstance, ExtFileManager } from "./core"; -export type { ExtFile, ExtFileListMap, UPLOADSTATUS, Localization, ServerResponse, UploadResponse, UploadConfig } from "./core"; \ No newline at end of file +export type { ExtFile, ExtFileListMap, UPLOADSTATUS, Localization, ServerResponse, UploadResponse, UploadConfig, CustomValidateFileResponse } from "./core"; \ No newline at end of file diff --git a/src/pages/demo/DropzoneDemoPage.jsx b/src/pages/demo/DropzoneDemoPage.jsx index fac35340e431cdf13123a0a4a954a0aafb63e561..bcfc979f2e3058ac3be2bce5b1101711c21ae009 100644 --- a/src/pages/demo/DropzoneDemoPage.jsx +++ b/src/pages/demo/DropzoneDemoPage.jsx @@ -1,9 +1,17 @@ -import { Paper, Alert, AlertTitle } from "@mui/material"; import * as React from "react"; +import { Paper, Alert, AlertTitle } from "@mui/material"; import CodeHighlight from "../../components/codeHighlight/CodeHighlight"; import DescParagraph from "../../components/demo-components/desc-paragraph/DescParagraph"; import BasicDropzoneCodeJS from "../../components/demo-components/dropzone-demo/BasicDropzoneCodeJS"; import BasicDemoDropzone from "../../components/demo-components/dropzone-demo/BasicDropzoneDemo"; +import CodeDemoDropzoneActionButtons from "../../components/demo-components/dropzone-demo/CodeDemoDropzoneActionButtons"; +import CodeDemoDropzoneCustomValidation from "../../components/demo-components/dropzone-demo/CodeDemoDropzoneCustomValidation"; +import CodeDemoDropzoneUploading from "../../components/demo-components/dropzone-demo/CodeDemoDropzoneUploading"; +import CodeDemoDropzoneValidation from "../../components/demo-components/dropzone-demo/CodeDemoDropzoneValidation"; +import DemoDropzoneActionButtons from "../../components/demo-components/dropzone-demo/DemoDropzoneActionButtons"; +import DemoDropzoneCustomValidation from "../../components/demo-components/dropzone-demo/DemoDropzoneCustomValidation"; +import DemoDropzoneUploading from "../../components/demo-components/dropzone-demo/DemoDropzoneUploading"; +import DemoDropzoneValidation from "../../components/demo-components/dropzone-demo/DemoDropzoneValidation"; import SubTitle from "../../components/demo-components/sub-title/SubTitle"; import MainContentContainer from "../../components/layout-pages/MainContentContainer"; import RightMenuContainer from "../../components/layout-pages/RightMenuContainer"; @@ -11,49 +19,26 @@ import MainTitle from "../../components/main-title/MainTitle"; import MainParagraph from "../../components/paragraph-main/MainParagraph"; import RightMenu from "../../components/RightMenu/RightMenu"; import TypeHighlight from "../../components/typeHighlight/TypeHighlight"; +import AnchorToTab from "../../components/util-components/AnchorToTab"; +import DemoDropzoneFooterConfig from "../../components/demo-components/dropzone-demo/DemoDropzoneFooterConfig"; +import DemoDropzoneHeaderConfig from "../../components/demo-components/dropzone-demo/DemoDropzoneHeaderConfig"; +import CodeDemoDropzoneHeaderConfig from "../../components/demo-components/dropzone-demo/CodeDemoDropzoneHeaderConfig"; +import CodeDemoDropzoneFooterConfig from "../../components/demo-components/dropzone-demo/CodeDemoDropzoneFooterConfig"; +import DemoDropzoneStyling from "../../components/demo-components/dropzone-demo/DemoDropzoneStyling"; +import DemoDropzoneBehaviour from "../../components/demo-components/dropzone-demo/DemoDropzoneBehaviour"; +import DemoDropzoneLabel from "../../components/demo-components/dropzone-demo/DemoDropzoneLabel"; +import DemoDropzoneDropLayer from "../../components/demo-components/dropzone-demo/DemoDropzoneDropLayer"; +import DemoDropzoneClickable from "../../components/demo-components/dropzone-demo/DemoDropzoneClickable"; +import DemoDropzoneDisabled from "../../components/demo-components/dropzone-demo/DemoDropzoneDisabled"; +import DemoDropzoneRipple from "../../components/demo-components/dropzone-demo/DemoDropzoneRipple"; +import CodeDemoDropzoneStyling from "../../components/demo-components/dropzone-demo/CodeDropzoneDemoStyling"; +import CodeDemoDropzoneRipple from "../../components/demo-components/dropzone-demo/CodeDemoDropzoneRipple"; +import CodeDemoDropzoneDisabled from "../../components/demo-components/dropzone-demo/CodeDemoDropzoneDisabled"; +import CodeDemoDropzoneClickable from "../../components/demo-components/dropzone-demo/CodeDemoDropzoneClickable"; +import CodeDemoDropzoneDropLayer from "../../components/demo-components/dropzone-demo/CodeDemoDropzoneDropLayer"; +import CodeDemoDropzoneBehaviour from "../../components/demo-components/dropzone-demo/CodeDemoDropzoneBehaviour"; +import CodeDemoDropzoneLabel from "../../components/demo-components/dropzone-demo/CodeDropzoneDemoLabel"; -const rightMenuItems = [ - { - id: 0, - label: "Basic dropzone", - referTo: "/components/dropzone#basic-dropzone", - }, - { - id: 1, - label: "Validation", - referTo: "/components/dropzone#validation", - }, - { - id: 1, - label: "Custom validation", - referTo: "/components/dropzone#custom-validation", - }, - { - id: 2, - label: "Dropzone events", - referTo: "/components/dropzone#dropzone-events", - }, - { - id: 3, - label: "Uploading", - referTo: "/components/dropzone#uploading", - }, - { - id: 4, - label: "Styling", - referTo: "/components/dropzone#styling", - }, - { - id: 5, - label: "Localization", - referTo: "/components/dropzone#localization", - }, - { - id: 6, - label: "Dark mode", - referTo: "/components/dropzone#dark-mode", - }, -]; const DropzoneDemoPage = (props) => { return ( <React.Fragment> @@ -62,9 +47,8 @@ const DropzoneDemoPage = (props) => { <MainParagraph> The "dropzone" component is a special{" "} - <CodeHighlight>input</CodeHighlight> enhanced by the capability of - allowing users to either drag and drop files there or picking files - from a file dialog. + <CodeHighlight>input</CodeHighlight> enhanced by the ability to allow + users to drag and drop files there or choose files from a file dialog. </MainParagraph> <DescParagraph> @@ -80,9 +64,9 @@ const DropzoneDemoPage = (props) => { <li> The file(s) must be validated or not. If validation is required, it can be done by taking into account a predefined set of allowed{" "} - <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept"> + <AnchorToTab href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept"> Common MIME Types - </a> + </AnchorToTab> ; specifiying the max file size (in bytes) and/or the max amount of files. </li> @@ -97,8 +81,8 @@ const DropzoneDemoPage = (props) => { <section id="basic-dropzone"> <SubTitle content="Basic Dropzone" /> <DescParagraph> - In this demo we set dropzone with the minimun props that allows you - to get done fast. These props are{" "} + In this demo we set dropzone with the minimum props that allows you + to get your task done fast. These props are{" "} <CodeHighlight>onChange</CodeHighlight> and{" "} <CodeHighlight>value</CodeHighlight>. </DescParagraph> @@ -112,7 +96,10 @@ const DropzoneDemoPage = (props) => { <CodeHighlight>{`<FileMosaic/>`} </CodeHighlight> component for showing the files selected by the user with minimun props too. For further information of this component check out the{" "} - <a href="/components/filemosaic">FileMosaic</a> page. + <AnchorToTab href="/components/filemosaic"> + FileMosaic + </AnchorToTab>{" "} + page. </Alert> <br /> <Alert severity="info"> @@ -125,102 +112,525 @@ const DropzoneDemoPage = (props) => { <strong>Javascript</strong> example for handling the metadata that makes possible the information exchange between components. For further information about this data type check out the{" "} - <a href="/types#ext-file">ExtFile-API. </a> + <AnchorToTab href="/types#ext-file">ExtFile-API. </AnchorToTab> </Alert> </section> <section id="validation"> <SubTitle content="Validation" /> <DescParagraph> - You can either "tell" Dropzone component to validate user files by - providing one or more of these criteria: + In this demo you can see how{" "} + <CodeHighlight>{"<Dropzone/>"}</CodeHighlight> component covers the + following features when it comes to validating files. <ol> - <li>Accept specific file types.</li> - <li>Accept an specific number of files.</li> - <li>Accept an specific size (in bytes) of files.</li> + <li>Accepting specific file types.</li> + <li>Accepting an specific number of files.</li> + <li>Accepting files with an specific size (in bytes).</li> </ol> </DescParagraph> <Paper variant="outlined" style={{ padding: "25px" }}> - <BasicDemoDropzone /> + <DemoDropzoneValidation /> </Paper> - - <p></p> - <BasicDropzoneCodeJS /> + <CodeDemoDropzoneValidation /> + <Alert severity="info"> + <AlertTitle> Removing non valid Files </AlertTitle> + We call "clean" to the operation of removing non valid files. Apart + from deleting them individually, there are some other ways in which + you can remove all of them. You can try the following props in the{" "} + {"<Dropzone/>"} component: + <ul> + <li> + <TypeHighlight>cleanFiles</TypeHighlight> : This will make + dropzone header to display the "clean" icon which can trigger + the "clean" operation. + </li> + <li> + <TypeHighlight>actionButtons</TypeHighlight> : By setting this + prop properly, a button will be visible and will trigger the + "clean" operation (This is the way used in this demo). + </li> + <li> + <TypeHighlight>autoClean</TypeHighlight> : By setting this prop, + non valid files will be automatically discarted and will not be + given in the <CodeHighlight>onChange</CodeHighlight> event. + </li> + </ul> + </Alert> </section> <section id="custom-validation"> <SubTitle content="Custom validation" /> <DescParagraph> - You can also "override the Dropzone validation by giving a custom - validation function that must fit the following signature:{" "} + You can also override the Dropzone validation operation by giving a + custom validation function that must fit the following signature:{" "} <CodeHighlight> {"validator?: (f: "} - <a href="https://developer.mozilla.org/en-US/docs/Web/API/File"> + <AnchorToTab href="https://developer.mozilla.org/en-US/docs/Web/API/File"> File - </a> + </AnchorToTab> {") => "} - <a href="/types#custom-validate-file-response"> + <AnchorToTab href="/types#custom-validate-file-response"> CustomValidateFileResponse - </a> + </AnchorToTab> </CodeHighlight> - . + . Custom validator can work together with{" "} + <TypeHighlight>accept</TypeHighlight>,{" "} + <TypeHighlight>maxFileSize</TypeHighlight> and{" "} + <TypeHighlight>maxFiles</TypeHighlight> props. + </DescParagraph> + <Paper variant="outlined" style={{ padding: "25px" }}> + <DemoDropzoneCustomValidation /> + </Paper> + <CodeDemoDropzoneCustomValidation /> + </section> + + <section id="uploading"> + <SubTitle content="Uploading" /> + <DescParagraph> + For uploading , the prop <TypeHighlight>uploadConfig</TypeHighlight>{" "} + must be given. This prop recieves an object in which you can specify + the HTTP method, the url, the headers and also extra data to be + uploaded. The type definition for the prop mentioned can be found{" "} + <AnchorToTab href="/types#UploadConfig">here</AnchorToTab>. </DescParagraph> <Paper variant="outlined" style={{ padding: "25px" }}> - <BasicDemoDropzone /> + <DemoDropzoneUploading /> </Paper> - <p></p> - <BasicDropzoneCodeJS /> - </section> - {/* - <section id="dropzone-events"> - <SubTitle content="Dropzone events" /> - <DescParagraph> - You can handle the following events: - <ul> - <li> - Dropzone with the <code className="code">onDelete</code> prop - defined can delete all the files associated with the{" "} - <code className="code">value</code> prop. - </li> - <li> + <CodeDemoDropzoneUploading /> + <Alert severity="info"> + <AlertTitle> Server response </AlertTitle> + For uploading files through files-ui, server must return the + response with the structure of the{" "} + <TypeHighlight> {" "} - Dropzone with the <code className="code">onDelete</code> prop - defined can delete all the files associated with the{" "} - <code className="code">value</code> prop.. - </li> - <li>Accept an specific size (in bytes) of files.</li> - </ul> - </DescParagraph> + <AnchorToTab href="/types#ServerResponse"> + ServerResponse + </AnchorToTab> + </TypeHighlight>{" "} + type. + </Alert> + <br /> + <Alert severity="info"> + <AlertTitle> "fakeUpload" </AlertTitle> + By giving this prop, the {"<Dropzone/>"} component will simulate the + upload operation by randomly set the upload status and message in + ech uploadable <TypeHighlight>ExtFile</TypeHighlight>. It also will + set a fake progress. + </Alert> + <br /> + <Alert severity="info"> + <AlertTitle> Upload config properties </AlertTitle> + Apart from the properties you can see in the example above, there + are more of them that can make the upload process to behave + differently. + <ul> + <li> + <TypeHighlight>autoUpload</TypeHighlight> : By setting this prop + to true, the upload process will start automatically just after + the user drops or selects files. + </li> + <li> + <TypeHighlight>cleanOnUpload</TypeHighlight> : By setting this + prop, you can control whether the upload process should "clean" + the non valid files before uploading or not By default this + value is true. + </li> + </ul> + More information about the uploadConfig type structure can be found{" "} + <AnchorToTab href="/types#UploadConfig">here</AnchorToTab>. + </Alert> + </section> + + <section id="action-buttons"> + <SubTitle content="Dropzone with action buttons" /> + <DescParagraph> + If you need to display buttons that trigger the default events in + the <CodeHighlight>{"<Dropzone/>"}</CodeHighlight> component, you + can do it by adding the <TypeHighlight>actionButtons</TypeHighlight>{" "} + prop. This will add buttons to the top or to the bottom of this + component. + <ul> + <li> + Dropzone with the{" "} + <TypeHighlight>actionButtons.cleanButton</TypeHighlight> prop + defined will display a button which triggers the clean process. + <br /> + This button will be visible only{" "} + <strong>when the "upload" process is not active</strong>. + </li> + <li> + Dropzone with the{" "} + <TypeHighlight>actionButtons.deleteButton</TypeHighlight> prop + defined will display a button which deletes all files. + <br /> + this button will be visible only{" "} + <strong>when the "upload" process is not active</strong>. + </li> + <li> + Dropzone with the{" "} + <TypeHighlight>actionButtons.uploadButton</TypeHighlight> prop + defined will display a button which starts the upload process. + This button will <strong>not</strong> be visible{" "} + <strong>during the "upload" process</strong>. + </li> + <li> + Dropzone with the{" "} + <TypeHighlight>actionButtons.abortButton</TypeHighlight> prop + defined will display a button which stops the upload process. + <br /> + This button will be visible only{" "} + <strong>during the "upload" process</strong>. + </li> + </ul> + </DescParagraph> - <Paper variant="outlined" style={{ padding: "25px" }}> - <BasicDemoDropzone /> - </Paper> + <Paper variant="outlined" style={{ padding: "25px" }}> + <DemoDropzoneActionButtons /> + </Paper> - <p></p> - <BasicDropzoneCodeJS /> - </section> - <section id="api"> - <SubTitle content="API" /> - <DescParagraph> - See the documentation below for a complete reference to all of the - props and classes available to the components mentioned here. - <ul> - <li> - <CodeHighlight> - <a href="/api/dropzone">{"<Dropzone />"}</a> - </CodeHighlight> - </li> - <li> - <CodeHighlight> - <a href="/api/filemosaic">{"<FileMosaic />"}</a> - </CodeHighlight> - </li> - </ul> - </DescParagraph> - </section> */} + <CodeDemoDropzoneActionButtons /> + </section> + + <section id="header-config"> + <SubTitle content="Dropzone header config" /> + <DescParagraph> + You can use the <TypeHighlight>headerConfig</TypeHighlight> prop to + define what will be displayed in the header. + <ul> + <li> + Dropzone with the{" "} + <TypeHighlight>headerConfig.customHeader</TypeHighlight> prop + defined will display this prop replacing the entire default + header. + </li> + </ul> + By default all of these values are set to{" "} + <TypeHighlight>true</TypeHighlight>. + </DescParagraph> + + <Paper variant="outlined" style={{ padding: "25px" }}> + <DemoDropzoneHeaderConfig /> + </Paper> + + <CodeDemoDropzoneHeaderConfig /> + <Alert severity="info"> + <AlertTitle> HeaderConfig </AlertTitle> + There are more options that can be defined such as: + <ul> + <li> + Dropzone with the{" "} + <TypeHighlight>headerConfig.deleteFiles</TypeHighlight> prop set + to <TypeHighlight>true</TypeHighlight> will display a delete + button which triggers the delete process. + <br /> + This button will be visible only{" "} + <strong>when the "upload" process is not active</strong>. + </li> + <li> + Dropzone with the{" "} + <TypeHighlight>headerConfig.cleanFiles</TypeHighlight> prop set + to <TypeHighlight>true</TypeHighlight> will display a "clean" + buton. This button will be visible only during the "upload" + process. + <br /> + button will be visible only{" "} + <strong>when the "upload" process is not active</strong>. + </li> + <li> + Dropzone with the{" "} + <TypeHighlight>headerConfig.uploadFiles</TypeHighlight> prop set + to <TypeHighlight>true</TypeHighlight> will display a button + which starts the upload process. + </li> + <li> + Dropzone with the{" "} + <TypeHighlight>headerConfig.uploading</TypeHighlight> prop set + to <TypeHighlight>true</TypeHighlight> will display a loading + icon <strong>during the "upload" process</strong>. + </li> + <li> + Dropzone with the{" "} + <TypeHighlight>headerConfig.maxFileSize</TypeHighlight> prop set + to <TypeHighlight>true</TypeHighlight> will display the max file + size label. + </li> + <li> + Dropzone with the{" "} + <TypeHighlight>headerConfig.validFilesCount</TypeHighlight> prop + set to <TypeHighlight>true</TypeHighlight> will display the + current count of valid files. + </li> + </ul> + By default all of these values are set to + <TypeHighlight>true</TypeHighlight>. The complete type definition + can be found{" "} + <AnchorToTab href="/types#header-config">here</AnchorToTab>. + </Alert> + </section> + + <section id="footer-config"> + <SubTitle content="Dropzone footer config" /> + <DescParagraph> + You can use the <TypeHighlight>footerConfig</TypeHighlight> prop to + define what will be displayed in the footer. + <ul> + <li> + Dropzone with the{" "} + <TypeHighlight>footerConfig.customMessage</TypeHighlight> prop + defined will be considered the message to display in the footer. + </li> + </ul> + </DescParagraph> + + <Paper variant="outlined" style={{ padding: "25px" }}> + <DemoDropzoneFooterConfig /> + </Paper> + + <CodeDemoDropzoneFooterConfig /> + + <Alert severity="info"> + <AlertTitle> FooterConfig </AlertTitle> + There are more options that can be defined such as: + <ul> + <li> + Dropzone with the{" "} + <TypeHighlight>footerConfig.allowedTypesLabel</TypeHighlight>{" "} + prop set to <TypeHighlight>false</TypeHighlight> will hide the + label that indicates the files types allowed. This label will be + hidden <strong>when the "upload" process is active</strong>. + </li> + <li> + Dropzone with the{" "} + <TypeHighlight> + footerConfig.uploadProgressMessage + </TypeHighlight>{" "} + prop set to <TypeHighlight>false</TypeHighlight> will not + display the label with the upload progress. This label will be + visible<strong> only during the "upload" process</strong>. + </li> + <li> + Dropzone with the{" "} + <TypeHighlight>footerConfig.uploadResultMessage</TypeHighlight>{" "} + prop set to <TypeHighlight>false</TypeHighlight> will not + display a label at the end of the upload process. This label can{" "} + be visible for 2 seconds just{" "} + <strong>after the "upload" process finishes</strong>. + </li> + <li> + Dropzone with the{" "} + <TypeHighlight>footerConfig.noMissingFilesLabel</TypeHighlight>{" "} + prop set to <TypeHighlight>false</TypeHighlight> will not + display a label when upload starts with no uploadable files. + </li> + <li> + Dropzone with the{" "} + <TypeHighlight>footerConfig.style</TypeHighlight> prop set to{" "} + <TypeHighlight>false</TypeHighlight>defined will override the + styles associated to the footer component. + </li> + </ul> + By default all of these values are set to + <TypeHighlight>true</TypeHighlight>. + <br /> + The complete type definition can be found{" "} + <AnchorToTab href="/types#footer-config">here</AnchorToTab>. + </Alert> + </section> + + <section id="styling"> + <SubTitle content="Styling Dropzone" /> + <DescParagraph> + You can use change the look and feel of the{" "} + <CodeHighlight>Dropzone</CodeHighlight> component + <ul> + <li> + Dropzone with the <TypeHighlight>color</TypeHighlight> prop + defined will use this color for border, drop layer, font color + and ripple. + </li> + <li> + Dropzone with the <TypeHighlight>minHeight</TypeHighlight> prop + defined will use this value to define the minimum height of the + component. + </li> + <li> + Dropzone with the <TypeHighlight>background</TypeHighlight> prop + defined will use this value for the background. You can set nice + gradients or even a background image. + </li> + </ul> + </DescParagraph> + + <Paper variant="outlined" style={{ padding: "25px" }}> + <DemoDropzoneStyling /> + </Paper> + + <CodeDemoDropzoneStyling /> + </section> + + <section id="label"> + <SubTitle content="Label" /> + <DescParagraph> + You can specify a fixed label for{" "} + <CodeHighlight>{"<Dropzone/>"}</CodeHighlight> component to display + when there isn't any files. + </DescParagraph> + + <Paper variant="outlined" style={{ padding: "25px" }}> + <DemoDropzoneLabel /> + </Paper> + + <CodeDemoDropzoneLabel /> + </section> + + <section id="ripple"> + <SubTitle content="Ripple" /> + <DescParagraph> + According to{" "} + <AnchorToTab href="https://m2.material.io/develop/ios/supporting/ripple"> + Material Design + </AnchorToTab> + , the Ripple component provides a radial action in the form of a + visual ripple expanding outward from the user's touch. Ripple is a + visual form of feedback for touch events providing users a clear + signal that an element is being touched. In this component, a ripple + is displayed after 2 user actions: + <ul> + <li>When user clicks or touches the component.</li> + <li>Just after user dropped files.</li> + </ul> + For disabling the ripple effect you can set the{" "} + <TypeHighlight>disableRipple</TypeHighlight> prop to true. + </DescParagraph> + + <Paper variant="outlined" style={{ padding: "25px" }}> + <DemoDropzoneRipple /> + </Paper> + + <CodeDemoDropzoneRipple /> + </section> + + <section id="disabled"> + <SubTitle content="Disabled Dropzone" /> + <DescParagraph> + According to{" "} + <AnchorToTab href="https://m2.material.io/develop/ios/supporting/ripple"> + Material Design + </AnchorToTab>{" "} + a disabled state communicates when a component or element isn’t + interactive, and should be deemphasized in a UI. + <TypeHighlight>disabled</TypeHighlight> prop to true. + </DescParagraph> + + <Paper variant="outlined" style={{ padding: "25px" }}> + <DemoDropzoneDisabled /> + </Paper> + + <CodeDemoDropzoneDisabled /> + </section> + + <section id="clickable"> + <SubTitle content="Clickable Dropzone" /> + <DescParagraph> + Dropzone with the + <TypeHighlight>clickable</TypeHighlight> prop set to false will not + open the file dialog to select files when user clicks the component. + </DescParagraph> + + <Paper variant="outlined" style={{ padding: "25px" }}> + <DemoDropzoneClickable /> + </Paper> + + <CodeDemoDropzoneClickable /> + </section> + + <section id="drop-layer"> + <SubTitle content="Drop Layer" /> + <DescParagraph> + Dropzone with the + <TypeHighlight>dropOnLayer</TypeHighlight> prop set to false will + not perform the drop operation in a layer that covers the complete + component container. + <br /> + In this demo try to drag and drop files in both dropzones to see the + difference. + </DescParagraph> + + <Paper variant="outlined" style={{ padding: "25px" }}> + <DemoDropzoneDropLayer /> + </Paper> + + <CodeDemoDropzoneDropLayer /> + </section> + + <section id="add-or-replace"> + <SubTitle content="Add or replace files" /> + <DescParagraph> + There are 2 different behaviours when user selects or drops new + files: + <ul> + <li> + Dropzone with the <CodeHighlight>behaviour</CodeHighlight> set + to <TypeHighlight>"add"</TypeHighlight> will add the new files + to the current array of ExtFiles. + </li> + <li> + Dropzone with the <CodeHighlight>behaviour</CodeHighlight> set + to <TypeHighlight>"replace"</TypeHighlight> will replace the + current array of ExtFiles with the new ones. + </li> + </ul> + In this demo try to select or drop files more than once on each + dropzone to see the difference. + </DescParagraph> + + <Paper variant="outlined" style={{ padding: "25px" }}> + <DemoDropzoneBehaviour /> + </Paper> + + <CodeDemoDropzoneBehaviour /> + </section> + + <section id="localization"> + <SubTitle content="Localization" /> + <DescParagraph> + The localization demo for this component can be found in the{" "} + <AnchorToTab href="/localization">localization page</AnchorToTab> + </DescParagraph> + + {/* <Paper variant="outlined" style={{ padding: "25px" }}> + <DemoDropzoneFooterConfig /> + </Paper> + + <CodeDemoDropzoneFooterConfig /> */} + </section> + + <section id="api"> + <SubTitle content="API" /> + <DescParagraph> + See the documentation below for a complete reference to all of the + props and classes available to the components mentioned here. + <ul> + <li> + <CodeHighlight> + <AnchorToTab href="/api/dropzone"> + {"<Dropzone />"} + </AnchorToTab> + </CodeHighlight> + </li> + <li> + <CodeHighlight> + <AnchorToTab href="/api/filemosaic"> + {"<FileMosaic />"} + </AnchorToTab> + </CodeHighlight> + </li> + </ul> + </DescParagraph> + </section> </MainContentContainer> <RightMenuContainer> @@ -230,3 +640,86 @@ const DropzoneDemoPage = (props) => { ); }; export default DropzoneDemoPage; + +const rightMenuItems = [ + { + id: 0, + label: "Basic dropzone", + referTo: "/components/dropzone#basic-dropzone", + }, + { + id: 1, + label: "Validation", + referTo: "/components/dropzone#validation", + }, + { + id: 2, + label: "Custom validation", + referTo: "/components/dropzone#custom-validation", + }, + { + id: 3, + label: "Uploading", + referTo: "/components/dropzone#uploading", + }, + /* { + id: 4, + label: "Dropzone events", + referTo: "/components/dropzone#dropzone-events", + }, */ + { + id: 5, + label: "Action buttons", + referTo: "/components/dropzone#action-buttons", + }, + { + id: 9, + label: "Header config", + referTo: "/components/dropzone#header-config", + }, + { + id: 10, + label: "Footer config", + referTo: "/components/dropzone#footer-config", + }, + { + id: 6, + label: "Styling", + referTo: "/components/dropzone#styling", + }, + { + id: 14, + label: "Label", + referTo: "/components/dropzone#label", + }, + { + id: 8, + label: "Ripple", + referTo: "/components/dropzone#ripple", + }, + { + id: 11, + label: "Disabled", + referTo: "/components/dropzone#disabled", + }, + { + id: 12, + label: "Clickable", + referTo: "/components/dropzone#clickable", + }, + { + id: 13, + label: "Drop Layer", + referTo: "/components/dropzone#drop-layer", + }, + { + id: 15, + label: "Add or replace", + referTo: "/components/dropzone#add-or-replace", + }, + { + id: 7, + label: "Localization", + referTo: "/components/dropzone#localization", + }, +];