diff --git a/src/components/MainMenu/MainMenuSideBar.tsx b/src/components/MainMenu/MainMenuSideBar.tsx index ee53559ac5b6297f7cdefa5d2d125aeb90c0f978..2891c843eb7f64c4b947249cf98209e5cfcc4cbf 100644 --- a/src/components/MainMenu/MainMenuSideBar.tsx +++ b/src/components/MainMenu/MainMenuSideBar.tsx @@ -140,6 +140,12 @@ export default function MainMenuSideBar(props: MainMenuSideBarProps) { index: 72, onClick: () => navigate("/utilities-methods/file-uploader"), }, + + { + label: "File download", + index: 73, + onClick: () => navigate("/utilities-methods/file-downloader"), + }, ], }, ]; diff --git a/src/components/MainPage/MainRight/FileMosaicImageVideoPreviews.tsx b/src/components/MainPage/MainRight/FileMosaicImageVideoPreviews.tsx index b71ca0b5f38d16f6785cb35e2103bba425ddea5e..073d27c2e487d37c6223c84bd0f20213e474346f 100644 --- a/src/components/MainPage/MainRight/FileMosaicImageVideoPreviews.tsx +++ b/src/components/MainPage/MainRight/FileMosaicImageVideoPreviews.tsx @@ -1,10 +1,17 @@ import * as React from "react"; import { Stack, Paper } from "@mui/material"; -import { FileMosaic } from "../../../files-ui/components/file-mosaic"; -import { ExtFile } from "../../../files-ui/core"; +import { FileMosaic, ExtFile, FileMosaicProps } from "../../../files-ui"; import DescParagraph from "../../demo-components/desc-paragraph/DescParagraph"; import { FullScreen, ImagePreview, VideoPreview } from "../../../files-ui"; +import { + NarutoAndSasukeVsMomoshikiEN, + NarutoAndSasukeVsMomoshikiES, + ThorArrivesWakandaEN, + ThorArrivesWakandaES, +} from "../../../data/videoLinks"; +const VIDEO_URL = + "https://srv23.y2mate.is/download?file=cd448fa7c7fe6c301970e890794fb682137140"; interface FileMosaicImageVideoPreviewsProps { darkMode?: boolean; } @@ -23,16 +30,88 @@ const FileMosaicImageVideoPreviews: React.FC< }; const handleWatch = (videoSource: File | string | undefined) => { - console.log( - "handleWatch videoSource", - "https://files-ui-temp-storage.s3.amazonaws.com/2029385a4ed32ff10beeb94c0585e8ac1a8c377c68d22ef25ce5863694a5499e.mp4" - ); + console.log("handleWatch videoSource", videoSource); //setVideoSrc(videoSource); - // - setVideoSrc("https://files-ui-temp-storage.s3.amazonaws.com/2029385a4ed32ff10beeb94c0585e8ac1a8c377c68d22ef25ce5863694a5499e.mp4"); - // setVideoSrc("https://www.w3schools.com/tags/movie.mp4"); + // + setVideoSrc(videoSource); + // setVideoSrc("https://www.w3schools.com/tags/movie.mp4"); }; + const handleDownload = async ( + fileId: FileMosaicProps["id"], + downloadUrl?: string + ) => { + console.log("Download fileId", fileId); + console.log("Download fileName", files.filter(x=>x.id===fileId)[0]); + console.log("Download downloadUrl", downloadUrl); + if (!downloadUrl) return; + try { + const image = await fetch(downloadUrl); + const imageBlob = await image.blob(); + const imageURL = URL.createObjectURL(imageBlob); + const anchor = document.createElement("a"); + anchor.href = imageURL; + anchor.download = "fileName.jpg"; + + document.body.appendChild(anchor); + anchor.click(); + document.body.removeChild(anchor); + URL.revokeObjectURL(imageURL); + /* const resJson = await response.json(); + console.log("Download", resJson); */ + } catch (error) { + console.log("Download error", error); + console.error( error); + } + }; + const handleDownloadXHR = async ( + fileId: FileMosaicProps["id"], + downloadUrl?: string + ) => { + + console.log("Download fileId", fileId); + console.log("Download fileName", files.filter(x=>x.id===fileId)[0].name); + console.log("Download downloadUrl", downloadUrl); + if (!downloadUrl) return; + try { + const request = new XMLHttpRequest(); + request.responseType="blob"; + request.open("get", downloadUrl, true); + request.send(); + + request.onreadystatechange=function(){ + if(this.readyState==4 && this.status==200){ + const imageURL = window.URL.createObjectURL(this.response); + + const anchor = document.createElement("a"); + anchor.href = imageURL; + anchor.download = "fileNamess.jpg"; + document.body.appendChild(anchor); + anchor.click(); + + }else{ + console.log("not yet"); + } + } + + const image = await fetch(downloadUrl); + const imageBlob = await image.blob(); + const imageURL = URL.createObjectURL(imageBlob); + + const anchor = document.createElement("a"); + anchor.href = imageURL; + anchor.download = "fileName.jpg"; + + document.body.appendChild(anchor); + anchor.click(); + document.body.removeChild(anchor); + URL.revokeObjectURL(imageURL); + /* const resJson = await response.json(); + console.log("Download", resJson); */ + } catch (error) { + console.log("Download error", error); + } + }; return ( <div style={{ width: "100%" }}> <h3>Or check the previews!</h3> @@ -66,6 +145,7 @@ const FileMosaicImageVideoPreviews: React.FC< onWatch={handleWatch} {...f.extraData} alwaysActive + onDownload={f.downloadUrl ? handleDownload : undefined} /> ))} <FullScreen @@ -78,7 +158,7 @@ const FileMosaicImageVideoPreviews: React.FC< open={videoSrc !== undefined} onClose={() => setVideoSrc(undefined)} > - <VideoPreview videoSrc={videoSrc} autoPlay controls /> + <VideoPreview src={videoSrc} autoPlay controls /> </FullScreen> </Stack> </Paper> @@ -90,10 +170,12 @@ export default FileMosaicImageVideoPreviews; const files: ExtFile[] = [ { id: 0, - name: "image-preview.png", - type: "image/png", + name: "mark45.jpg", + type: "image/jpeg", size: 282000, imageUrl: "https://i.ytimg.com/vi/98FO19TuI9A/maxresdefault.jpg", + downloadUrl: "https://i.ytimg.com/vi/98FO19TuI9A/maxresdefault.jpg", + }, { @@ -101,8 +183,8 @@ const files: ExtFile[] = [ name: "video-preview.mp4", type: "video/mp4", size: 282000, - downloadUrl: - "https://files-ui-temp-storage.s3.amazonaws.com/2029385a4ed32ff10beeb94c0585e8ac1a8c377c68d22ef25ce5863694a5499e.mp4", + downloadUrl: VIDEO_URL, + extraData: { videoUrl: VIDEO_URL }, }, { id: 3, @@ -121,9 +203,46 @@ const files: ExtFile[] = [ type: "image/png", size: 282000, downloadUrl: - "https://i.pinimg.com/236x/3e/e9/46/3ee946b27fd1cc5eb0b485e4a0669534.jpg", + "https://upload.wikimedia.org/wikipedia/commons/thumb/5/51/Facebook_f_logo_%282019%29.svg/768px-Facebook_f_logo_%282019%29.svg.png", imageUrl: - "https://i.pinimg.com/236x/3e/e9/46/3ee946b27fd1cc5eb0b485e4a0669534.jpg", + "https://upload.wikimedia.org/wikipedia/commons/thumb/5/51/Facebook_f_logo_%282019%29.svg/768px-Facebook_f_logo_%282019%29.svg.png", + + }, +]; + +const videos: ExtFile[] = [ + { + id: 0, + name: "ThorArrivesWakandaEN.mp4", + type: "video/mp4", + size: 282000, + downloadUrl: ThorArrivesWakandaEN, + extraData: { videoUrl: ThorArrivesWakandaEN }, + }, + { + id: 1, + name: "ThorArrivesWakandaES.mp4", + type: "video/mp4", + size: 282000, + downloadUrl: ThorArrivesWakandaES, + extraData: { videoUrl: ThorArrivesWakandaES }, + }, + + { + id: 1, + name: "NarutoAndSasukeVsMomoshikiEN.mp4", + type: "video/mp4", + size: 282000, + downloadUrl: NarutoAndSasukeVsMomoshikiEN, + extraData: { videoUrl: NarutoAndSasukeVsMomoshikiEN }, + }, + { + id: 1, + name: "NarutoAndSasukeVsMomoshikiES.mp4", + type: "video/mp4", + size: 282000, + downloadUrl: NarutoAndSasukeVsMomoshikiES, + extraData: { videoUrl: NarutoAndSasukeVsMomoshikiES }, }, ]; diff --git a/src/components/demo-components/avatar-demo/BasicDemoAvatar.tsx b/src/components/demo-components/avatar-demo/BasicDemoAvatar.tsx index b64cf952e160fb5b4bb7fabdd781c11ec40229ad..2a25a639af53bad955a85fa4a90f99cd60a929ca 100644 --- a/src/components/demo-components/avatar-demo/BasicDemoAvatar.tsx +++ b/src/components/demo-components/avatar-demo/BasicDemoAvatar.tsx @@ -1,6 +1,6 @@ import * as React from "react"; -import { Avatar } from "../../../files-ui"; -import { ServerResponse, uploadFile } from "../../../files-ui/core"; +import { Avatar,ServerResponse, uploadFile } from "../../../files-ui"; + const REMOTE = "https://files-ui-express-static-file-storage.vercel.app/39d33dff2d41b522c1ea276c4b82507f96b9699493d2e7b3f5c864ba743d9503"; diff --git a/src/components/demo-components/dropzone-demo/AdvancedDropzoneDemo.jsx b/src/components/demo-components/dropzone-demo/AdvancedDropzoneDemo.jsx index 7fb9aea6f0f366dcfd1d5764e54a7e6090bad4f7..b9e04b34e6dd754d88ddfe9eac25a9fd9501fb87 100644 --- a/src/components/demo-components/dropzone-demo/AdvancedDropzoneDemo.jsx +++ b/src/components/demo-components/dropzone-demo/AdvancedDropzoneDemo.jsx @@ -1,14 +1,15 @@ import * as React from "react"; import { Dropzone, - FileMosaic /* FullScreenPreview */, + ExtFileInstance, + FileMosaic, FullScreen, ImagePreview, VideoPreview, + } from "../../../files-ui"; import { useEffect, useState } from "react"; import axios from "axios"; -import { ExtFileInstance } from "../../../files-ui/core"; const REMOTE = "https://files-ui-express-static-file-storage.vercel.app/39d33dff2d41b522c1ea276c4b82507f96b9699493d2e7b3f5c864ba743d9503"; //const LOCAL = "http://localhost/39d33dff2d41b522c1ea276c4b82507f96b9699493d2e7b3f5c864ba743d9503"; @@ -20,14 +21,16 @@ export default function AdvancedDropzoneDemo() { const updateFiles = (incommingFiles) => { console.log("incomming extFiles", incommingFiles); - const arrExtFIleInstances = incommingFiles.map( + const arrExtFIleInstances = incommingFiles.map( (extFile) => new ExtFileInstance(extFile) ); - const listExtFileObjects = arrExtFIleInstances.map((extFileInstance) => + + console.log("incomming arrExtFIleInstances", arrExtFIleInstances); + /* const listExtFileObjects = arrExtFIleInstances.map((extFileInstance) => extFileInstance.toExtFile() ); - console.log("incomming arrExtFIleInstances", arrExtFIleInstances); - console.log("incomming listExtFileObjects", listExtFileObjects); + + console.log("incomming listExtFileObjects", listExtFileObjects); */ setExtFiles(incommingFiles); }; @@ -37,9 +40,9 @@ export default function AdvancedDropzoneDemo() { const handleSee = (imageSource) => { setImageSrc(imageSource); }; - const onClean = () => { +/* const onClean = () => { setExtFiles(extFiles.filter((ef) => ef.valid !== false)); - }; + }; */ const handleFinish = (res) => { console.log("finish", res); }; diff --git a/src/components/demo-components/file-card-demo/CodeJSFileCardBasic.jsx b/src/components/demo-components/file-card-demo/CodeJSFileCardBasic.jsx new file mode 100644 index 0000000000000000000000000000000000000000..80b8e737ac5dd1876cd0724b25433dcd6d42f975 --- /dev/null +++ b/src/components/demo-components/file-card-demo/CodeJSFileCardBasic.jsx @@ -0,0 +1,117 @@ +import * as React from "react"; +import ShowDemoCode from "../../show-demo-code/ShowDemoCode"; + +const CodeJSFileCardBasic = ({ card }) => { + return ( + <ShowDemoCode + codeCompleteJS={completeCodeJS(card)} + codeCompleteTS={completeCodeTS(card)} + codeSandboxJS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSandboxTS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSplittedJS={splittedCodeJS(card)} + codeSplittedTS={splittedCodeTS(card)} + /> + ); +}; +export default CodeJSFileCardBasic; + +const splittedCodeJS = (card) => `<> + {value ? ( + <${!card ? "FileMosaic" : "FileCard"} {...value} onDelete={removeFile} /> + ) : ( + <FileInputButton value={value ? [value] : []} onChange={updateFile} /> + )} + <${!card ? "FileMosaic" : "FileCard"} {...sampleFileProps} /> +</> + +// file props +const sampleFileProps = { + id: ":0:", + size: 28 * 1024 * 1024, + type: "text/plain", + name: "file created from props.jsx", +};`; + +const completeCodeJS = (card) => `import * as React from "react"; +import { InputButton, FileCard } from "@files-ui/react"; + +const sampleFileProps = { + id: ":0:", + size: 28 * 1024 * 1024, + type: "text/plain", + name: "file created from props.jsx", +}; + +export default function App() { + const [value, setValue] = React.useState(undefined); + + const updateFiles = (incommingFiles) => { + console.log("incomming extFiles", incommingFiles); + setValue(incommingFiles[0]); + }; + const removeFile = () => { + setValue(undefined); + }; + return ( + <div style={{display:"flex", gap:"10px"}}> + {value ? ( + <${ + !card ? "FileMosaic" : "FileCard" + } {...value} onDelete={removeFile} info/> + ) : ( + <FileInputButton value={value ? [value] : []} onChange={updateFile} /> + )} + <${!card ? "FileMosaic" : "FileCard"} {...sampleFileProps} info/> + </div> + ); +};`; + +const splittedCodeTS = (card) => `<> + {value ? ( + <${!card ? "FileMosaic" : "FileCard"} {...value} onDelete={removeFile} info/> + ) : ( + <FileInputButton value={value ? [value] : []} onChange={updateFile} /> + )} + <${!card ? "FileMosaic" : "FileCard"} {...sampleFileProps} info/> +</> + +// file props +const sampleFileProps: ExtFile = { + id: ":0:", + size: 28 * 1024 * 1024, + type: "text/plain", + name: "file created from props.jsx", +};`; +const completeCodeTS = (card) => `import * as React from "react"; +import { InputButton, FileCard, ExtFile } from "@files-ui/react"; + +const sampleFileProps:ExtFile = { + id: ":0:", + size: 28 * 1024 * 1024, + type: "text/plain", + name: "file created from props.jsx", +}; + +export default function App() { + const [value, setValue] = React.useState<ExtFile | undefined>(undefined); + + const updateFiles = (incommingFiles:ExtFile[]) => { + console.log("incomming extFiles", incommingFiles); + setValue(incommingFiles[0]); + }; + const removeFile = () => { + setValue(undefined); + }; + return ( + <div style={{display:"flex", gap:"10px"}}> + {value ? ( + <${ + !card ? "FileMosaic" : "FileCard" + } {...value} onDelete={removeFile} info/> + ) : ( + <FileInputButton value={value ? [value] : []} onChange={updateFile} /> + )} + <${!card ? "FileMosaic" : "FileCard"} {...sampleFileProps} info/> + </div> + ); +};`; diff --git a/src/components/demo-components/file-card-demo/DemoFileCardBasic.jsx b/src/components/demo-components/file-card-demo/DemoFileCardBasic.jsx new file mode 100644 index 0000000000000000000000000000000000000000..88b6d771258838b30b09dce2816d365c6f9be4fd --- /dev/null +++ b/src/components/demo-components/file-card-demo/DemoFileCardBasic.jsx @@ -0,0 +1,32 @@ +import * as React from "react"; +import { FileCard, FileInputButton, FileMosaic } from "../../../files-ui"; + +const sampleFileProps = { + id: "fileId", + size: 28 * 1024 * 1024, + type: "text/plain", + name: "file created from props.jsx", +}; +const DemoFileCardBasic = (props) => { + const [value, setValue] = React.useState(undefined); + + const updateFile = (incommingFiles) => { + console.log("incomming extFiles", incommingFiles); + setValue(incommingFiles[0]); + }; + + const removeFile = () => { + setValue(undefined); + }; + return ( + <> + {value ? ( + <FileCard {...value} onDelete={removeFile} info/> + ) : ( + <FileInputButton value={value ? [value] : []} onChange={updateFile} /> + )} + <FileCard {...sampleFileProps} info/> + </> + ); +}; +export default DemoFileCardBasic; diff --git a/src/components/demo-components/filemosaic-demo/CodeJSFileCardActions.tsx b/src/components/demo-components/filemosaic-demo/CodeJSFileCardActions.tsx new file mode 100644 index 0000000000000000000000000000000000000000..48015be11943dd2450d507e89fc8055ae78b6db2 --- /dev/null +++ b/src/components/demo-components/filemosaic-demo/CodeJSFileCardActions.tsx @@ -0,0 +1,170 @@ +import * as React from "react"; +import ShowDemoCode from "../../show-demo-code/ShowDemoCode"; + +const CodeJSFileCardActions = (props: { card?: boolean }) => { + const { card } = props; + return ( + <ShowDemoCode + codeCompleteJS={completeCodeJS(card)} + codeCompleteTS={completeCodeTS(card)} + codeSandboxJS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSandboxTS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" + codeSplittedJS={splittedCodeJS(card)} + codeSplittedTS={splittedCodeTS(card)} + /> + ); +}; +export default CodeJSFileCardActions; + +const splittedCodeJS = (card?: boolean) => ``; +const splittedCodeTS = (card?: boolean) => ``; + +const completeCodeJS = (card?: boolean) => `import * as React from "react"; +import { + ${card?"FileCard":"FileMosaic"}, + FullScreen, + ImagePreview, + VideoPreview, +} from "@files-ui/react"; + +const sampleFile = { + size: 28 * 1024 * 1024, + type: "text/plain", + name: "actions sampleFile.jsx", + valid: true, +}; +const IMG_URL = + "https://w0.peakpx.com/wallpaper/635/84/HD-wallpaper-thanos-and-iron-man-avengers-clouds-cloudy-face-to-face-her-iron-man-marvel-thanos-war.jpg"; +const VIDEO_URL = + "https://srv23.y2mate.is/download?file=cd448fa7c7fe6c301970e890794fb682137140"; + +const Demo${card?"FileCard":"FileMosaic"}Actions = ({ card }) => { + const [imgSrc, setImgSrc] = React.useState(undefined); + const [videoSrc, setVideoSrc] = React.useState(undefined); + const handleDelete = (id) => { + console.log("delete button clicked on file: " + id); + }; + const handleSee = (imageSource) => { + console.log("watch image:", imageSource); + setImgSrc(imageSource); + }; + const handleWatch = (videoSource) => { + console.log("watch video:", videoSource); + setVideoSrc(videoSource); + }; + const handleDownload = (fileId, downloadUrl) => { + console.log("download file id", fileId); + console.log("download url", downloadUrl); + }; + return ( + <> + <${card?"FileCard":"FileMosaic"} id={0} {...sampleFile} onDelete={handleDelete} /> + <${card?"FileCard":"FileMosaic"} id={1} {...sampleFile} info /> + <${card?"FileCard":"FileMosaic"} id={2} {...sampleFile} onSee={handleSee} imageUrl={IMG_URL} /> + <${card?"FileCard":"FileMosaic"} + id={3} + {...sampleFile} + onWatch={handleWatch} + videoUrl={VIDEO_URL} + /> + <${card?"FileCard":"FileMosaic"} id={4} {...sampleFile} downloadUrl={IMG_URL} /> + <${card?"FileCard":"FileMosaic"} + id={5} + {...sampleFile} + onDownload={handleDownload} + downloadUrl={VIDEO_URL} + /> + <FullScreen + open={imgSrc !== undefined} + onClose={() => setImgSrc(undefined)} + > + <ImagePreview src={imgSrc} /> + </FullScreen> + <FullScreen + open={videoSrc !== undefined} + onClose={() => setVideoSrc(undefined)} + > + <VideoPreview src={videoSrc} autoPlay controls /> + </FullScreen> + </> + ); +}; +export default Demo${card?"FileCard":"FileMosaic"}Actions;`; + +const completeCodeTS = (card?: boolean) => `import * as React from "react"; +import { + ${card?"FileCard":"FileMosaic"}, + FullScreen, + ImagePreview, + VideoPreview, + ExtFile, +} from "@files-ui/react"; + +const sampleFile: ExtFile = { + size: 28 * 1024 * 1024, + type: "text/plain", + name: "actions sampleFile.jsx", + valid: true, +}; +const IMG_URL = + "https://w0.peakpx.com/wallpaper/635/84/HD-wallpaper-thanos-and-iron-man-avengers-clouds-cloudy-face-to-face-her-iron-man-marvel-thanos-war.jpg"; +const VIDEO_URL = + "https://srv23.y2mate.is/download?file=cd448fa7c7fe6c301970e890794fb682137140"; + +const Demo${card?"FileCard":"FileMosaic"}Actions = (props: { card?: boolean }) => { + const [imgSrc, setImgSrc] = React.useState<string | undefined>(undefined); + const [videoSrc, setVideoSrc] = React.useState<File | string | undefined>( + undefined + ); + const handleDelete = (id: string | number | undefined) => { + console.log("delete button clicked on file: " + id); + }; + const handleSee = (imageSource: string | undefined) => { + console.log("watch image:", imageSource); + setImgSrc(imageSource); + }; + const handleWatch = (videoSource: File | string | undefined) => { + console.log("watch video:", videoSource); + setVideoSrc(videoSource); + }; + const handleDownload = ( + fileId: string | number | undefined, + downloadUrl?: string | undefined + ) => { + console.log("download file id", fileId); + console.log("download url", downloadUrl); + }; + return ( + <> + <${card?"FileCard":"FileMosaic"} id={0} {...sampleFile} onDelete={handleDelete} /> + <${card?"FileCard":"FileMosaic"} id={1} {...sampleFile} info /> + <${card?"FileCard":"FileMosaic"} id={2} {...sampleFile} onSee={handleSee} imageUrl={IMG_URL} /> + <${card?"FileCard":"FileMosaic"} + id={3} + {...sampleFile} + onWatch={handleWatch} + videoUrl={VIDEO_URL} + /> + <${card?"FileCard":"FileMosaic"} id={4} {...sampleFile} downloadUrl={IMG_URL} /> + <${card?"FileCard":"FileMosaic"} + id={5} + {...sampleFile} + onDownload={handleDownload} + downloadUrl={VIDEO_URL} + /> + <FullScreen + open={imgSrc !== undefined} + onClose={() => setImgSrc(undefined)} + > + <ImagePreview src={imgSrc} /> + </FullScreen> + <FullScreen + open={videoSrc !== undefined} + onClose={() => setVideoSrc(undefined)} + > + <VideoPreview src={videoSrc} autoPlay controls /> + </FullScreen> + </> + ); +}; +export default Demo${card?"FileCard":"FileMosaic"}Actions;`; diff --git a/src/components/demo-components/filemosaic-demo/CodeJSFileMosaicDarkMode.tsx b/src/components/demo-components/filemosaic-demo/CodeJSFileMosaicDarkMode.tsx index bdcbb484ce16bc7fc4909584fca2b957666c800d..44516d3b9a7e95421558a400f97c9e34455b273e 100644 --- a/src/components/demo-components/filemosaic-demo/CodeJSFileMosaicDarkMode.tsx +++ b/src/components/demo-components/filemosaic-demo/CodeJSFileMosaicDarkMode.tsx @@ -1,31 +1,112 @@ import * as React from "react"; import ShowDemoCode from "../../show-demo-code/ShowDemoCode"; -const CodeJSFileMosaicDarkMode = () => { +const CodeJSFileMosaicDarkMode = (props: { card?: boolean }) => { + const { card } = props; return ( <ShowDemoCode - codeCompleteJS={completeCodeJS} - codeCompleteTS={completeCodeTS} + codeCompleteJS={completeCodeJS(card)} + codeCompleteTS={completeCodeTS(card)} codeSandboxJS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" codeSandboxTS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" - codeSplittedJS={splittedCodeJS} - codeSplittedTS={splittedCodeTS} + codeSplittedJS={splittedCodeJS(card)} + codeSplittedTS={splittedCodeTS(card)} /> ); }; export default CodeJSFileMosaicDarkMode; -const splittedCodeJS = `<> - <FileMosaic {...sampleFileProps} info onDelete={removeFile} /> - <FileMosaic {...sampleFileProps} info onDelete={removeFile} darkMode/> +const splittedCodeJS = (card?: boolean) => + card + ? "" + : `<> + <${ + card ? "FileCard" : "FileMosaic" + } {...sampleFileProps} info onDelete={removeFile} /> + <${ + card ? "FileCard" : "FileMosaic" + } {...sampleFileProps} info onDelete={removeFile} darkMode/> </>`; -const splittedCodeTS = `<> -<FileMosaic {...sampleFileProps} info onDelete={removeFile} /> -<FileMosaic {...sampleFileProps} info onDelete={removeFile} darkMode/> +const splittedCodeTS = (card?: boolean) => + card + ? "" + : `<> + <${ + card ? "FileCard" : "FileMosaic" + } {...sampleFileProps} info onDelete={removeFile} /> + <${ + card ? "FileCard" : "FileMosaic" + } {...sampleFileProps} info onDelete={removeFile} darkMode/> </>`; -const completeCodeJS = `import * as React from "react"; -import { FileMosaic } from "@files-ui/react"; +const completeCodeJS = (card?: boolean) => + card + ? `import { ${card ? "FileCard" : "FileMosaic"} } from "@files-ui/react"; +const sampleFilePropsCard = (elevationNumber) => { + return { + id: "fileId", + size: 28 * 1024 * 1024, + type: "text/plain", + name: "elevation=" + elevationNumber + ".jsx", + valid: true, + }; +}; +const Demo${card ? "FileCard" : "FileMosaic"}DarkMode = () => { + const removeFile = (id) => { + console.log("delete button clicked on file: " + id); + }; + return ( + <> + <div + style={{ + display: "flex", + justifyContent: "center", + flexDirection: "column", + backgroundColor: "white", + alignItems: "center", + padding: "25px 0", + flexGrow: 1, + gap: "7px", + }} + > + {[0, 4, 16, 24].map((elevation) => ( + <FileCard + key={elevation} + {...sampleFilePropsCard(elevation)} + onDelete={removeFile} + info + elevation={elevation} + /> + ))} + </div> + <div + style={{ + display: "flex", + flexDirection: "column", + justifyContent: "center", + backgroundColor: "#121212", + alignItems: "center", + padding: "25px 0", + flexGrow: 1, + gap: "7px", + }} + > + {[0, 4, 16, 24].map((elevation) => ( + <FileCard + key={elevation} + {...sampleFilePropsCard(elevation)} + onDelete={removeFile} + darkMode + info + elevation={elevation} + /> + ))} + </div> + </> + ); +export default Demo${card ? "FileCard" : "FileMosaic"}DarkMode;` + : `import * as React from "react"; +import { ${card ? "FileCard" : "FileMosaic"} } from "@files-ui/react"; const sampleFileProps = { id: "fileId", @@ -33,42 +114,114 @@ const sampleFileProps = { type: "text/plain", name: "file created from props.jsx", }; -const DemoFileMosaicDarkMode = () => { +const Demo${card ? "FileCard" : "FileMosaic"}DarkMode = () => { const removeFile = (id) => { console.log("delete button clicked on file: " + id); }; return ( - <> + <div style={{ display: "flex", flexWrap: "wrap" }}> <div style={{ display: "flex", justifyContent: "center", - width: "50%", backgroundColor: "white", - padding: "10px 0", + padding: "25px 0", + flexGrow:1 }} > - <FileMosaic {...sampleFileProps} info onDelete={removeFile} /> + <${ + card ? "FileCard" : "FileMosaic" + } {...sampleFileProps} info onDelete={removeFile} /> </div> <div style={{ display: "flex", justifyContent: "center", - width: "50%", backgroundColor: "#121212", - padding: "10px 0", + padding: "25px 0", + flexGrow:1 }} > - <FileMosaic {...sampleFileProps} info darkMode onDelete={removeFile} /> + <${ + card ? "FileCard" : "FileMosaic" + } {...sampleFileProps} info darkMode onDelete={removeFile} /> </div> - </> + </div> ); }; -export default DemoFileMosaicDarkMode; -`; +export default Demo${card ? "FileCard" : "FileMosaic"}DarkMode;`; -const completeCodeTS = `import * as React from "react"; -import { ExtFile, FileMosaic } from "@files-ui/react"; +const completeCodeTS = (card?: boolean) => + card + ? `import { ${ + card ? "FileCard" : "FileMosaic" + }, ExtFile, FileCardProps } from "@files-ui/react"; +const sampleFilePropsCard = (elevationNumber: number): ExtFile => { + return { + id: "fileId", + size: 28 * 1024 * 1024, + type: "text/plain", + name: "elevation=" + elevationNumber + ".jsx", + valid: true, + }; +}; +const Demo${card ? "FileCard" : "FileMosaic"}DarkMode = () => { + const removeFile = (id: string | number | undefined) => { + console.log("delete button clicked on file: " + id); + }; + return ( + <> + <div + style={{ + display: "flex", + justifyContent: "center", + flexDirection: "column", + backgroundColor: "white", + alignItems: "center", + padding: "25px 0", + flexGrow: 1, + gap: "7px", + }} + > + {[0, 4, 16, 24].map((elevation) => ( + <FileCard + key={elevation} + {...sampleFilePropsCard(elevation)} + onDelete={removeFile} + info + elevation={elevation as FileCardProps["elevation"]} + /> + ))} + </div> + <div + style={{ + display: "flex", + flexDirection: "column", + justifyContent: "center", + backgroundColor: "#121212", + alignItems: "center", + padding: "25px 0", + flexGrow: 1, + gap: "7px", + }} + > + {[0, 4, 16, 24].map((elevation) => ( + <FileCard + key={elevation} + {...sampleFilePropsCard(elevation)} + onDelete={removeFile} + darkMode + info + elevation={elevation as FileCardProps["elevation"]} + /> + ))} + </div> + </> + ); +export default Demo${card ? "FileCard" : "FileMosaic"}DarkMode; +` + : `import * as React from "react"; +import { ExtFile, ${card ? "FileCard" : "FileMosaic"} } from "@files-ui/react"; const sampleFileProps: ExtFile = { id: "fileId", @@ -76,36 +229,39 @@ const sampleFileProps: ExtFile = { type: "text/plain", name: "file created from props.jsx", }; -const DemoFileMosaicDarkMode = () => { +const Demo${card ? "FileCard" : "FileMosaic"}DarkMode = () => { const removeFile = (id: string | number | undefined) => { console.log("delete button clicked on file: " + id); }; return ( - <> + <div style={{ display: "flex", flexWrap: "wrap" }}> <div style={{ display: "flex", justifyContent: "center", - width: "50%", backgroundColor: "white", - padding: "10px 0", + padding: "25px 0", + flexGrow:1 }} > - <FileMosaic {...sampleFileProps} info onDelete={removeFile} /> + <${ + card ? "FileCard" : "FileMosaic" + } {...sampleFileProps} info onDelete={removeFile} /> </div> <div style={{ display: "flex", justifyContent: "center", - width: "50%", backgroundColor: "#121212", - padding: "10px 0", + padding: "25px 0", + flexGrow:1 }} > - <FileMosaic {...sampleFileProps} info darkMode onDelete={removeFile} /> + <${ + card ? "FileCard" : "FileMosaic" + } {...sampleFileProps} info darkMode onDelete={removeFile} /> </div> - </> + </div> ); }; -export default DemoFileMosaicDarkMode; -`; +export default Demo${card ? "FileCard" : "FileMosaic"}DarkMode;`; diff --git a/src/components/demo-components/filemosaic-demo/CodeJSFileMosaicImagePreview.tsx b/src/components/demo-components/filemosaic-demo/CodeJSFileMosaicImagePreview.tsx index 63d00ec046e154d51b75b1eb7ef57372af907bcb..4ea1437c8f46e20d6d8e516a70665cb19d79e857 100644 --- a/src/components/demo-components/filemosaic-demo/CodeJSFileMosaicImagePreview.tsx +++ b/src/components/demo-components/filemosaic-demo/CodeJSFileMosaicImagePreview.tsx @@ -1,27 +1,28 @@ import * as React from "react"; import ShowDemoCode from "../../show-demo-code/ShowDemoCode"; -const CodeJSFileMosaicImagePreview = () => { +const CodeJSFileMosaicImagePreview = (props:{card:boolean}) => { + const {card}=props; return ( <ShowDemoCode - codeCompleteJS={completeCodeJS} - codeCompleteTS={completeCodeTS} + codeCompleteJS={completeCodeJS(card)} + codeCompleteTS={completeCodeTS(card)} codeSandboxJS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" codeSandboxTS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" - codeSplittedJS={splittedCodeJS} - codeSplittedTS={splittedCodeTS} + codeSplittedJS={splittedCodeJS(card)} + codeSplittedTS={splittedCodeTS(card)} /> ); }; export default CodeJSFileMosaicImagePreview; -const splittedCodeJS = `<> +const splittedCodeJS = (card:boolean)=>`<> {value ? ( - <FileMosaic {...value} onDelete={removeFile} info preview/> + <${card?"FileCard":"FileMosaic"} {...value} onDelete={removeFile} info preview/> ) : ( <FileInputButton onChange={updateFile} accept="image/*"/> )} - <FileMosaic {...sampleFileProps} info/> + <${card?"FileCard":"FileMosaic"} {...sampleFileProps} info/> </> // file props @@ -33,8 +34,8 @@ const sampleFileProps = { imageUrl:"https://cdn.wallpapersafari.com/0/95/1zms6H.jpg" };`; -const completeCodeJS = `import * as React from "react"; -import { InputButton, FileMosaic } from "@files-ui/react"; +const completeCodeJS = (card:boolean)=>`import * as React from "react"; +import { InputButton, ${card?"FileCard":"FileMosaic"} } from "@files-ui/react"; const sampleFileProps = { id: "fileId", @@ -57,22 +58,22 @@ export default function App() { return ( <div style={{display:"flex", gap:"10px"}}> {value ? ( - <FileMosaic {...value} onDelete={removeFile} info preview/> + <${card?"FileCard":"FileMosaic"} {...value} onDelete={removeFile} info preview/> ) : ( <FileInputButton onChange={updateFile} accept="image/*"/> )} - <FileMosaic {...sampleFileProps} info/> + <${card?"FileCard":"FileMosaic"} {...sampleFileProps} info/> </div> ); };`; -const splittedCodeTS = `<> +const splittedCodeTS = (card:boolean)=>`<> {value ? ( - <FileMosaic {...value} onDelete={removeFile} info preview/> + <${card?"FileCard":"FileMosaic"} {...value} onDelete={removeFile} info preview/> ) : ( <FileInputButton onChange={updateFile} accept="image/*"/> )} -<FileMosaic {...sampleFileProps} info/> +<${card?"FileCard":"FileMosaic"} {...sampleFileProps} info/> </> // file props @@ -83,8 +84,8 @@ const sampleFileProps: ExtFile = { name: "Thor arrives wakanda.jpg", imageUrl:"https://cdn.wallpapersafari.com/0/95/1zms6H.jpg" };`; -const completeCodeTS = `import * as React from "react"; -import { InputButton, FileMosaic, ExtFile } from "@files-ui/react"; +const completeCodeTS = (card:boolean)=>`import * as React from "react"; +import { InputButton, ${card?"FileCard":"FileMosaic"}, ExtFile } from "@files-ui/react"; const sampleFileProps: ExtFile = { id: "fileId", @@ -107,11 +108,11 @@ export default function App() { return ( <div style={{display:"flex", gap:"10px"}}> {value ? ( - <FileMosaic {...value} onDelete={removeFile} info preview/> + <${card?"FileCard":"FileMosaic"} {...value} onDelete={removeFile} info preview/> ) : ( <FileInputButton onChange={updateFile} accept="image/*"/> )} - <FileMosaic {...sampleFileProps} info/> + <${card?"FileCard":"FileMosaic"} {...sampleFileProps} info/> </div> ); };`; diff --git a/src/components/demo-components/filemosaic-demo/CodeJSFileMosaicUploadStatus.jsx b/src/components/demo-components/filemosaic-demo/CodeJSFileMosaicUploadStatus.jsx index 0ee972514373b1bef851907506b6c5d59c6c71eb..6daac5179658221e44b380226e00a458a90c8b07 100644 --- a/src/components/demo-components/filemosaic-demo/CodeJSFileMosaicUploadStatus.jsx +++ b/src/components/demo-components/filemosaic-demo/CodeJSFileMosaicUploadStatus.jsx @@ -1,31 +1,31 @@ import * as React from "react"; import ShowDemoCode from "../../show-demo-code/ShowDemoCode"; -const CodeJSFileMosaicUploadStatus = () => { +const CodeJSFileMosaicUploadStatus = ({card}) => { return ( <ShowDemoCode - codeCompleteJS={completeCodeJS} - codeCompleteTS={completeCodeTS} + codeCompleteJS={completeCodeJS(card)} + codeCompleteTS={completeCodeTS(card)} codeSandboxJS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" codeSandboxTS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" - codeSplittedJS={splittedCodeJS} - codeSplittedTS={splittedCodeTS} + codeSplittedJS={splittedCodeJS(card)} + codeSplittedTS={splittedCodeTS(card)} /> ); }; export default CodeJSFileMosaicUploadStatus; -const splittedCodeJS = ``; +const splittedCodeJS =(card)=> ``; -const splittedCodeTS = ``; +const splittedCodeTS =(card)=> ``; -const completeCodeJS = `import * as React from "react"; +const completeCodeJS =(card)=> `import * as React from "react"; import { - FileMosaic, + ${card?"FileCard":"FileMosaic"}, useFakeProgress, } from "@files-ui/react"; -const DemoFileMosaicUploadStatus = () => { +const Demo${card?"FileCard":"FileMosaic"}UploadStatus = () => { const progress = useFakeProgress(); const [status1, setStatus1] = React.useState("uploading"); @@ -57,26 +57,26 @@ const DemoFileMosaicUploadStatus = () => { return ( <> <FlexRowContainer> - <FileMosaic {...preparingFile} /> - <FileMosaic {...preparingFile} onCancel={handleCancel} /> + <${card?"FileCard":"FileMosaic"} {...preparingFile} /> + <${card?"FileCard":"FileMosaic"} {...preparingFile} onCancel={handleCancel} /> </FlexRowContainer> <FlexRowContainer> - <FileMosaic {...uploadingFile} /> - <FileMosaic {...uploadingFile} progress={progress} /> - <FileMosaic {...uploadingFile} onAbort={handleAbort} /> - <FileMosaic {...uploadingFile} onAbort={handleAbort} progress={progress} /> + <${card?"FileCard":"FileMosaic"} {...uploadingFile} /> + <${card?"FileCard":"FileMosaic"} {...uploadingFile} progress={progress} /> + <${card?"FileCard":"FileMosaic"} {...uploadingFile} onAbort={handleAbort} /> + <${card?"FileCard":"FileMosaic"} {...uploadingFile} onAbort={handleAbort} progress={progress} /> </FlexRowContainer> <FlexRowContainer> - <FileMosaic {...uploadResultFiles[0]} uploadStatus={status1} /> - <FileMosaic {...uploadResultFiles[1]} uploadStatus={status2} /> - <FileMosaic {...uploadResultFiles[2]} uploadStatus={status3} /> + <${card?"FileCard":"FileMosaic"} {...uploadResultFiles[0]} uploadStatus={status1} /> + <${card?"FileCard":"FileMosaic"} {...uploadResultFiles[1]} uploadStatus={status2} /> + <${card?"FileCard":"FileMosaic"} {...uploadResultFiles[2]} uploadStatus={status3} /> </FlexRowContainer> </> ); }; -export default DemoFileMosaicUploadStatus; +export default Demo${card?"FileCard":"FileMosaic"}UploadStatus; const FlexRowContainer = ({ children }) => { return ( @@ -142,15 +142,15 @@ const uploadResultFiles = [ }, ];`; -const completeCodeTS = `import * as React from "react"; +const completeCodeTS =(card)=> `import * as React from "react"; import { - FileMosaic, + ${card?"FileCard":"FileMosaic"}, useFakeProgress, ExtFile, UPLOADSTATUS, } from "@files-ui/react"; -const DemoFileMosaicUploadStatus = () => { +const Demo${card?"FileCard":"FileMosaic"}UploadStatus = () => { const progress = useFakeProgress(); const [status1, setStatus1] = React.useState<UPLOADSTATUS>("uploading"); @@ -182,26 +182,26 @@ const DemoFileMosaicUploadStatus = () => { return ( <> <FlexRowContainer> - <FileMosaic {...preparingFile} /> - <FileMosaic {...preparingFile} onCancel={handleCancel} /> + <${card?"FileCard":"FileMosaic"} {...preparingFile} /> + <${card?"FileCard":"FileMosaic"} {...preparingFile} onCancel={handleCancel} /> </FlexRowContainer> <FlexRowContainer> - <FileMosaic {...uploadingFile} /> - <FileMosaic {...uploadingFile} progress={progress} /> - <FileMosaic {...uploadingFile} onAbort={handleAbort} /> - <FileMosaic {...uploadingFile} onAbort={handleAbort} progress={progress} /> + <${card?"FileCard":"FileMosaic"} {...uploadingFile} /> + <${card?"FileCard":"FileMosaic"} {...uploadingFile} progress={progress} /> + <${card?"FileCard":"FileMosaic"} {...uploadingFile} onAbort={handleAbort} /> + <${card?"FileCard":"FileMosaic"} {...uploadingFile} onAbort={handleAbort} progress={progress} /> </FlexRowContainer> <FlexRowContainer> - <FileMosaic {...uploadResultFiles[0]} uploadStatus={status1} /> - <FileMosaic {...uploadResultFiles[1]} uploadStatus={status2} /> - <FileMosaic {...uploadResultFiles[2]} uploadStatus={status3} /> + <${card?"FileCard":"FileMosaic"} {...uploadResultFiles[0]} uploadStatus={status1} /> + <${card?"FileCard":"FileMosaic"} {...uploadResultFiles[1]} uploadStatus={status2} /> + <${card?"FileCard":"FileMosaic"} {...uploadResultFiles[2]} uploadStatus={status3} /> </FlexRowContainer> </> ); }; -export default DemoFileMosaicUploadStatus; +export default Demo${card?"FileCard":"FileMosaic"}UploadStatus; const FlexRowContainer = (props: { children: React.ReactNode }) => { return ( diff --git a/src/components/demo-components/filemosaic-demo/CodeJSFileMosaicValidation.jsx b/src/components/demo-components/filemosaic-demo/CodeJSFileMosaicValidation.jsx index 435488cb7cc7f0dadaeffbd45acee903c758b373..3bd8a473455ac2df4a8e9c3ba6fdf132319df5da 100644 --- a/src/components/demo-components/filemosaic-demo/CodeJSFileMosaicValidation.jsx +++ b/src/components/demo-components/filemosaic-demo/CodeJSFileMosaicValidation.jsx @@ -1,36 +1,35 @@ import * as React from "react"; import ShowDemoCode from "../../show-demo-code/ShowDemoCode"; -const CodeJSFileMosaicValidation = () => { +const CodeJSFileMosaicValidation = (card) => { return ( <ShowDemoCode - codeCompleteJS={completeCodeJS} - codeCompleteTS={completeCodeTS} + codeCompleteJS={completeCodeJS(card)} + codeCompleteTS={completeCodeTS(card)} codeSandboxJS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" codeSandboxTS="https://codesandbox.io/s/dropzone-ui-basic-3j01v" - codeSplittedJS={splittedCodeJS} - codeSplittedTS={splittedCodeTS} + codeSplittedJS={splittedCodeJS(card)} + codeSplittedTS={splittedCodeTS(card)} /> ); }; export default CodeJSFileMosaicValidation; -const splittedCodeJS = ``; -const splittedCodeTS = ``; +const splittedCodeJS = (card)=>``; +const splittedCodeTS = (card)=>``; -const completeCodeJS = `import * as React from "react"; -import { FileMosaic } from "../../../files-ui"; +const completeCodeJS = (card)=>`import * as React from "react"; +import { ${card?"FileCard":"FileMosaic"} } from "@files-ui/react"; -const DemoFileMosaicValidation = () => { +export default function App() { return ( <div style={{display:"flex", gap:"10px"}}> {sampleFilesProps.map((extFile) => ( - <FileMosaic key={extFile.id} valid={extFile.valid} {...extFile} info/> + <${card?"FileCard":"FileMosaic"} key={extFile.id} valid={extFile.valid} {...extFile} info/> ))} </div> ); }; -export default DemoFileMosaicValidation; const sampleFilesProps = [ { @@ -56,19 +55,18 @@ const sampleFilesProps = [ }, ];`; -const completeCodeTS = `import * as React from "react"; -import { FileMosaic, ExtFile } from "../../../files-ui"; +const completeCodeTS = (card)=>`import * as React from "react"; +import { ${card?"FileCard":"FileMosaic"}, ExtFile } from "@files-ui/react"; -const DemoFileMosaicValidation = () => { +export default function App() { return ( <div style={{display:"flex", gap:"10px"}}> {sampleFilesProps.map((extFile: ExtFile) => ( - <FileMosaic key={extFile.id} valid={extFile.valid} {...extFile} info/> + <${card?"FileCard":"FileMosaic"} key={extFile.id} valid={extFile.valid} {...extFile} info/> ))} </div> ); }; -export default DemoFileMosaicValidation; const sampleFilesProps: ExtFile[] = [ { diff --git a/src/components/demo-components/filemosaic-demo/DemoContainerFileMosaic.jsx b/src/components/demo-components/filemosaic-demo/DemoContainerFileMosaic.jsx index 623359b3d2d99f3f5a6ba2eea2630d861cc44ace..9e63d14e64fcacc2a0cf34ada2709eb2138fe016 100644 --- a/src/components/demo-components/filemosaic-demo/DemoContainerFileMosaic.jsx +++ b/src/components/demo-components/filemosaic-demo/DemoContainerFileMosaic.jsx @@ -1,7 +1,7 @@ -import { Paper, Stack } from "@mui/material"; +import { Paper, Stack, Box } from "@mui/material"; import * as React from "react"; -const DemoContainerFileMosaic = ({ children }) => { +const DemoContainerFileMosaic = ({ children, card }) => { return ( <Paper variant="outlined" @@ -12,15 +12,21 @@ const DemoContainerFileMosaic = ({ children }) => { justifyContent: "center", }} > - <Stack - spacing={4} - direction="row" - alignItems={"center"} - flexWrap="wrap" - justifyContent={"space-evenly"} + <Box + sx={{ + display: "flex", + gap: "20px", + flexWrap: "wrap", + flexDirection: { xs: card ? "column" : undefined, md: "row" }, + justifyContent: { + xs: "center", + md: "space-evenly", + }, + alignItems: { xs: "center" }, + }} > {children} - </Stack> + </Box> </Paper> ); }; diff --git a/src/components/demo-components/filemosaic-demo/DemoFileCradActions.tsx b/src/components/demo-components/filemosaic-demo/DemoFileCradActions.tsx new file mode 100644 index 0000000000000000000000000000000000000000..08e9359493b5e2273ce20df6f51787eeefebe47d --- /dev/null +++ b/src/components/demo-components/filemosaic-demo/DemoFileCradActions.tsx @@ -0,0 +1,111 @@ +import * as React from "react"; +import { + FileCard, + FileMosaic, + FullScreen, + ImagePreview, + VideoPreview, + ExtFile, +} from "../../../files-ui"; + +const sampleFile: ExtFile = { + size: 28 * 1024 * 1024, + type: "text/plain", + name: "actions sampleFile.jsx", + valid: true, +}; +const IMG_URL = + "https://w0.peakpx.com/wallpaper/635/84/HD-wallpaper-thanos-and-iron-man-avengers-clouds-cloudy-face-to-face-her-iron-man-marvel-thanos-war.jpg"; +const VIDEO_URL = + "https://srv23.y2mate.is/download?file=cd448fa7c7fe6c301970e890794fb682137140"; + +const DemoFileCardActions = (props: { card?: boolean }) => { + const [imgSrc, setImgSrc] = React.useState<string | undefined>(undefined); + const [videoSrc, setVideoSrc] = React.useState<File | string | undefined>( + undefined + ); + const handleDelete = (id: string | number | undefined) => { + console.log("delete button clicked on file: " + id); + }; + const handleSee = (imageSource: string | undefined) => { + console.log("watch image:", imageSource); + setImgSrc(imageSource); + }; + const handleWatch = (videoSource: File | string | undefined) => { + console.log("watch video:", videoSource); + setVideoSrc(videoSource); + }; + const handleDownload = ( + fileId: string | number | undefined, + downloadUrl?: string | undefined + ) => { + console.log("download file id", fileId); + console.log("download url", downloadUrl); + }; + if (props.card) + return ( + <> + <FileCard id={0} {...sampleFile} onDelete={handleDelete} /> + <FileCard id={1} {...sampleFile} info /> + <FileCard id={2} {...sampleFile} onSee={handleSee} imageUrl={IMG_URL} /> + <FileCard + id={3} + {...sampleFile} + onWatch={handleWatch} + videoUrl={VIDEO_URL} + /> + <FileCard id={4} {...sampleFile} downloadUrl={IMG_URL} /> + <FileCard + id={5} + {...sampleFile} + onDownload={handleDownload} + downloadUrl={VIDEO_URL} + /> + <FullScreen + open={imgSrc !== undefined} + onClose={() => setImgSrc(undefined)} + > + <ImagePreview src={imgSrc} /> + </FullScreen> + <FullScreen + open={videoSrc !== undefined} + onClose={() => setVideoSrc(undefined)} + > + <VideoPreview src={videoSrc} autoPlay controls /> + </FullScreen> + </> + ); + return ( + <> + <FileMosaic id={0} {...sampleFile} onDelete={handleDelete} /> + <FileMosaic id={1} {...sampleFile} info /> + <FileMosaic id={2} {...sampleFile} onSee={handleSee} imageUrl={IMG_URL} /> + <FileMosaic + id={3} + {...sampleFile} + onWatch={handleWatch} + videoUrl={VIDEO_URL} + /> + <FileMosaic id={4} {...sampleFile} downloadUrl={IMG_URL} /> + <FileMosaic + id={5} + {...sampleFile} + onDownload={handleDownload} + downloadUrl={VIDEO_URL} + /> + <FullScreen + open={imgSrc !== undefined} + onClose={() => setImgSrc(undefined)} + > + <ImagePreview src={imgSrc} /> + </FullScreen> + <FullScreen + open={videoSrc !== undefined} + onClose={() => setVideoSrc(undefined)} + > + <VideoPreview src={videoSrc} autoPlay controls /> + </FullScreen> + </> + ); +}; +export default DemoFileCardActions; diff --git a/src/components/demo-components/filemosaic-demo/DemoFileMosaicDarkMode.tsx b/src/components/demo-components/filemosaic-demo/DemoFileMosaicDarkMode.tsx index a878f3fa86f042b278fbd84c9add44021d3925ed..14216b7c139e9f974632c1c1e7b250858cb73301 100644 --- a/src/components/demo-components/filemosaic-demo/DemoFileMosaicDarkMode.tsx +++ b/src/components/demo-components/filemosaic-demo/DemoFileMosaicDarkMode.tsx @@ -1,25 +1,91 @@ import * as React from "react"; -import { ExtFile, FileMosaic } from "../../../files-ui"; - -const sampleFileProps: ExtFile = { +import { + ExtFile, + FileCard, + FileCardProps, + FileMosaic, +} from "../../../files-ui"; +const sampleFileProps = { id: "fileId", size: 28 * 1024 * 1024, type: "text/plain", - name: "file created from props.jsx", + name: "sampleFile" + ".jsx", + valid: true, +}; +const sampleFilePropsCard = (elevationNumber: number): ExtFile => { + return { + id: "fileId", + size: 28 * 1024 * 1024, + type: "text/plain", + name: "elevation=" + elevationNumber + ".jsx", + valid: true, + }; }; -const DemoFileMosaicDarkMode = () => { + +const DemoFileMosaicDarkMode = (props: { card?: boolean }) => { const removeFile = (id: string | number | undefined) => { console.log("delete button clicked on file: " + id); }; + if (props.card) + return ( + <> + <div + style={{ + display: "flex", + justifyContent: "center", + flexDirection: "column", + backgroundColor: "white", + alignItems: "center", + padding: "25px 0", + flexGrow: 1, + gap: "7px", + }} + > + {[0, 4, 16, 24].map((elevation) => ( + <FileCard + key={elevation} + {...sampleFilePropsCard(elevation)} + onDelete={removeFile} + info + elevation={elevation as FileCardProps["elevation"]} + /> + ))} + </div> + <div + style={{ + display: "flex", + flexDirection: "column", + justifyContent: "center", + backgroundColor: "#121212", + alignItems: "center", + padding: "25px 0", + flexGrow: 1, + gap: "7px", + }} + > + {[0, 4, 16, 24].map((elevation) => ( + <FileCard + key={elevation} + {...sampleFilePropsCard(elevation)} + onDelete={removeFile} + darkMode + info + elevation={elevation as FileCardProps["elevation"]} + /> + ))} + </div> + </> + ); return ( <> <div style={{ display: "flex", justifyContent: "center", - width: "50%", + minWidth: "50%", backgroundColor: "white", padding: "15px 0", + flexGrow: 1, }} > <FileMosaic {...sampleFileProps} info onDelete={removeFile} /> @@ -28,9 +94,10 @@ const DemoFileMosaicDarkMode = () => { style={{ display: "flex", justifyContent: "center", - width: "50%", + minWidth: "50%", backgroundColor: "#121212", padding: "15px 0", + flexGrow: 1, }} > <FileMosaic {...sampleFileProps} info darkMode onDelete={removeFile} /> diff --git a/src/components/demo-components/filemosaic-demo/DemoFileMosaicImagePreview.tsx b/src/components/demo-components/filemosaic-demo/DemoFileMosaicImagePreview.tsx index f59b0708432269eee2a25f4c21ac885920202ec7..0185a47df1a2a3b89a92d07238d817bbe7bc8d6c 100644 --- a/src/components/demo-components/filemosaic-demo/DemoFileMosaicImagePreview.tsx +++ b/src/components/demo-components/filemosaic-demo/DemoFileMosaicImagePreview.tsx @@ -1,8 +1,14 @@ import * as React from "react"; -import { FileInputButton, FileMosaic } from "../../../files-ui"; -import { ExtFile } from "../../../files-ui/core"; +import { + FileCard, + FileInputButton, + FileMosaic, + ExtFile, +} from "../../../files-ui"; -interface DemoFileMosaicImagePreviewProps {} +interface DemoFileMosaicImagePreviewProps { + card?: boolean; +} const sampleFileProps: ExtFile = { id: "fileId", @@ -24,10 +30,25 @@ const DemoFileMosaicImagePreview: React.FC<DemoFileMosaicImagePreviewProps> = ( const removeFile = () => { setValue(undefined); }; + if (props.card) + return ( + <> + {value ? ( + <FileCard {...value} onDelete={removeFile} info preview /> + ) : ( + <FileInputButton + value={value ? [value] : []} + onChange={updateFile} + accept="image/*" + /> + )} + <FileCard {...sampleFileProps} info /> + </> + ); return ( <> {value ? ( - <FileMosaic {...value} onDelete={removeFile} info preview/> + <FileMosaic {...value} onDelete={removeFile} info preview /> ) : ( <FileInputButton value={value ? [value] : []} @@ -35,7 +56,7 @@ const DemoFileMosaicImagePreview: React.FC<DemoFileMosaicImagePreviewProps> = ( accept="image/*" /> )} - <FileMosaic {...sampleFileProps} info/> + <FileMosaic {...sampleFileProps} info /> </> ); }; diff --git a/src/components/demo-components/filemosaic-demo/DemoFileMosaicImgPreview.tsx b/src/components/demo-components/filemosaic-demo/DemoFileMosaicImgPreview.tsx index b7b4cb30ee65d95bbc25ecf78c37c5e76eed608b..c65c711408450430433a96c982875c7396e7f9d4 100644 --- a/src/components/demo-components/filemosaic-demo/DemoFileMosaicImgPreview.tsx +++ b/src/components/demo-components/filemosaic-demo/DemoFileMosaicImgPreview.tsx @@ -1,43 +1,42 @@ import * as React from "react"; -import { FileMosaic, FileInputButton } from "../../../files-ui"; -import { ExtFile } from "../../../files-ui/core"; -interface DemoFileMosaicImgPreviewProps{ +import { FileMosaic, FileInputButton, ExtFile } from "../../../files-ui"; +interface DemoFileMosaicImgPreviewProps {} +const sampleFileProps: ExtFile = { + id: ":0:", + size: 28 * 1024 * 1024, + type: "image/png", + name: "Thor arrives Wakanda.png", + imageUrl: + "https://www.google.com/url?sa=i&url=https%3A%2F%2Fwww.disneylatino.com%2Fnovedades%2Favengers-la-escena-postcredito-de-thor-que-fue-clave-en-infinity-war&psig=AOvVaw2wijpnp7AqNfVNPszdMkOw&ust=1677372146505000&source=images&cd=vfe&ved=0CBAQjRxqFwoTCIj_pqy4r_0CFQAAAAAdAAAAABAQ", +}; +const DemoFileMosaicImgPreview: React.FC<DemoFileMosaicImgPreviewProps> = ( + props: DemoFileMosaicImgPreviewProps +) => { + const [value, setValue] = React.useState<ExtFile | undefined>(undefined); -} -const sampleFileProps:ExtFile = { - id: ":0:", - size: 28 * 1024 * 1024, - type: "image/png", - name: "Thor arrives Wakanda.png", - imageUrl:"https://www.google.com/url?sa=i&url=https%3A%2F%2Fwww.disneylatino.com%2Fnovedades%2Favengers-la-escena-postcredito-de-thor-que-fue-clave-en-infinity-war&psig=AOvVaw2wijpnp7AqNfVNPszdMkOw&ust=1677372146505000&source=images&cd=vfe&ved=0CBAQjRxqFwoTCIj_pqy4r_0CFQAAAAAdAAAAABAQ" + const updateFiles = (incommingFiles: ExtFile[]) => { + console.log("incomming extFiles", incommingFiles); + + setValue(incommingFiles[0]); + }; + const removeFile = () => { + setValue(undefined); }; -const DemoFileMosaicImgPreview:React.FC<DemoFileMosaicImgPreviewProps> = (props:DemoFileMosaicImgPreviewProps) =>{ - - const [value, setValue] = React.useState<ExtFile | undefined>(undefined); - const updateFiles = (incommingFiles:ExtFile[]) => { - console.log("incomming extFiles", incommingFiles); - - setValue(incommingFiles[0]); - }; - const removeFile = () => { - setValue(undefined); - }; - - return( - <> - {value ? ( - <FileMosaic {...value} onDelete={removeFile} alwaysActive info /> - ) : ( - <FileInputButton - label="Browse File..." - onChange={updateFiles} - textDecoration="uppercase" - accept="image/*" - /> - )} - <FileMosaic {...sampleFileProps} onDelete={() => {}} alwaysActive info /> - </> - ) -} -export default DemoFileMosaicImgPreview; \ No newline at end of file + return ( + <> + {value ? ( + <FileMosaic {...value} onDelete={removeFile} alwaysActive info /> + ) : ( + <FileInputButton + label="Browse File..." + onChange={updateFiles} + textDecoration="uppercase" + accept="image/*" + /> + )} + <FileMosaic {...sampleFileProps} onDelete={() => {}} alwaysActive info /> + </> + ); +}; +export default DemoFileMosaicImgPreview; diff --git a/src/components/demo-components/filemosaic-demo/DemoFileMosaicUpload.scss b/src/components/demo-components/filemosaic-demo/DemoFileMosaicUpload.scss new file mode 100644 index 0000000000000000000000000000000000000000..a90ac865c623c3bae5d76277dff42be8d8bd97b0 --- /dev/null +++ b/src/components/demo-components/filemosaic-demo/DemoFileMosaicUpload.scss @@ -0,0 +1,20 @@ +/* .flex-container-group-group { + display: flex; + flex-direction: row; + gap: 10px; +} */ +.flex-container-group-card { + display: flex; + flex-direction: column; + flex-wrap: wrap; + gap: 10px; +} +.flex-container-group-mosaic { + display: flex; + flex-direction: row; + flex-wrap: wrap; + gap: 10px; + align-items: center; + justify-content: center; + } + diff --git a/src/components/demo-components/filemosaic-demo/DemoFileMosaicUploadStatus.tsx b/src/components/demo-components/filemosaic-demo/DemoFileMosaicUploadStatus.tsx index ed044dff66fa76e7a871647996712dd8e1c5edb2..8668e4f2ba3e3cf276a567419e6bb00478012ad9 100644 --- a/src/components/demo-components/filemosaic-demo/DemoFileMosaicUploadStatus.tsx +++ b/src/components/demo-components/filemosaic-demo/DemoFileMosaicUploadStatus.tsx @@ -4,9 +4,12 @@ import { useFakeProgress, ExtFile, UPLOADSTATUS, + FileCard, } from "../../../files-ui"; +import TypeHighlight from "../../typeHighlight/TypeHighlight"; -const DemoFileMosaicUploadStatus = () => { +import "./DemoFileMosaicUpload.scss"; +const DemoFileMosaicUploadStatus = (props: { card?: boolean }) => { const progress = useFakeProgress(); const [status1, setStatus1] = React.useState<UPLOADSTATUS>("uploading"); @@ -35,14 +38,40 @@ const DemoFileMosaicUploadStatus = () => { const handleAbort = (id: string | number | undefined) => { console.log("Upload aborted in file: " + id); }; + if (props.card) + return ( + <> + <FlexRowContainer card title={"preparing stage"}> + <FileCard {...preparingFile} /> + <FileCard {...preparingFile} onCancel={handleCancel} /> + </FlexRowContainer> + + <FlexRowContainer card title={"uploading stage"}> + <FileCard {...uploadingFile} /> + <FileCard {...uploadingFile} progress={progress} /> + <FileCard {...uploadingFile} onAbort={handleAbort} /> + <FileCard + {...uploadingFile} + onAbort={handleAbort} + progress={progress} + /> + </FlexRowContainer> + + <FlexRowContainer card title={"upload result stage"}> + <FileCard {...uploadResultFiles[0]} uploadStatus={status1} /> + <FileCard {...uploadResultFiles[1]} uploadStatus={status2} /> + <FileCard {...uploadResultFiles[2]} uploadStatus={status3} /> + </FlexRowContainer> + </> + ); return ( <> - <FlexRowContainer> + <FlexRowContainer title={"preparing stage"}> <FileMosaic {...preparingFile} /> <FileMosaic {...preparingFile} onCancel={handleCancel} /> </FlexRowContainer> - <FlexRowContainer> + <FlexRowContainer title={"uploading stage"}> <FileMosaic {...uploadingFile} /> <FileMosaic {...uploadingFile} progress={progress} /> <FileMosaic {...uploadingFile} onAbort={handleAbort} /> @@ -53,7 +82,7 @@ const DemoFileMosaicUploadStatus = () => { /> </FlexRowContainer> - <FlexRowContainer> + <FlexRowContainer title={"upload result stage"}> <FileMosaic {...uploadResultFiles[0]} uploadStatus={status1} /> <FileMosaic {...uploadResultFiles[1]} uploadStatus={status2} /> <FileMosaic {...uploadResultFiles[2]} uploadStatus={status3} /> @@ -63,18 +92,24 @@ const DemoFileMosaicUploadStatus = () => { }; export default DemoFileMosaicUploadStatus; -const FlexRowContainer = (props: { children: React.ReactNode }) => { +const FlexRowContainer = (props: { + children: React.ReactNode; + card?: boolean; + title?: string; +}) => { + const { children, card, title } = props; return ( - <div - style={{ - display: "flex", - flexWrap: "wrap", - justifyContent: "space-evenly", - width: "100%", - }} - > - {props.children} - </div> + <React.Fragment> + {!card && <h4 style={{ margin: 0 }}>{title}</h4>} + <div + className={ + card ? "flex-container-group-card" : "flex-container-group-mosaic" + } + > + {card && <h4 style={{ margin: 0 }}>{title}</h4>} + {children} + </div> + </React.Fragment> ); }; diff --git a/src/components/demo-components/filemosaic-demo/DemoFileMosaicValidation.jsx b/src/components/demo-components/filemosaic-demo/DemoFileMosaicValidation.jsx index 0c3d1a9fb6c579ab89d21074d52a30fa0ef0053d..f02c9956d3ec6f0adf7b9d271f039aa20413fe1d 100644 --- a/src/components/demo-components/filemosaic-demo/DemoFileMosaicValidation.jsx +++ b/src/components/demo-components/filemosaic-demo/DemoFileMosaicValidation.jsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { FileMosaic } from "../../../files-ui"; +import { FileCard, FileMosaic } from "../../../files-ui"; const sampleFilesProps = [ { @@ -14,7 +14,7 @@ const sampleFilesProps = [ type: "image/png", name: "valid file created from props.png", valid: false, - errors: ["File is too big", "File type is not allowed"] + errors: ["File is too big", "File type is not allowed"], }, { id: "fileId-3", @@ -25,7 +25,16 @@ const sampleFilesProps = [ }, ]; -const DemoFileMosaicValidation = () => { +const DemoFileMosaicValidation = ({ card }) => { + if (card) + return ( + <> + {sampleFilesProps.map((extFile) => ( + <FileCard key={extFile.id} {...extFile} info /> + ))} + </> + ); + return ( <> {sampleFilesProps.map((extFile) => ( diff --git a/src/data/videoLinks.js b/src/data/videoLinks.js new file mode 100644 index 0000000000000000000000000000000000000000..01e7e05a8574a7273afb5c10dce4298b5802a061 --- /dev/null +++ b/src/data/videoLinks.js @@ -0,0 +1,7 @@ +const BASE_URL = + "http://10.100.179.201:2800/39d33dff2d41b522c1ea276c4b82507f96b9699493d2e7b3f5c864ba743d9503/static"; + +export const ThorArrivesWakandaEN = BASE_URL+"/ThorEN.mp4"; +export const ThorArrivesWakandaES = BASE_URL+"/ThorEN.mp4"; +export const NarutoAndSasukeVsMomoshikiEN = BASE_URL+"/NarutoEN.mp4"; +export const NarutoAndSasukeVsMomoshikiES = BASE_URL+"/NarutoES.mp4"; diff --git a/src/files-ui/components/file-card/FileCard.scss b/src/files-ui/components/file-card/FileCard.scss new file mode 100644 index 0000000000000000000000000000000000000000..a9c9ebd51f4daace080349bd329e32b62f3440e0 --- /dev/null +++ b/src/files-ui/components/file-card/FileCard.scss @@ -0,0 +1,214 @@ +@import url(https://fonts.googleapis.com/css?family=Poppins:300,400,500,600,700,900); + +.files-ui-file-card-main-container { + border-radius: 8px; + //overflow: hidden; + color: rgba(0, 0, 0, 0.858); + display: flex; + flex-direction: row; + align-items: center; + //margin: 10px; + min-height: 100px; + box-sizing: border-box; + position: relative; + font-size: 15px; + font-weight: 400; + width: 320px; + .files-ui-file-card-main-layer-container { + border-radius: 8px; + overflow: hidden; + width: 320px; + box-sizing: border-box; + height: 100px; + box-sizing: border-box; + + .file-card-main-layer { + //padding: 5px 0; + box-sizing: border-box; + position: absolute; + left: 0; + right: 0; + width: 100%; + height: 100%; + display: flex; + box-sizing: border-box; + align-items: center; + justify-content: space-between; + .file-card-icon-plus-data { + display: flex; + align-items: center; + justify-content: flex-start; + gap: 5px; + .file-card-icon-container { + //margin: 0 10px; + width: 100px; + height: 100px; + overflow: hidden; + position: relative; + display: flex; + align-items: center; + justify-content: center; + box-sizing: border-box; + // background-color: aqua; + .file-card-icon-layer { + box-sizing: border-box; + position: absolute; + width: 100px; + height: 100px; + display: flex; + align-items: center; + justify-content: center; + box-sizing: border-box; + //background-color: blue; + + overflow: hidden; + img { + //filter: blur(0); + //margin: auto; + background-repeat: no-repeat; + background-size: cover; + background-position: center; + } + &.blur { + img { + filter: blur(4px); + width: 200%; + height: 200%; + } + } + } + .file-card-status-layer { + display: flex; + align-items: flex-end; + justify-content: flex-start; + padding: 5px; + box-sizing: border-box; + } + } + .file-card-data { + padding-right: 10px; + box-sizing: border-box; + line-height: 19px; + font-weight: 500; + //width: 250px; + width: calc(100% - 100px); + //flex-grow: 1; + word-break: break-all; + color: black; + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: center; + height: 100%; + &.dark-mode { + color: rgba(255, 255, 255, 0.7); + } + .file-card-size { + font-weight: 400; + font-size: 0.9rem; + } + .file-card-name { + //letter-spacing: 0.09em; + font-size: 1rem; + //text-align: center; + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 1; /* number of lines to show */ + line-clamp: 1; + -webkit-box-orient: vertical; + &.not-allowed { + background-color: rgba(180, 16, 16, 0.7); + } + } + } + } + } + + .file-card-upload-layer-container { + display: flex; + box-sizing: border-box; + //background-color: rgba(0, 0, 0, 0.5); + background: linear-gradient( + to right, + rgba(0, 0, 0, 0.2), + rgba(0, 0, 0, 0.35), + rgba(0, 0, 0, 0.625), + rgba(0, 0, 0, 0.9), + rgba(0, 0, 0, 0.9) + ); + color: rgba(255, 255, 255, 0.8); + font-weight: 500; + font-size: 1em; + position: relative; + overflow: hidden; + align-items: center; + justify-content: flex-end; + height: 100%; + } + .file-card-info-layer-container { + display: flex; + box-sizing: border-box; + //background-color: rgba(0, 0, 0, 0.5); + background: linear-gradient( + to right, + rgba(0, 0, 0, 0.2), + rgba(0, 0, 0, 0.3), + rgba(0, 0, 0, 0.4), + rgba(0, 0, 0, 0.75), + rgba(0, 0, 0, 0.9), + rgba(0, 0, 0, 0.9), + rgba(0, 0, 0, 0.9), + rgba(0, 0, 0, 0.9), + rgba(0, 0, 0, 0.9), + rgba(0, 0, 0, 0.9), + rgba(0, 0, 0, 0.9) + ); + color: rgba(255, 255, 255, 0.8); + font-weight: 500; + font-size: 1em; + overflow: hidden; + align-items: center; + justify-content: flex-end; + .file-card-file-info { + width: calc(100% - 100px); + height: 100px; + text-align: left; + scrollbar-width: thin; + overflow: auto; + scrollbar-color: #646c7fa9 transparent; + // position: relative; + + &::-webkit-scrollbar { + width: 9px; + } + &::-webkit-scrollbar-track { + background: transparent; + } + &::-webkit-scrollbar-thumb { + background-color: #646c7fa9; + border-radius: 20px; + border: transparent; + } + .files-ui-file-card-info-layer-header { + display: flex; + width: 100%; + flex-direction: row; + align-items: center; + justify-content: flex-end; + /* position: absolute; + top: 5; + right: 5; */ + } + .heading { + font-weight: 600; + padding: 0 5px; + } + .label { + padding: 0 5px; + font-weight: 399; + } + } + } + } +} diff --git a/src/files-ui/components/file-card/FileCard.tsx b/src/files-ui/components/file-card/FileCard.tsx new file mode 100644 index 0000000000000000000000000000000000000000..065c9abbc9cfa85901b5c6f8adde2a7e84904d90 --- /dev/null +++ b/src/files-ui/components/file-card/FileCard.tsx @@ -0,0 +1,376 @@ +import * as React from "react"; +import { FileCardProps } from "./FileCardProps"; +import "./FileCard.scss"; +import "./components/FileCardPaper.scss"; +import { getLocalFileItemData } from "../file-item/utils/getLocalFileItemData"; +import { fileSizeFormater, shrinkWord } from "../../core"; +import useProgress from "../file-mosaic/hooks/useProgress"; +import useFileMosaicInitializer from "../file-mosaic/hooks/useFileMosaicInitializer"; +import { useIsUploading } from "../file-mosaic/hooks/useIsUploading"; +import LayerContainer from "../file-mosaic/components/file-mosaic-layer/LayerContainer"; +import Layer from "../file-mosaic/components/file-mosaic-layer/Layer"; +import FileMosaicImageLayer from "../file-mosaic/components/FIleMosaicImageLayer/FileMosaicImageLayer"; +import FileCardRightActions from "./components/FileCardRightActions"; +import FileCardInfoLayer from "./components/FileCardInfoLayer"; +import FileMosaicStatus from "../file-mosaic/components/FileMosaicStatus/FileMosaicStatus"; +import FileCardUploadLayer from "./components/FileCardUploadLayer"; +import { Tooltip } from "../tooltip"; + +const setFinalElevation = (elevation: string | number): number => { + // let finalElevation: number = ""; + let finalElevation = Number(elevation); + + if (!isNaN(finalElevation)) { + if (finalElevation > 24) { + return 24; + } else if (finalElevation < 0) { + return 0; + } else { + return finalElevation; + } + } else { + return 0; + } +}; +const makeFileCardClassName = ( + elevation: FileCardProps["elevation"], + darkMode: boolean | undefined, + className: string | undefined +): string => { + console.log("FileCard makeFileCardClassName", elevation, darkMode, className); + let finalClassName: string = + "files-ui-file-card-main-container files-ui-tooltip card"; + + if (elevation) { + finalClassName += " elevation-" + setFinalElevation(elevation); + } + if (darkMode) { + finalClassName += " dark-mode"; + } + if (className) { + finalClassName += ` ${className}`; + } + console.log("FileCard finalClassName", finalClassName); + + return finalClassName; +}; + +const FileCard: React.FC<FileCardProps> = (props: FileCardProps) => { + const { + style, + className, + + file, + name: propName, + size: propSize, + type: propType, + + id, + valid, + errors, + uploadStatus, + uploadMessage, + progress, + + xhr, + + localization, + preview, + imageUrl, + videoUrl, + info, + backgroundBlurImage = true, + darkMode, + + alwaysActive = true, + + resultOnTooltip = true, + + downloadUrl, + + onDelete, + onCancel, + onAbort, + + onDownload, + onSee, + onWatch, + + onDoubleClick, + onClick, + onRightClick, + + elevation = 4, + + //} = mergeProps(props, FileCardPropsDefault); + } = props; + + //ref for anchor element + const downloadRef = React.useRef<HTMLAnchorElement>(null); + + //className created + const finalClassName: string = makeFileCardClassName( + elevation, + darkMode, + className + ); + + // local properties from file + const [localName, localType, localSize]: [ + string, + string | undefined, + number | undefined + ] = getLocalFileItemData(file, propName, propType, propSize); + + // handle progress + const localProgress: number | undefined = useProgress(progress, xhr); + + //Initialize File Item + const [isReady, isImage, isVideo, url, imageSource, videoSource]: [ + boolean, + boolean, + boolean, + string, + string | undefined, + File | string | undefined + ] = useFileMosaicInitializer( + file, + propName, + propType, + valid, + preview as boolean, + imageUrl, + videoUrl + ); + //The size formatted and rounded in 2 decimals + const sizeFormatted: string = localSize + ? fileSizeFormater(localSize) + : "0 KB"; + + //alwaysActive + const [showInfo, setShowInfo] = React.useState<boolean>(false); + + /********* ALWAYS ACTIVE LOGIC ***************/ + //state for actionOnHover + const [hovering, setHovering] = React.useState<boolean>(false); + const handleOnHoverEnter: React.MouseEventHandler<HTMLDivElement> = () => { + if (alwaysActive) return; + setHovering(true); + }; + const handleOnHoverLeave: React.MouseEventHandler<HTMLDivElement> = () => { + if (alwaysActive) return; + setHovering(false); + }; + + /***************** HANDLERS **********/ + //delete file item + const handleDelete = (): void => onDelete?.(id); + + //open info layer + const handleOpenInfo = (): void => setShowInfo(true); + + //close info layer + const handleCloseInfo = (): void => setShowInfo(false); + + const isUploading: boolean = useIsUploading(uploadStatus); + + React.useEffect(() => { + //console.log("Change isUploading", isUploading); + if (isUploading && showInfo) { + handleCloseInfo(); + } + // eslint-disable-next-line + }, [isUploading]); + + /*************** Click ***************/ + /*************** CLICK ***************/ + /** + * TO-DO: Add functionallity on click event + * @param e event object + */ + function handleClick<T extends HTMLDivElement>( + e: React.MouseEvent<T, MouseEvent> + ): void { + //avoid children to trigger onClick ripple from parent + e.stopPropagation(); + onClick?.(e); + } + const handleDoubleClick: React.MouseEventHandler<HTMLDivElement> = ( + evt: React.MouseEvent + ): void => { + alert("double click on file"); + evt.preventDefault(); + + onDoubleClick?.(evt); + }; + function handleRightClick(evt: React.MouseEvent) { + // alert("right click!!!!"); + //get coordinates + //zindex + //create menu component + // evt.preventDefault(); + onRightClick?.(evt); + } + + // DOWNLOAD FILE + /** + * onDownload, form 1 + * Trigger dowload directly performing a click on anchor element + */ + const innerDownload = () => { + const anchorElement = downloadRef.current; + if (anchorElement) { + anchorElement.click(); + } + }; + /** + * onDownlad, form 2 + * Handle the download triggering an outside event + */ + const handleDownload = () => { + if (onDownload) { + onDownload?.(id, downloadUrl); + } else if (typeof downloadUrl == "string") { + innerDownload(); + } + }; + + if (isReady) { + return ( + <div + className={finalClassName} + style={style} + onClick={handleClick} + onMouseEnter={handleOnHoverEnter} + onMouseLeave={handleOnHoverLeave} + onDoubleClick={handleDoubleClick} + onContextMenu={handleRightClick} + > + <LayerContainer + className="files-ui-file-card-main-layer-container" + style={style} + > + <Layer className="file-card-main-layer" visible={true}> + <div className="file-card-icon-plus-data"> + {/** ICON + STATUS */} + <div className="file-card-icon-container"> + <LayerContainer className="file-card-icon-layer"> + {/** IMAGE LAYER BLUR */} + <Layer + className="file-card-icon-layer blur" + visible={backgroundBlurImage} + > + <FileMosaicImageLayer + imageSource={imageSource} + url={url} + fileName={localName} + isBlur={true} + /> + </Layer> + {/** IMAGE LAYER NO BLUR */} + <Layer className="file-card-icon-layer" visible={true}> + <FileMosaicImageLayer + imageSource={imageSource} + url={url} + fileName={localName} + isBlur={false} + /> + </Layer> + <Layer className="file-card-status-layer" visible={true}> + <FileMosaicStatus + valid={valid} + uploadStatus={uploadStatus} + localization={localization} + /> + </Layer> + </LayerContainer> + </div> + {/** DATA */} + <div + className={ + darkMode ? "file-card-data dark-mode" : "file-card-data" + } + > + <div className={"file-card-name"}>{localName}</div> + <div className={"file-card-size"}>{sizeFormatted}</div> + <div className={"file-card-size"}>{shrinkWord(localType)}</div> + </div> + </div> + </Layer> + + {/* <Layer + className="files-ui-file-card-right-layer" + visible={!isUploading} + > */} + + {/* </Layer> */} + + <Layer className="file-card-info-layer-container" visible={showInfo}> + <FileCardInfoLayer + onCloseInfo={handleCloseInfo} + valid={valid} + localization={localization} + localName={localName} + sizeFormatted={sizeFormatted} + localType={localType} + /> + </Layer> + + {/** UPLOAD LAYER */} + <Layer + className="file-card-upload-layer-container" + visible={isUploading} + > + <div className="files-ui-file-card-upload-layer"> + <FileCardUploadLayer + uploadStatus={uploadStatus} + progress={localProgress} + onCancel={onCancel ? () => onCancel?.(id) : undefined} + onAbort={ + onAbort + ? () => { + xhr?.abort(); + onAbort?.(id); + } + : undefined + } + localization={localization} + /> + </div> + </Layer> + </LayerContainer> + + <FileCardRightActions + deleteIcon={onDelete !== undefined} + onDelete={handleDelete} + darkMode={darkMode} + imageIcon={isImage && onSee !== undefined} + onSee={() => onSee?.(imageSource)} + videoIcon={isVideo && onWatch !== undefined} + onWatch={() => onWatch?.(videoSource)} + downloadIcon={onDownload !== undefined || downloadUrl !== undefined} + onDownload={handleDownload} + infoIcon={info !== undefined} + onOpenInfo={handleOpenInfo} + isActive={alwaysActive || hovering} + visible={!isUploading && !showInfo} + /> + + <Tooltip + open={resultOnTooltip} + uploadStatus={uploadStatus} + valid={valid} + errors={errors} + uploadMessage={uploadMessage} + /> + {downloadUrl && ( + <a ref={downloadRef} href={downloadUrl} download={localName} hidden> + download_file + </a> + )} + </div> + ); + } + return <></>; +}; +export default FileCard; diff --git a/src/files-ui/components/file-card/FileCardProps.ts b/src/files-ui/components/file-card/FileCardProps.ts new file mode 100644 index 0000000000000000000000000000000000000000..ca3012323eb979e734e5eb3257918ebe700760c2 --- /dev/null +++ b/src/files-ui/components/file-card/FileCardProps.ts @@ -0,0 +1,39 @@ +import { FileMosaicPropsMap } from "../file-mosaic/components/file-mosaic/FileMosaicProps"; + + +export interface FileCardPropsMap extends FileMosaicPropsMap { + elevation?: false | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 + | "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "10" | "11" | "12" | "13" | "14" | "15" | "16" | "17" | "18" | "19" | "20" | "21" | "22" | "23" | "24"; +} + + +export type FileCardProps = { + [F in keyof FileCardPropsMap]: FileCardPropsMap[F] +} + +/** + * Base default props + */ +export const FileCardPropsDefault: FileCardProps = { + onDelete: undefined, + file: undefined, + color: "#071e25", + // validator: undefined, + id: undefined, + style: {}, + preview: false, + valid: undefined, + info: false, + //hd: undefined, + localization: "EN-en", + //onlyImage: false, + imageUrl: undefined, + errors: undefined, + elevation: 2, + alwaysActive: undefined, + progress: undefined, + resultOnTooltip: true, + backgroundBlurImage: true, + darkMode: false, + //fileName: "bottom" +} diff --git a/src/files-ui/components/file-card/components/FileCardInfoLayer.tsx b/src/files-ui/components/file-card/components/FileCardInfoLayer.tsx new file mode 100644 index 0000000000000000000000000000000000000000..823437c92e429479beb2ec5598ac138bcac3dd7e --- /dev/null +++ b/src/files-ui/components/file-card/components/FileCardInfoLayer.tsx @@ -0,0 +1,41 @@ +import * as React from "react"; +import { FileMosaicInfoLayerProps } from "../../file-mosaic/components/FileMosaicInfoLayer/FileMosaicInfoLayerProps"; +import FileMosaicStatus from "../../file-mosaic/components/FileMosaicStatus/FileMosaicStatus"; +import { Cancel } from "../../icons"; +type FileCardInfoLayerProps = FileMosaicInfoLayerProps; +const FileCardInfoLayer: React.FC<FileCardInfoLayerProps> = ( + props: FileCardInfoLayerProps +) => { + const { + valid, + localization, + onCloseInfo, + uploadStatus, + localName, + sizeFormatted, + localType, + } = props; + return ( + <div className="file-card-file-info"> + {/* <FileMosaicStatus + style={{ margin: 0, right: 5, bottom: 0, position:"absolute" }} + valid={valid} + uploadStatus={uploadStatus} + localization={localization} + /> */} + <Cancel + style={{ margin: 0, right: 5, top: 0, position:"absolute" }} + color="rgba(255,255,255,0.8)" + onClick={onCloseInfo} + colorFill="black" + /> + <div className="heading">Name:</div> + <div className="label">{localName}</div> + <div className="heading">Size:</div> + <div className="label">{sizeFormatted}</div> + <div className="heading">Type:</div> + <div className="label">{localType}</div> + </div> + ); +}; +export default FileCardInfoLayer; diff --git a/src/files-ui/components/file-item/components/FileCard/FileCardPaper.scss b/src/files-ui/components/file-card/components/FileCardPaper.scss similarity index 100% rename from src/files-ui/components/file-item/components/FileCard/FileCardPaper.scss rename to src/files-ui/components/file-card/components/FileCardPaper.scss diff --git a/src/files-ui/components/file-card/components/FileCardRightActions.scss b/src/files-ui/components/file-card/components/FileCardRightActions.scss new file mode 100644 index 0000000000000000000000000000000000000000..54531d6080b035cb536cd76d1db2f10a9193e440 --- /dev/null +++ b/src/files-ui/components/file-card/components/FileCardRightActions.scss @@ -0,0 +1,27 @@ +.file-card-right-layer-header { + margin-top: 3px; + margin-right: 3px; + position: absolute; + top: 0; + right: 0; + left: unset; + display: flex; + align-items: center; + justify-content: flex-end; + gap: 2px; +} +.file-card-right-layer-footer { + margin-bottom: 3px; + margin-right: 3px; + left: unset; + position: absolute; + + display: flex; + align-items: center; + justify-content: flex-end; + gap: 2px; + //height: 20px; + //width: 100%; + bottom: 0; + right: 0; +} diff --git a/src/files-ui/components/file-card/components/FileCardRightActions.tsx b/src/files-ui/components/file-card/components/FileCardRightActions.tsx new file mode 100644 index 0000000000000000000000000000000000000000..2cb660fb57fe4bd2aed77502de10d51b0e4da399 --- /dev/null +++ b/src/files-ui/components/file-card/components/FileCardRightActions.tsx @@ -0,0 +1,126 @@ +import * as React from "react"; +import { Localization, UPLOADSTATUS } from "../../../core"; +import { + Clear, + DownloadFile, + InfoDisney, + PlayIcon, + Visibility, +} from "../../icons"; +import "./FileCardRightActions.scss"; + +declare type FileCardRightActionsProps = { + darkMode?: boolean; + deleteIcon?: boolean; + onDelete?: Function; + + imageIcon: boolean; + onSee: ((imageSource: string | undefined) => void) | undefined; + + videoIcon: boolean; + onWatch: ((videoSource: File | undefined) => void) | undefined; + + downloadIcon: boolean; + onDownload: Function | undefined; + + infoIcon: boolean; + onOpenInfo: Function | undefined; + + isActive?: boolean; + visible?: boolean; +}; +const FileCardRightActions: React.FC<FileCardRightActionsProps> = ( + props: FileCardRightActionsProps +) => { + const { + darkMode, + deleteIcon, + downloadIcon, + imageIcon, + infoIcon, + onDelete, + onDownload, + onOpenInfo, + onSee, + onWatch, + + videoIcon, + isActive, + visible, + } = props; + if (visible) + return ( + <> + <div className="file-card-right-layer-header"> + {isActive && deleteIcon && ( + <Clear + className={ + darkMode ? "files-ui-file-icon dark-mode" : "files-ui-file-icon" + } + color={darkMode ? "#121212" : "rgba(255,255,255,0.851)"} + onClick={onDelete} + size="small" + colorFill="transparent" + /> + )} + </div> + <div className="file-card-right-layer-footer"> + {isActive && ( + <React.Fragment> + {imageIcon && ( + <Visibility + className={ + darkMode + ? "files-ui-file-icon dark-mode" + : "files-ui-file-icon" + } + color={darkMode ? "#121212" : "rgba(255,255,255,0.851)"} + onClick={onSee} + size="small" + /> + )} + + {videoIcon && ( + <PlayIcon + className={ + darkMode + ? "files-ui-file-icon dark-mode" + : "files-ui-file-icon" + } + color={darkMode ? "#121212" : "rgba(255,255,255,0.851)"} + onClick={onWatch} + size="small" + /> + )} + {downloadIcon && ( + <DownloadFile + className={ + darkMode + ? "files-ui-file-icon dark-mode" + : "files-ui-file-icon" + } + color={darkMode ? "#121212" : "rgba(255,255,255,0.851)"} + onClick={onDownload} + size="small" + /> + )} + {infoIcon && ( + <InfoDisney + className={ + darkMode + ? "files-ui-file-icon dark-mode" + : "files-ui-file-icon" + } + onClick={onOpenInfo} + color={darkMode ? "#121212" : "rgba(255,255,255,0.851)"} + size="micro" + /> + )} + </React.Fragment> + )} + </div> + </> + ); + return <></>; +}; +export default FileCardRightActions; diff --git a/src/files-ui/components/file-card/components/FileCardUploadLayer.scss b/src/files-ui/components/file-card/components/FileCardUploadLayer.scss new file mode 100644 index 0000000000000000000000000000000000000000..c84a6ef13847fd3462bcaba82ddfe0875996c59e --- /dev/null +++ b/src/files-ui/components/file-card/components/FileCardUploadLayer.scss @@ -0,0 +1,44 @@ +.files-ui-file-card-upload-layer { + width: 100px; + height: 100%; + //background-color: rgba(0, 0, 0, 0.5); + color: rgba(255, 255, 255, 0.8); + font-weight: 500; + font-size: 1em; + position: relative; + overflow: hidden; + + .elevation-list-card { + transition: all 1.5s ease; + position: absolute; + top: 0; + left: 0; + width: 100%; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + .elevation-item-card { + width: 100%; + height: 100px; + + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + padding: 0 10px; + box-sizing: border-box; + + span { + text-align: center; + word-break: break-word; + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 2; /* number of lines to show */ + line-clamp: 2; + -webkit-box-orient: vertical; + } + } + } +} diff --git a/src/files-ui/components/file-card/components/FileCardUploadLayer.tsx b/src/files-ui/components/file-card/components/FileCardUploadLayer.tsx new file mode 100644 index 0000000000000000000000000000000000000000..57f0c68a5423573f862d2b47de36838c056ee3bb --- /dev/null +++ b/src/files-ui/components/file-card/components/FileCardUploadLayer.tsx @@ -0,0 +1,119 @@ +import * as React from "react"; +import { Localization, UPLOADSTATUS } from "../../../core"; +import { + AbortedStatus, + EmptyStatus, + ErrorStatus, + PreparingStatus, + SuccessStatus, + UploadingStatus, +} from "../../file-status"; +import "./FileCardUploadLayer.scss"; +export interface FileCardUploadLayerPropsMap { + visible?: boolean; + uploadStatus?: UPLOADSTATUS; + onCancel?: Function; + onAbort?: Function; + progress?: number; + localization?: Localization; +} + +export type FileCardUploadLayerProps = { + [T in keyof FileCardUploadLayerPropsMap]: FileCardUploadLayerPropsMap[T]; +}; + +const FileCardUploadLayer: React.FC<FileCardUploadLayerProps> = ( + props: FileCardUploadLayerProps +) => { + const { uploadStatus, onCancel, onAbort, progress, localization } = props; + const elevationContainerRef = React.useRef<HTMLDivElement | null>(null); + const listContainerStoryRef = React.useRef<HTMLDivElement | null>(null); + + const [statusHistory, setStatusHistory] = React.useState< + Array<UPLOADSTATUS | undefined> + >([undefined]); + + React.useEffect(() => { + setStatusHistory((statusHistory: Array<UPLOADSTATUS | undefined>) => { + if ( + statusHistory[statusHistory.length - 1] === "preparing" && + uploadStatus === "uploading" + ) { + const tempStatusHistory = [...statusHistory]; + tempStatusHistory[statusHistory.length - 1] = uploadStatus; + //replace + return [...tempStatusHistory]; + } + return [...statusHistory, uploadStatus]; + }); + }, [uploadStatus]); + + const elevate = () => { + const currentElevationContainer = elevationContainerRef.current; + const currentElevationList = listContainerStoryRef.current; + if (currentElevationContainer === null || currentElevationList === null) + return; + + currentElevationList.style.top = + 0 - (statusHistory.length - 1) * 100 + "px"; + }; + React.useEffect(() => { + if (statusHistory.length > 1) elevate(); + // eslint-disable-next-line + }, [statusHistory.length]); + + return ( + <div className={"elevation-layer-container"} ref={elevationContainerRef}> + <div className="elevation-list-card" ref={listContainerStoryRef}> + {statusHistory.map((status, index) => { + switch (status) { + case "preparing": + return ( + <div className="elevation-item-card" key={index + 1}> + <PreparingStatus + onCancel={onCancel} + localization={localization} + /> + </div> + ); + case "uploading": + return ( + <div className="elevation-item-card" key={index + 1}> + <UploadingStatus + onAbort={onAbort} + progress={progress} + localization={localization} + /> + </div> + ); + case "error": + return ( + <div className="elevation-item-card" key={index + 1}> + <ErrorStatus size={60} localization={localization} /> + </div> + ); + case "success": + return ( + <div className="elevation-item-card" key={index + 1}> + <SuccessStatus localization={localization} /> + </div> + ); + case "aborted": + return ( + <div className="elevation-item-card" key={index + 1}> + <AbortedStatus localization={localization} /> + </div> + ); + default: + return ( + <div className="elevation-item-card" key={index + 1}> + <EmptyStatus /> + </div> + ); + } + })} + </div> + </div> + ); +}; +export default FileCardUploadLayer; diff --git a/src/files-ui/components/file-item/components/FileCard/FileCard.scss b/src/files-ui/components/file-item/components/FileCard/FileCard.scss deleted file mode 100644 index 37faae4fd4c79760fa5903ab1accc098e41c085b..0000000000000000000000000000000000000000 --- a/src/files-ui/components/file-item/components/FileCard/FileCard.scss +++ /dev/null @@ -1,51 +0,0 @@ -@import url(https://fonts.googleapis.com/css?family=Poppins:300,400,500,600,700,900); -.files-ui-file-card-main-container { - border-radius: 8px; - color: rgba(0, 0, 0, 0.858); - display: flex; - flex-direction: row; - align-items: center; - margin: 10px; - min-height: 100px; - box-sizing: border-box; - padding: 10px; - position: relative; - font-size: 15px; - font-weight: 400; - min-width: 280px; - - .file-card-data { - line-height: 18px; - font-weight: 500; - - word-break: break-all; - color: black; - &.dark-mode { - color: rgba(255, 255, 255, 0.7); - } - .file-card-size { - font-weight: 400; - font-size: 0.95rem; - } - .file-card-name { - //margin-top: 3px; - letter-spacing: 0.125em; - font-size: 1rem; - //white-space: nowrap; - //padding: 0.5px; - text-align: center; - overflow: hidden; - text-overflow: ellipsis; - display: -webkit-box; - -webkit-line-clamp: 1; /* number of lines to show */ - line-clamp: 1; - -webkit-box-orient: vertical; - &.not-allowed { - background-color: rgba(180, 16, 16, 0.7); - } - } - } - .files-ui-file-card-right { - min-width: 60px; - } -} diff --git a/src/files-ui/components/file-item/components/FileCard/FileCard.tsx b/src/files-ui/components/file-item/components/FileCard/FileCard.tsx deleted file mode 100644 index 8765dea26dea3181b2ea6f44758058f10f7be2a8..0000000000000000000000000000000000000000 --- a/src/files-ui/components/file-item/components/FileCard/FileCard.tsx +++ /dev/null @@ -1,285 +0,0 @@ -import * as React from "react"; -import { FileCardProps, FileCardPropsDefault } from "./FileCardProps"; -import "./FileCard.scss"; -import "./FileCardPaper.scss"; -import FileItemImage from "../FileItemImage/FileItemImage"; -import useFileItemInitializer from "../../hooks/useFileItemInitializer"; -import { getLocalFileItemData } from "../../utils/getLocalFileItemData"; -import { Clear } from "../../../icons"; -import { fileSizeFormater, shrinkWord } from "../../../../core"; -import { mergeProps } from "../../../overridable"; -import { showFileItemComponent } from "../../utils/showFileItemComponent"; -import useFileItemProgress from "../../hooks/useFileItemProgress"; -import MainLayerBodyNeo from "../FileItemMainLayer/MainLayerBody/MainLayerBodyNeo"; - -const setFinalElevation = (elevation: string | number): number => { - // let finalElevation: number = ""; - let finalElevation = Number(elevation); - - if (!isNaN(finalElevation)) { - if (finalElevation > 24) { - return 24; - } else if (finalElevation < 0) { - return 0; - } else { - return finalElevation; - } - } else { - return 0; - } -}; -const makeFileCardClassName = ( - elevation: FileCardProps["elevation"], - darkMode: boolean, - className: string | undefined -): string => { - console.log("FileCard makeFileCardClassName", elevation, darkMode, className); - let finalClassName: string = "files-ui-file-card-main-container"; - if (elevation) { - finalClassName += " elevation-" + setFinalElevation(elevation); - } - if (darkMode) { - finalClassName += " dark-mode"; - } - if (className) { - finalClassName += ` ${className}`; - } - console.log("FileCard finalClassName", finalClassName); - - return finalClassName; -}; - -const FileCard: React.FC<FileCardProps> = (props: FileCardProps) => { - const { - file, - name: propName, - size: propSize, - type: propType, - onDelete, - onSee, - onWatch, - style, - preview, - //onlyImage, - info, - id, - valid, - uploadStatus, - uploadMessage, - hd, - localization, - errors, - imageUrl, - elevation, - alwaysActive, - resultOnTooltip, - downloadUrl, - onDownload, - progress, - onAbort, - xhr, - onCancel, - showProgress, - className, - onDoubleClick, - onRightClick, - backgroundBlurImage, - darkMode, - } = mergeProps(props, FileCardPropsDefault); - - //ref for anchor element - const downloadAnchorRef = React.useRef<HTMLAnchorElement>(null); - - //className created - const finalClassName: string = makeFileCardClassName( - elevation, - darkMode, - className - ); - - const showFileItem: boolean = showFileItemComponent( - file, - propName - // Boolean(rootClassNameCreated) - ); - - // local properties from file - const [localName, localType, localSize]: [ - string, - string | undefined, - number | undefined - ] = getLocalFileItemData(file, propName, propType, propSize); - - // handle progress - const localProgress = useFileItemProgress(progress, showProgress, xhr); - - //Initialize File Item - const [isImage, isVideo, url, imageSource]: [ - boolean, - boolean, - string, - string | undefined - ] = useFileItemInitializer( - file, - propName, - propType, - valid, - preview as boolean, - imageUrl - ); - - //The size formatted and rounded in 2 decimals - const sizeFormatted: string = localSize - ? fileSizeFormater(localSize) - : "0 KB"; - - /*************** Click ***************/ - /** - * TO-DO: Add functionallity on click event - * @param e event object - */ - function handleClick<T extends HTMLDivElement>( - e: React.MouseEvent<T, MouseEvent> - ): void { - //avoid children to trigger onClick ripple from parent - e.stopPropagation(); - } - - /***************** HANDLERS **********/ - //delete file item - const handleDelete = (): void => { - onDelete?.(id); - }; - //open info layer - /* const handleOpenInfo = () => { - setShowInfo(true); - }; */ - //close info layer - /* const handleCloseInfo = () => { - setShowInfo(false); - }; */ - //handle watch video - const handleOpenVideo = async () => { - if (file) onWatch?.(file); - }; - //open image - const handleOpenImage = async () => { - if (imageSource) { - // if (hd) { - // const response = await readImagePromise(file); - // onSee?.(response || ""); - // } else { - onSee?.(imageSource); - //} - } - }; - - /********** DOWNLOAD HANDLERS **********/ - /** - * onDownload, form 1 - * Trigger dowload directly performing a click on anchor element - */ - const innerDownload = () => { - const anchorElement = downloadAnchorRef.current; - if (anchorElement) { - anchorElement.click(); - } - }; - /** - * onDownlad, form 2 - * Handle the download triggering an outside event - */ - const handleDownload = () => { - if (onDownload) { - onDownload?.(id, downloadUrl); - } else if (typeof downloadUrl == "string") { - innerDownload(); - } - }; - /** - * Perform abort event when xhr is given - */ - const handleAbort = (): void => { - //trigger abort event - xhr?.abort(); - // handle externally the abort event - onAbort?.(id); - }; - /** - * Handle onCancel event - */ - const handleCancel = (): void => { - // handle externally the cancel event - onCancel?.(id); - }; - - const handleDoubleClick: React.MouseEventHandler<HTMLDivElement> = ( - evt: React.MouseEvent - ): void => { - alert("double click on file"); - evt.preventDefault(); - - onDoubleClick?.(evt); - }; - function handleRightClick(evt: React.MouseEvent) { - // alert("right click!!!!"); - //get coordinates - //zindex - //create menu component - // evt.preventDefault(); - // onRightClick?.(evt); - } - //console.log("FileItem onCancel", onCancel); - - if (showFileItem) { - return ( - <div - className={finalClassName} - style={style} - onClick={handleClick} - onDoubleClick={handleDoubleClick} - onContextMenu={handleRightClick} - > - <FileItemImage - imageSource={imageSource} - url={url} - fileName={localName} - backgroundBlurImage={backgroundBlurImage as boolean} - card={true} - /> - - <div - className={darkMode ? "file-card-data dark-mode" : "file-card-data"} - > - <div className={"file-card-name"}>{shrinkWord(localName, true)}</div> - - <div className={"file-card-size"}>{sizeFormatted}</div> - <div className={"file-card-size"}>{shrinkWord(localType)}</div> - </div> - - <div className="files-ui-file-card-right"> - <Clear - style={{ position: "absolute", right: 0, top: 0 }} - className="dui-file-item-icon" - color="rgba(255,255,255,0.851)" - onClick={handleDelete} - size="small" - colorFill="transparent" - /> - <MainLayerBodyNeo - hide={false} - uploadStatus={uploadStatus} - localization={localization} - progress={progress} - onAbort={onAbort} - valid={valid} - hovering={true} - onCancel={onCancel} - /> - </div> - </div> - ); - } - return <></>; -}; -export default FileCard; diff --git a/src/files-ui/components/file-item/components/FileCard/FileCardProps.ts b/src/files-ui/components/file-item/components/FileCard/FileCardProps.ts deleted file mode 100644 index 2dbc3541718029f063e179ea2596a91c58bcca50..0000000000000000000000000000000000000000 --- a/src/files-ui/components/file-item/components/FileCard/FileCardProps.ts +++ /dev/null @@ -1,198 +0,0 @@ -import { Localization, UPLOADSTATUS } from "../../../../core"; -import { OverridableComponentProps } from "../../../overridable"; - -export interface FileCardPropsMap extends OverridableComponentProps { - /** - * The file object obtained from client drop or selection - */ - file?: File; - /** - * The name of the file - */ - name?: string; - /** - * The file mime type - */ - type?: string; - /** - * the size of the file in bytes - */ - size?: number; - /** - * A function of what to do when close button is pressed or clicked - */ - onDelete?: (fileId: number | string | undefined) => void; - /** - * A function that return a file object when "see" button is pressed or clicked - */ - onSee?: (imageUrl: string) => void; - /** - * A function that return a file object when "play" button is presssed or clicked - */ - onWatch?: (videoUrl: File) => void; - /** - * Whether to see as grid or inline (horizontal list) - */ - errors?: string[]; - /** - * individual validator for each file - */ - //validator?: FileItemValidator; - /** - * identifier for the file - */ - id?: string | number; - /** - * if true, and if the file is an image, - * makes visible the "view" button that will get the image url - * Also, it will be visible only when file is valid - */ - preview?: boolean; - /** - * whether to show a valid or rejected message ("ok", "rejected") - * by def. valid is false (if not present, it's false too) - * This value wil affect preview behaviour, - * If not valid, the preview will not be shown, nor the view button - */ - valid?: boolean | null; - /** - * This feature is hidden, it is not present on the documentation - * because it's experimental. If you found this prop, you can test it - * and comment us if any issue is found. Thanks in advance. - * - * Make file name, info layer, size and "valid message" - * not visible - */ - //onlyImage?: boolean; - /** - * whether to show the info layer or not - * also whether to make visible the info button or not , - * Only works when given a image file - */ - info?: boolean; - /** - * A string representation or web url of the image - * that will be set to the "src" prop of an <img/> tag - * <img src={`${url}`} /> - */ - imageUrl?: string; - - - elevation: false | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 - | "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "10" | "11" | "12" | "13" | "14" | "15" | "16" | "17" | "18" | "19" | "20" | "21" | "22" | "23" | "24"; - - /** - * The message from server - */ - uploadMessage?: string; - /** - * where to place the file name - * [in construction] - */ - //fileName?: "bottom" | "inside"; - /** - * The current upload status of the file - */ - uploadStatus?: UPLOADSTATUS; - /** - * If present, preview on full screen will - * be presented in the real image resolution - */ - hd?: boolean | undefined; - /** - * language to be used - * for now - * only English and Spanish is supported - */ - localization?: Localization; - /** - * Flag that determines whether actions are visible always, or only on hover event - */ - alwaysActive?: boolean; - /** - * Where to display result of validation: on InfoLayer or in Tooltip when user hovers the FileItem - */ - resultOnTooltip?: boolean; - /** - * Url to perform a GET request in order to download the file. - * This action is triggered when download button is clicked or pressed. - * In case onDownload prop is given - */ - downloadUrl?: string; - /** - * Event that is triggered when download button is clicked or pressed - */ - onDownload?: (fileId: number | string | undefined, downloadUrl?: string) => void; - /** - * the current percentage upload progress - * - */ - progress?: number; - /** - * Whether to show progress or not. - * @default true if xhr is initialized - * @default false if xhr is not given - */ - showProgress?: boolean; - /** - * abort event - */ - onAbort?: Function; - /** - * cancel when preparing event - */ - onCancel?: Function; - /** - * A reference to the XHR object that allows the upload and abort event. - * and progress - */ - xhr?: XMLHttpRequest; - /** - * Event that is triggered when user duble clicks the component - */ - onDoubleClick?: (evt: React.MouseEvent) => void; - /** - * Event that is triggered when user duble clicks the component - */ - onRightClick?: (evt: React.MouseEvent) => void; - /** - * Flag that indicates whether to show a background blur image or not - */ - backgroundBlurImage?: boolean; - /** - * Flag that indicates whether to activates dark mode for component or not - */ - darkMode: boolean; -} - - -export type FileCardProps = { - [F in keyof FileCardPropsMap]: FileCardPropsMap[F] -} - -/** - * Base default props - */ -export const FileCardPropsDefault: FileCardProps = { - onDelete: undefined, - file: undefined, - color: "#071e25", - // validator: undefined, - id: undefined, - style: {}, - preview: false, - valid: undefined, - info: false, - hd: undefined, - localization: "EN-en", - //onlyImage: false, - imageUrl: undefined, - errors: undefined, - elevation: 2, - alwaysActive: undefined, - progress: undefined, - resultOnTooltip: true, - backgroundBlurImage: true, - darkMode: false, - //fileName: "bottom" -} diff --git a/src/files-ui/components/file-mosaic/components/FileMosaicInfoLayer/FileMosaicInfoLayer.tsx b/src/files-ui/components/file-mosaic/components/FileMosaicInfoLayer/FileMosaicInfoLayer.tsx index 3e5156647fb6c43287acf9eb17ed28c90bb0f5c3..21a2e6a43af382f3c94f0312e33e16247c379e6e 100644 --- a/src/files-ui/components/file-mosaic/components/FileMosaicInfoLayer/FileMosaicInfoLayer.tsx +++ b/src/files-ui/components/file-mosaic/components/FileMosaicInfoLayer/FileMosaicInfoLayer.tsx @@ -19,7 +19,7 @@ const FileMosaicInfoLayer: React.FC<FileMosaicInfoLayerProps> = ( <React.Fragment> <div className="files-ui-file-mosaic-info-layer-header"> <Cancel - style={{ margin: 0, right: 0, top: 0 }} + //style={{ margin: 0, right: 0, top: 0 }} color="rgba(255,255,255,0.8)" onClick={onCloseInfo} colorFill="black" diff --git a/src/files-ui/components/file-mosaic/components/FileMosaicInfoLayer/FileMosaicInfoLayerProps.ts b/src/files-ui/components/file-mosaic/components/FileMosaicInfoLayer/FileMosaicInfoLayerProps.ts index 7bb535a6f84145dcf297a2433d7bd139fd3a0fb2..658746700c32cc75b94029a7a0ec8cf704778f99 100644 --- a/src/files-ui/components/file-mosaic/components/FileMosaicInfoLayer/FileMosaicInfoLayerProps.ts +++ b/src/files-ui/components/file-mosaic/components/FileMosaicInfoLayer/FileMosaicInfoLayerProps.ts @@ -1,6 +1,6 @@ import { Localization, UPLOADSTATUS } from "../../../../core"; -export interface FileMosaicInfoLayerProps{ +export type FileMosaicInfoLayerProps = { valid: boolean | null | undefined; uploadStatus?: UPLOADSTATUS; localization?: Localization; diff --git a/src/files-ui/components/file-mosaic/components/FileMosaicStatus/FileMosaicStatus.tsx b/src/files-ui/components/file-mosaic/components/FileMosaicStatus/FileMosaicStatus.tsx index 8501bfb4d9d11cf462076b0a79b320ae4301eb4e..66dd014627d2163f8ce4678a104e01ef2cf2fa4d 100644 --- a/src/files-ui/components/file-mosaic/components/FileMosaicStatus/FileMosaicStatus.tsx +++ b/src/files-ui/components/file-mosaic/components/FileMosaicStatus/FileMosaicStatus.tsx @@ -8,7 +8,7 @@ import { FileItemLocalizerSelector, LocalLabels } from "../../../../core"; const FileMosaicStatus: React.FC<FileMosaicStatusProps> = ( props: FileMosaicStatusProps ) => { - const { valid, uploadStatus, localization } = props; + const { valid, uploadStatus, localization, style } = props; const FileItemStatusLocalizer: LocalLabels = FileItemLocalizerSelector( localization @@ -16,7 +16,7 @@ const FileMosaicStatus: React.FC<FileMosaicStatusProps> = ( if (uploadStatus === "success") { return ( - <div className="files-ui-file-item-status-container file-status-ok"> + <div className="files-ui-file-item-status-container file-status-ok" style={style}> <CloudDone color="#4caf50" size="small" className="status-icon" /> {FileItemStatusLocalizer.success as string} </div> @@ -24,7 +24,7 @@ const FileMosaicStatus: React.FC<FileMosaicStatusProps> = ( } if (uploadStatus === "error" || uploadStatus === "aborted") { return ( - <div className="files-ui-file-item-status-container file-status-error"> + <div className="files-ui-file-item-status-container file-status-error" style={style}> <UploadError color="#f44336" size="semi-medium" @@ -37,14 +37,14 @@ const FileMosaicStatus: React.FC<FileMosaicStatusProps> = ( if (valid !== undefined && valid !== null) { if (valid) { return ( - <div className="files-ui-file-item-status-container file-status-ok"> + <div className="files-ui-file-item-status-container file-status-ok" style={style}> <CheckCircle color="#4caf50" size="small" className="status-icon" /> {FileItemStatusLocalizer.valid as string} </div> ); } else { return ( - <div className="files-ui-file-item-status-container file-status-error"> + <div className="files-ui-file-item-status-container file-status-error" style={style}> <DoDisturb color="#f44336" size="small" className="status-icon" /> {FileItemStatusLocalizer.denied as string} </div> diff --git a/src/files-ui/components/file-mosaic/components/FileMosaicStatus/FileMosaicStatusProps.ts b/src/files-ui/components/file-mosaic/components/FileMosaicStatus/FileMosaicStatusProps.ts index 577c58a09659bca32ad33e996eebe2faaa7811ec..e449be638a27d7a09ddf7628d8a8d2c9c23e7743 100644 --- a/src/files-ui/components/file-mosaic/components/FileMosaicStatus/FileMosaicStatusProps.ts +++ b/src/files-ui/components/file-mosaic/components/FileMosaicStatus/FileMosaicStatusProps.ts @@ -22,4 +22,7 @@ export interface FileMosaicStatusProps { */ localization?: Localization; + + style?:React.CSSProperties; + } \ No newline at end of file diff --git a/src/files-ui/components/file-mosaic/components/FileMosaicUploadLayer/FileMosaicUploadLayer.scss b/src/files-ui/components/file-mosaic/components/FileMosaicUploadLayer/FileMosaicUploadLayer.scss index 4ef8d675cb7da04781c8570f36b4b220ae07b15b..1c4795bd66041c8392b1730e2b6271761eb184e3 100644 --- a/src/files-ui/components/file-mosaic/components/FileMosaicUploadLayer/FileMosaicUploadLayer.scss +++ b/src/files-ui/components/file-mosaic/components/FileMosaicUploadLayer/FileMosaicUploadLayer.scss @@ -1,7 +1,7 @@ .files-ui-file-mosaic-upload-layer { width: 100%; height: 100%; - background-color: rgba(0, 0, 0, 0.5); + background-color: rgba(0, 0, 0, 0.65); color: rgba(255, 255, 255, 0.8); font-weight: 500; font-size: 1em; diff --git a/src/files-ui/components/file-mosaic/components/file-mosaic/FileMosaic.scss b/src/files-ui/components/file-mosaic/components/file-mosaic/FileMosaic.scss index 78bf294dc99cda25b94a6523d27c7010dc2517ed..23e29424be4489b2ddfc784c7907393c639a47b2 100644 --- a/src/files-ui/components/file-mosaic/components/file-mosaic/FileMosaic.scss +++ b/src/files-ui/components/file-mosaic/components/file-mosaic/FileMosaic.scss @@ -163,73 +163,7 @@ } } -/* &.files-ui-tooltip { - cursor: default; - position: relative; - display: inline-block; - box-sizing: border-box; - // border-bottom: 1px dotted black; - &:hover { - z-index: 2; - - .files-ui-tooltip-tooltiptext { - visibility: visible; - opacity: 1; - z-index: 2; - } - } - - .files-ui-tooltip-tooltiptext { - box-sizing: border-box; - font-family: "Poppins", sans-serif; - font-size: 0.8rem; - visibility: hidden; - width: 132px; - // background-color: green; - color: #fff; - text-align: center; - border-radius: 6px; - padding: 2px 2px; - position: absolute; - //z-index: 2; - //top: 190px; - top: 180px; - left: 66px; - margin-left: -60px; - - // Fade in tooltip - takes 1 second to go from 0% to 100% opac: - opacity: 0; - transition: opacity 1s; - &.not-valid-error { - background: linear-gradient(to top, #c62828, #d32f2f); - - &::after { - border-color: transparent transparent #d32f2f transparent; - } - } - - &.success { - //background-color: green; - background: linear-gradient(to top, #1b5e20, #2e7d32); - - &::after { - border-color: transparent transparent #2e7d32 transparent; - } - } - - &::after { - content: ""; - position: absolute; - bottom: 100%; - left: 50%; - margin-left: -5px; - border-width: 5px; - border-style: solid; - //border-color: transparent transparent green transparent; - } - } - } */ } //// ICONS diff --git a/src/files-ui/components/file-mosaic/components/file-mosaic/FileMosaic.tsx b/src/files-ui/components/file-mosaic/components/file-mosaic/FileMosaic.tsx index 0ce316027b59cd38e516a3185340df3c0ffd7b0c..e5806f2dc41e6cb2f0f1e10cc51af6aa8622976d 100644 --- a/src/files-ui/components/file-mosaic/components/file-mosaic/FileMosaic.tsx +++ b/src/files-ui/components/file-mosaic/components/file-mosaic/FileMosaic.tsx @@ -37,6 +37,7 @@ const FileMosaic: React.FC<FileMosaicProps> = (props: FileMosaicProps) => { localization, preview, imageUrl, + videoUrl, info, backgroundBlurImage = true, darkMode, @@ -60,7 +61,7 @@ const FileMosaic: React.FC<FileMosaicProps> = (props: FileMosaicProps) => { onRightClick, } = props; - console.log("FileMosaic progress " + id, progress); + // console.log("FileMosaic progress " + id, progress); //ref for anchor download element const downloadRef = React.useRef<HTMLAnchorElement>(null); @@ -87,22 +88,24 @@ const FileMosaic: React.FC<FileMosaicProps> = (props: FileMosaicProps) => { ); */ const localProgress: number | undefined = useProgress(progress, xhr); - console.log("FileMosaic progress localProgress " + localProgress); + //console.log("FileMosaic progress localProgress " + localProgress); //Initialize File Item - const [isReady, isImage, isVideo, url, imageSource]: [ + const [isReady, isImage, isVideo, url, imageSource, videoSource]: [ boolean, boolean, boolean, string, - string | undefined + string | undefined, + File | string | undefined ] = useFileMosaicInitializer( file, propName, propType, valid, preview as boolean, - imageUrl + imageUrl, + videoUrl ); //The size formatted and rounded in 2 decimals @@ -250,7 +253,7 @@ const FileMosaic: React.FC<FileMosaicProps> = (props: FileMosaicProps) => { imageIcon={isImage && onSee !== undefined} onSee={() => onSee?.(imageSource)} videoIcon={isVideo && onWatch !== undefined} - onWatch={() => onWatch?.(file)} + onWatch={() => onWatch?.(videoSource)} downloadIcon={ onDownload !== undefined || downloadUrl !== undefined } @@ -307,7 +310,7 @@ const FileMosaic: React.FC<FileMosaicProps> = (props: FileMosaicProps) => { uploadMessage={uploadMessage} /> {downloadUrl && ( - <a ref={downloadRef} href={downloadUrl} download={localName} hidden> + <a ref={downloadRef} target={"_blank"} href={downloadUrl} download={localName} hidden> download_file </a> )} diff --git a/src/files-ui/components/file-mosaic/components/file-mosaic/FileMosaicProps.ts b/src/files-ui/components/file-mosaic/components/file-mosaic/FileMosaicProps.ts index 899f9a6391eea5e8ed9966e5e7de4340744bc660..29d1948f4d7a34dce3179bde51dae77d916b6786 100644 --- a/src/files-ui/components/file-mosaic/components/file-mosaic/FileMosaicProps.ts +++ b/src/files-ui/components/file-mosaic/components/file-mosaic/FileMosaicProps.ts @@ -56,9 +56,15 @@ export interface FileMosaicPropsMap extends OverridableComponentProps { /** * A string representation or web url of the image * that will be set to the "src" prop of an <img/> tag - * <img src={`${url}`} /> + * <img src={`${imageUrl}`} /> */ imageUrl?: string; + /** + * A string representation or web url of the video + * that will be set to the "src" prop of an <video/> tag + * <video src={`${videoUrl}`} /> + */ + videoUrl?: string; /** * If true, a background blur image will be shown */ @@ -91,7 +97,7 @@ export interface FileMosaicPropsMap extends OverridableComponentProps { /** * A function that return a file object when "play" button is presssed or clicked */ - onWatch?: (videoSource: File | undefined) => void; + onWatch?: (videoSource: File | string | undefined) => void; /** * Event that is triggered when `delete` button is clicked or pressed. * If present, `delete` button will be visible. diff --git a/src/files-ui/components/file-mosaic/hooks/useFileMosaicInitializer.ts b/src/files-ui/components/file-mosaic/hooks/useFileMosaicInitializer.ts index e28ec9028fcc6c72c85816cbd04a642fbe72d142..b28c2ce32da74459a98b4c8e6bca07de6518db28 100644 --- a/src/files-ui/components/file-mosaic/hooks/useFileMosaicInitializer.ts +++ b/src/files-ui/components/file-mosaic/hooks/useFileMosaicInitializer.ts @@ -19,15 +19,17 @@ const useFileMosaicInitializer = ( valid: boolean | undefined | null, preview: boolean, imageUrl: string | undefined, + videoUrl: string | undefined, xhr?: XMLHttpRequest, -): [boolean,boolean, boolean, string, string | undefined] => { +): [boolean, boolean, boolean, string, string | undefined, File | string | undefined] => { const [isImage, setIsImage] = React.useState<boolean>(false); const [isVideo, setIsVideo] = React.useState<boolean>(false); const [url, setUrl] = React.useState<string>(""); const [imageSource, setImageSource] = React.useState<string | undefined>(undefined); - const [isReady,setIsReady]=React.useState(false); + const [videoSource, setVideoSource] = React.useState<File | string | undefined>(undefined); + const [isReady, setIsReady] = React.useState(false); const init = async ( @@ -37,6 +39,7 @@ const useFileMosaicInitializer = ( valid: boolean | undefined | null, preview: boolean, imageUrl: string | undefined, + videoUrl: string | undefined, xhr?: XMLHttpRequest, progress?: number ) => { @@ -51,16 +54,27 @@ const useFileMosaicInitializer = ( //console.log("init", url); setUrl(url); - - if (imageUrl) { + //In case is a video and also has a image preview + if (imageUrl && videoUrl) { + setIsVideo(true); + setImageSource(imageUrl); + setVideoSource(videoUrl); + setIsReady(true); + return; + } else if (imageUrl) { setIsImage(true); setImageSource(imageUrl); setIsReady(true); return; + } else if (videoUrl) { + setIsVideo(true); + setVideoSource(videoUrl); + setIsReady(true); } else { const [headerMime, tailMime] = getHeaderAndTail(file, type); setIsImage(headerMime === "image"); + setIsVideo( headerMime === "video" && ["mp4", "ogg", "webm"].includes(tailMime) ); @@ -93,7 +107,7 @@ const useFileMosaicInitializer = ( ////// CLEAN UP React.useEffect(() => { - init(file, name, type, valid, preview || false, imageUrl); + init(file, name, type, valid, preview || false, imageUrl, videoUrl); return () => { setImageSource(undefined); setIsImage(false); @@ -101,8 +115,9 @@ const useFileMosaicInitializer = ( setIsReady(false); }; // eslint-disable-next-line - }, [file, name, type, valid, preview, imageUrl,]); - return [isReady,isImage, isVideo, url, imageSource]; + }, [file, name, type, valid, preview, imageUrl, videoUrl]); + + return [isReady, isImage, isVideo, url, imageSource, videoSource]; } export default useFileMosaicInitializer; diff --git a/src/files-ui/components/file-status/ErrorStatus.tsx b/src/files-ui/components/file-status/ErrorStatus.tsx index b17f6bde8f44446dc232bfe0196a371a921ccf09..e930203c62556e7baa55443ad821c3a981300e80 100644 --- a/src/files-ui/components/file-status/ErrorStatus.tsx +++ b/src/files-ui/components/file-status/ErrorStatus.tsx @@ -17,7 +17,7 @@ const ErrorStatus: React.FC<ErrorStatusProps> = (props: ErrorStatusProps) => { backgroundColor: "rgba(244, 67, 54, 0.8)", borderRadius: "50%", }} - size={size || 65} + size={size || 65} /> <span> {FileItemStatusLocalizer.error as string}</span> </React.Fragment> diff --git a/src/files-ui/components/previews/VideoPreview/VideoPreview.tsx b/src/files-ui/components/previews/VideoPreview/VideoPreview.tsx index 63d7dfcdb64f78f828c2085523f041d0244d29ce..3cfe978e3d08e4e6e2a18713fe1a9b1c91b7899e 100644 --- a/src/files-ui/components/previews/VideoPreview/VideoPreview.tsx +++ b/src/files-ui/components/previews/VideoPreview/VideoPreview.tsx @@ -4,7 +4,12 @@ import { VideoPreviewProps } from "./VideoPreviewProps"; const VideoPreview: React.FC<VideoPreviewProps> = ( props: VideoPreviewProps ) => { - const { videoSrc, /* autoPlay, controls, */style, className, ...others } = props; + const { + src: videoSrc, + /* autoPlay, controls, */ style, + className, + ...others + } = props; const videoRef = React.useRef<HTMLVideoElement>(null); diff --git a/src/files-ui/components/previews/VideoPreview/VideoPreviewProps.ts b/src/files-ui/components/previews/VideoPreview/VideoPreviewProps.ts index 5f7ba752ef3007c137744f6307cdfa7a0a91e671..95d7159c5bde3da6d2c1528a1f55f795700e2afb 100644 --- a/src/files-ui/components/previews/VideoPreview/VideoPreviewProps.ts +++ b/src/files-ui/components/previews/VideoPreview/VideoPreviewProps.ts @@ -6,22 +6,20 @@ export interface VideoPreviewPropsMap extends OverridableComponentProps { * video source in string format or File object * FileItemComponent returns this value in onWatch handler */ - videoSrc?: File | string; + src?: File | string; } -type DefVideoProps = React.DetailedHTMLProps<React.VideoHTMLAttributes<HTMLVideoElement>, HTMLVideoElement>; +type DefVideoProps = React.HTMLProps<HTMLVideoElement>; +type VideoPropsOmitVideoPreviewPropsMap = Omit<DefVideoProps, keyof VideoPreviewPropsMap>; -export type VideoPreviewProps = +export type VideoPreviewProps = VideoPropsOmitVideoPreviewPropsMap & { [F in keyof VideoPreviewPropsMap]: VideoPreviewPropsMap[F] } - & - { [K in keyof DefVideoProps]: - DefVideoProps[K] - } + diff --git a/src/files-ui/components/tooltip/components/Tooltip.scss b/src/files-ui/components/tooltip/components/Tooltip.scss index 0b35afd6fde8c572c9f6a74493273959aae08cbe..1e186717ea9d0deda76f77c35888b7b849707443 100644 --- a/src/files-ui/components/tooltip/components/Tooltip.scss +++ b/src/files-ui/components/tooltip/components/Tooltip.scss @@ -12,6 +12,66 @@ z-index: 2; } } + &.card { + &:hover { + z-index: 2; + .files-ui-tooltiptext { + visibility: visible; + opacity: 1; + z-index: 2; + } + } + .files-ui-tooltiptext { + box-sizing: border-box; + font-family: "Poppins", sans-serif; + + font-size: 0.8rem; + font-weight: 400; + visibility: hidden; + width: 200px; + // background-color: green; + color: #fff; + text-align: center; + border-radius: 6px; + padding: 2px 2px; + position: absolute; + z-index: 2; + left: calc(50% - 100px); + left: 0; + margin-top: 5px; + //top: calc(100% + 5px); + top: 100%; + // Fade in tooltip - takes 1 second to go from 0% to 100% opac: + opacity: 0; + transition: opacity 1s; + + &.not-valid-error { + background: linear-gradient(to top, #c62828, #d32f2f); + &::after { + border-color: transparent transparent #d32f2f transparent; + } + } + &.success { + //background-color: green; + background: linear-gradient(to top, #1b5e20, #2e7d32); + &::after { + border-color: transparent transparent #2e7d32 transparent; + } + } + + &::after { + content: ""; + position: absolute; + bottom: 100%; + left: 50%; + margin-left: -5px; + border-width: 5px; + border-style: solid; + //border-color: transparent transparent green transparent; + } + } + } + .files-ui-tooltiptext { box-sizing: border-box; font-family: "Poppins", sans-serif; @@ -46,6 +106,7 @@ border-color: transparent transparent #2e7d32 transparent; } } + &::after { content: ""; position: absolute; diff --git a/src/files-ui/index.ts b/src/files-ui/index.ts index 2fa22c1948c5632f693cee686783bfc5be82aebe..ed04b69303497448d9adb23d2ef53945757f26b8 100644 --- a/src/files-ui/index.ts +++ b/src/files-ui/index.ts @@ -7,6 +7,9 @@ export * from "./components/avatar/Avatar"; export { FileItem } from "./components"; export * from "./components"; +export { default as FileCard } from "./components/file-card/FileCard"; +export * from "./components/file-card/FileCard"; + export { default as FileMosaic } from "./components/file-mosaic/components/file-mosaic/FileMosaic"; export * from "./components/file-mosaic/components/file-mosaic/FileMosaic"; @@ -19,8 +22,13 @@ export * from "./components/previews/ImagePreview/ImagePreview"; export { default as VideoPreview } from "./components/previews/VideoPreview/VideoPreview"; export * from "./components/previews/VideoPreview/VideoPreview"; +export type { FileMosaicProps } from "./components/file-mosaic/components/file-mosaic/FileMosaicProps"; +export type { DropzoneProps } from "./components/dropzone/components/dropzone/DropzoneProps"; +export type { FileInputButtonProps } from "./components/file-input-button/InputButtonProps"; +export type { FileCardProps } from "./components/file-card/FileCardProps"; + export { useFakeProgress } from "./hooks"; -export {createListOfMultiTypeFile} from "./core"; +export { createListOfMultiTypeFile, uploadFile, uploadFormData, uploadExtFile, ExtFileInstance, ExtFileManager } from "./core"; -export type { ExtFile, ExtFileInstance, ExtFileListMap, ExtFileManager, UPLOADSTATUS, Localization } from "./core"; \ No newline at end of file +export type { ExtFile, ExtFileListMap, UPLOADSTATUS, Localization, ServerResponse, UploadResponse, UploadConfig } from "./core"; \ No newline at end of file diff --git a/src/pages/FileCardMock.tsx b/src/pages/FileCardMock.tsx index b8b353ad9be98b186488a946d35ee77b053cff07..75299f2390097f48a701c9a16b2ae4017f3fb6fa 100644 --- a/src/pages/FileCardMock.tsx +++ b/src/pages/FileCardMock.tsx @@ -1,6 +1,5 @@ import * as React from "react"; -import FileCard from "../files-ui/components/file-item/components/FileCard/FileCard"; -import { ExtFile } from "../files-ui/core"; +import { FileCard, ExtFile } from "../files-ui"; const baseFiles: ExtFile[] = [ { id: Math.random(), @@ -15,7 +14,7 @@ const baseFiles: ExtFile[] = [ type: "image/png", size: 280000, valid: true, - uploadStatus:"uploading", + uploadStatus: "uploading", imageUrl: "https://super-ficcion.com/wp-content/uploads/2022/10/como-podria-regresar-iron-man-1-780x470.webp", }, @@ -37,10 +36,10 @@ const FileCardMock = ({ darkMode = false, elevation = 2 }) => { onSee={handleSee} info alwaysActive - hd + //hd elevation={2} darkMode={darkMode} - onCancel={()=>{}} + onCancel={() => {}} progress={25} /> ))} diff --git a/src/pages/FileItemMock.tsx b/src/pages/FileItemMock.tsx index d1dd261ae5684bd95ed8f4c9c4cf7f1010127cc1..d118bb015113bf21ce69e53aa50ac832e060c4ff 100644 --- a/src/pages/FileItemMock.tsx +++ b/src/pages/FileItemMock.tsx @@ -1,10 +1,12 @@ import * as React from "react"; import { FileItem } from "../files-ui"; -import { FileMosaic, FileMosaicProps } from "../files-ui/components/file-mosaic"; -import { BasePreparingLoader } from "../files-ui/components/loader"; -import InfiniteLoader from "../files-ui/components/loader/InfiniteLoader/InfiniteLoader"; -import LoaderContainer from "../files-ui/components/loader/LoaderContainer/LoaderContainer"; -import { ExtFile, UPLOADSTATUS } from "../files-ui/core"; +import { + FileMosaic, + FileMosaicProps, + ExtFile, + UPLOADSTATUS, +} from "../files-ui"; + const baseFiles: ExtFile[] = [ { id: Math.random(), diff --git a/src/pages/demo/FileCardDemoPage.jsx b/src/pages/demo/FileCardDemoPage.jsx index 3309c7d19fb3ee66af3018be17a76f895f19f327..6dcd66dfc78fc245cd93b6b5e239e916526210a3 100644 --- a/src/pages/demo/FileCardDemoPage.jsx +++ b/src/pages/demo/FileCardDemoPage.jsx @@ -2,17 +2,27 @@ import * as React from "react"; import MainContentContainer from "../../components/layout-pages/MainContentContainer"; import RightMenuContainer from "../../components/layout-pages/RightMenuContainer"; import RightMenu from "../../components/RightMenu/RightMenu"; -import Paper from "@mui/material/Paper"; -import Stack from "@mui/material/Stack"; -import Alert from "@mui/material/Alert"; import CodeHighlight from "../../components/codeHighlight/CodeHighlight"; import DescParagraph from "../../components/demo-components/desc-paragraph/DescParagraph"; -import BasicFileMosaicDemo from "../../components/demo-components/filemosaic-demo/DemoFileMosaicBasic"; import SubTitle from "../../components/demo-components/sub-title/SubTitle"; import TypeHighlight from "../../components/typeHighlight/TypeHighlight"; import MainTitle from "../../components/main-title/MainTitle"; import MainParagraph from "../../components/paragraph-main/MainParagraph"; - +import DemoFileCardBasic from "../../components/demo-components/file-card-demo/DemoFileCardBasic"; +import CodeJSFileCardBasic from "../../components/demo-components/file-card-demo/CodeJSFileCardBasic"; +import { AlertTitle, Alert, Paper } from "@mui/material"; +import DemoFileMosaicImagePreview from "../../components/demo-components/filemosaic-demo/DemoFileMosaicImagePreview"; +import CodeJSFileMosaicImagePreview from "../../components/demo-components/filemosaic-demo/CodeJSFileMosaicImagePreview"; +import DemoContainerFileMosaic from "../../components/demo-components/filemosaic-demo/DemoContainerFileMosaic"; +import DemoFileMosaicDarkMode from "../../components/demo-components/filemosaic-demo/DemoFileMosaicDarkMode"; +import CodeJSFileMosaicDarkMode from "../../components/demo-components/filemosaic-demo/CodeJSFileMosaicDarkMode"; +import AnchorToTab from "../../components/util-components/AnchorToTab"; +import DemoFileMosaicValidation from "../../components/demo-components/filemosaic-demo/DemoFileMosaicValidation"; +import CodeJSFileMosaicValidation from "../../components/demo-components/filemosaic-demo/CodeJSFileMosaicValidation"; +import DemoFileMosaicUploadStatus from "../../components/demo-components/filemosaic-demo/DemoFileMosaicUploadStatus"; +import CodeJSFileMosaicUploadStatus from "../../components/demo-components/filemosaic-demo/CodeJSFileMosaicUploadStatus"; +import DemoFileCardActions from "../../components/demo-components/filemosaic-demo/DemoFileCradActions"; +import CodeJSFileCardActions from "../../components/demo-components/filemosaic-demo/CodeJSFileCardActions"; const FileCardDemoPage = (props) => { return ( @@ -20,74 +30,314 @@ const FileCardDemoPage = (props) => { <MainContentContainer> <MainTitle>FileCard</MainTitle> <MainParagraph> - File mosaics are compact elements that represent a file in the UI. - They can be used for just showing general info of the file, or either + File cards are compact elements that represent a file in the UI. They + can be used for just showing general info of the file, or either to allow the user to interact with them. </MainParagraph> <DescParagraph> - This widget allow users to see information of Files and / or trigger - actions. + This widget allow users to see information of{" "} + <TypeHighlight> Files</TypeHighlight> and / or trigger actions. </DescParagraph> <Alert severity="info"> While included here as a standalone component, the most common use - will be as a result of the "onChange" event of {"<Dropzone/>"} or{" "} - {"<InputButton/>"} components, so some of the behavior demonstrated - here is not shown in context.{" "} + will be to display the result of the "onChange" event of{" "} + <CodeHighlight>{"<Dropzone/>"}</CodeHighlight> or{" "} + <CodeHighlight>{"<InputButton/>"}</CodeHighlight> components, so some + of the behavior demonstrated here is not totally shown in context.{" "} </Alert> - <section id="basic-filemosaic"> - <SubTitle content="Basic FileMosaic" /> + <section id="basic-filecard"> + <SubTitle content="Basic FileCard" /> <DescParagraph> - The <CodeHighlight>FileMosaic</CodeHighlight> supports displaying - information from <TypeHighlight>File</TypeHighlight> object or - individual props. + The <CodeHighlight>FileCard</CodeHighlight> component supports + displaying information from a{" "} + <TypeHighlight> + <AnchorToTab href="https://developer.mozilla.org/en-US/docs/Web/API/File"> + File + </AnchorToTab> + </TypeHighlight>{" "} + object or from given props. + <br /> + Also, the <TypeHighlight>onDelete</TypeHighlight> prop is used to + remove the file selection. </DescParagraph> - <Paper - variant="outlined" - style={{ - padding: "25px", - display: "flex", - alignItems: "center", - justifyContent: "center", - }} - > - <Stack spacing={2} direction="row" alignItems={"center"}> - <BasicFileMosaicDemo /> - </Stack> - </Paper> - <p></p> - {/* <BasicDropzoneCodeJS /> */} + <DemoContainerFileMosaic> + <DemoFileCardBasic /> + </DemoContainerFileMosaic> + <CodeJSFileCardBasic card /> + + <Alert severity="info"> + <AlertTitle> FileInputButton </AlertTitle> + For completeness, some of these examples include{" "} + <CodeHighlight>{`<FileInputButton/>`} </CodeHighlight> + component for allowing the user to select files. For further + information of this component check out the{" "} + <a href="/components/fileinputbutton">FileInputButton</a> page. + </Alert> + <br /> + <Alert severity="info"> + <AlertTitle> ExtFile </AlertTitle> + {/* This is an info alert — <strong>check it out!</strong> + */} + <strong>ExtFile type </strong> + is explicity used in the + <strong> Typescript</strong> example and is implicity used in the{" "} + <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> + </Alert> </section> <section id="image-preview"> <SubTitle content="Image preview" /> <DescParagraph> By setting the <CodeHighlight>preview</CodeHighlight> prop to{" "} - <TypeHighlight>true</TypeHighlight> the component will show a image - preview using the <CodeHighlight>imageUrl</CodeHighlight> - prop or by reading the <TypeHighlight>File</TypeHighlight> object if - given (file prop). + <TypeHighlight>true</TypeHighlight> the component will show an image + preview instead of a file icon. + <br /> + To acomplish this task this component will take the{" "} + <CodeHighlight>imageUrl</CodeHighlight> + prop or will read the <TypeHighlight>File</TypeHighlight> object if + given and if it is an image. + <br /> + Finally, the <TypeHighlight>info</TypeHighlight> prop is used to + show the complete information of the file. </DescParagraph> - <Paper variant="outlined" style={{ padding: "25px" }}> - {/* <BasicDemoDropzone /> */} - </Paper> - <p></p> - {/* <BasicDropzoneCodeJS /> */} + <DemoContainerFileMosaic> + <DemoFileMosaicImagePreview card /> + </DemoContainerFileMosaic> + + <CodeJSFileMosaicImagePreview card /> + <Alert severity="info"> + As you can notice, when + <CodeHighlight>{`imageUrl`}</CodeHighlight> prop is present, the{" "} + <CodeHighlight>{`preview`}</CodeHighlight> prop is not needed since + it has more priority. + <br /> + On the other side, for displaying an image preview as a result of + reading an image File it is necesary to set the{" "} + <CodeHighlight>{`preview`}</CodeHighlight> prop, otherwise a default + image preview will be shown in order to save memory. + </Alert> </section> + {/** VALIDATION UPLOAD AND MORE */} + <section id="validation"> <SubTitle content="Validation" /> <DescParagraph> The <CodeHighlight>valid</CodeHighlight> prop can be set to{" "} <TypeHighlight>true</TypeHighlight>,{" "} <TypeHighlight>false</TypeHighlight> or{" "} - <TypeHighlight>undefined</TypeHighlight> + <TypeHighlight>undefined</TypeHighlight>. </DescParagraph> - <Paper variant="outlined" style={{ padding: "25px" }}> - {/* <BasicDemoDropzone /> */} + <DemoContainerFileMosaic card> + <DemoFileMosaicValidation card /> + </DemoContainerFileMosaic> + + <CodeJSFileMosaicValidation card /> + + <Alert severity="info"> + Typically, <CodeHighlight>{"<Dropzone/>"}</CodeHighlight> or{" "} + <CodeHighlight>{"<FileInputButton/>"}</CodeHighlight> components set + this prop when validating the input from a given criteria. You can + see the behaviour mentioned in the following demos: + <ul> + <li> + <AnchorToTab href="/components/dropzone#validation"> + Dropzone validation + </AnchorToTab> + </li> + <li> + <AnchorToTab href="/components/fileinputbutton#validation"> + FileInputButton validation + </AnchorToTab> + </li> + </ul> + </Alert> + </section> + + <section id="uploading"> + <SubTitle content="Uploading status" /> + <DescParagraph> + The <CodeHighlight>uploadStatus</CodeHighlight> prop can be set to{" "} + <TypeHighlight>"preparing"</TypeHighlight>,{" "} + <TypeHighlight>"uploading"</TypeHighlight>,{" "} + <TypeHighlight>"aborted"</TypeHighlight>,{" "} + <TypeHighlight>"error"</TypeHighlight> or{" "} + <TypeHighlight>"success"</TypeHighlight>. Also the{" "} + <CodeHighlight>uploadMessage</CodeHighlight> prop is used for + displaying the error or success message. Finally, the{" "} + <CodeHighlight>progress</CodeHighlight> prop can be used to show the + current progress of the upload process. + <br /> + Each of the following examples demonstrates one state combination of + the FileCard component. + </DescParagraph> + + <DemoContainerFileMosaic card> + <DemoFileMosaicUploadStatus card /> + </DemoContainerFileMosaic> + + <CodeJSFileMosaicUploadStatus card /> + + <Alert severity="info"> + As you can see, you have full control of the FileCard upload props. + You can take advantage of them to ake your own upload function and + show the user the progress. + <br /> On the other hand, you can also leverage the capability of{" "} + <CodeHighlight>{"<Dropzone/>"}</CodeHighlight> and{" "} + <CodeHighlight>{"<FileInputButton/>"}</CodeHighlight> components + since they also manage the{" "} + <TypeHighlight>{"uploadStatus"}</TypeHighlight> + prop for you when upload is enabled. You can see the behaviour + mentioned in the following demos: + <ul> + <li> + <AnchorToTab href="/components/dropzone#uploading"> + Dropzone upload + </AnchorToTab> + </li> + <li> + <AnchorToTab href="/components/fileinputbutton#uploading"> + FileInputButton upload + </AnchorToTab> + </li> + </ul> + </Alert> + </section> + <section id="actions"> + <SubTitle content="Actions: info, delete, see, watch, download" /> + <DescParagraph> + You can use the following actions. + <ul> + <li> + FileCard with the <CodeHighlight>onDelete</CodeHighlight> prop + will display a delete icon. + </li> + <li> + FileCard with the <CodeHighlight>info</CodeHighlight> prop will + display an "info" icon that will display an info layer. + </li> + <li> + FileCard with the <CodeHighlight>onSee</CodeHighlight> prop will + display the "see" button that can be used to retrieve an image + <TypeHighlight>URI</TypeHighlight> obtained by reading a{" "} + <TypeHighlight>File</TypeHighlight> object if given or just the{" "} + <TypeHighlight>imageUrl</TypeHighlight> prop if given. In the + first case, the file must be an image. + </li> + <li> + FileCard with the <CodeHighlight>onWatch</CodeHighlight> prop + will display the "play" icon that can be used to retrieve the + video as a <TypeHighlight>File</TypeHighlight> object if given + or just the <TypeHighlight>videoUrl</TypeHighlight> prop if + given. The file must be an video. + </li> + <li> + FileCard with the <CodeHighlight>downloadUrl</CodeHighlight>{" "} + prop will display the "download" icon that can be used to start + the download. If the <CodeHighlight>onDownload</CodeHighlight>{" "} + prop is given, the "download" icon will also be visible, but it + will be taken as the user will perform their own download. + </li> + </ul> + </DescParagraph> + + <Paper + variant="outlined" + style={{ + padding: "25px", + display: "flex", + alignItems: "center", + justifyContent: "center", + gap: "7px", + flexWrap: "wrap", + width: "100%", + //padding: "25px 0", + flexDirection: "row", + }} + > + <DemoFileCardActions card /> </Paper> - <p></p> - {/* <BasicDropzoneCodeJS /> */} + <CodeJSFileCardActions card /> + <Alert severity="info"> + <AlertTitle> + {" "} + <strong>downloadUrl</strong> and <strong>onDownload</strong> props + </AlertTitle> + {/* This is an info alert — <strong>check it out!</strong> + */} + When only <CodeHighlight>downloadUrl</CodeHighlight> is specifyed, + FileCard will perform the download only for{" "} + <AnchorToTab href="https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy"> + same-origin URLs + </AnchorToTab>{" "} + since it uses the{" "} + <AnchorToTab href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a"> + anchor + </AnchorToTab>{" "} + tag under the hood. + <br /> + If the resource is located in any other host, it will open a new tab + and display the content. + <br /> + For avoiding that behaviour you can do you own download + implementation by overriding the download function by setting the{" "} + <CodeHighlight>onDownload</CodeHighlight> prop. + <br /> + You can check an example here: + <ul> + <li> + <AnchorToTab href="/utilities-methods/file-downloader"> + {"Download a File"} + </AnchorToTab> + </li> + </ul> + </Alert> + </section> + <section id="dark-mode"> + <SubTitle content="Dark mode and Elevation" /> + <DescParagraph> + The <CodeHighlight>FileCard</CodeHighlight> component supports dark + mode by setting the prop <TypeHighlight>darkMode</TypeHighlight> to{" "} + <TypeHighlight>true</TypeHighlight>. The elevation can be used to + establish a hierarchy between other content. In practical terms, the + elevation controls the size of the shadow applied to the surface. In + dark mode, raising the elevation also makes the surface lighter. + </DescParagraph> + + <div + style={{ + display: "flex", + flexWrap: "wrap", + width: "100%", + padding: "25px 0", + }} + > + <DemoFileMosaicDarkMode card /> + </div> + + <CodeJSFileMosaicDarkMode card /> + </section> + + <section id="api"> + <SubTitle content="API" /> + <DescParagraph> + See the documentation below for a complete reference to all of the + props available to the components mentioned here. + </DescParagraph> + <ul> + <li> + <AnchorToTab href="/api/filecard">{"<FileCard/>"}</AnchorToTab> + </li> + <li>| + <AnchorToTab href="/api/fileinputbuttom"> + {"<FileInputButton/>"} + </AnchorToTab> + </li> + </ul> </section> </MainContentContainer> <RightMenuContainer> @@ -100,47 +350,37 @@ export default FileCardDemoPage; const rightMenuItems = [ { id: 0, - label: "Basic file mosaic", - referTo: "/components/file-mosaic#basic-filemosaic", + label: "Basic file card", + referTo: "/components/filecard#basic-filecard", }, { id: 1, label: "Image Preview", - referTo: "/components/file-mosaic#file-mosaic-image-preview", + referTo: "/components/filecard#image-preview", }, { id: 2, label: "Validation", - referTo: "/components/file-mosaic#file-mosaic-validation", + referTo: "/components/filecard#validation", }, { id: 3, label: "Uploading", - referTo: "/components/file-mosaic#file-mosaic-uploading", + referTo: "/components/filecard#uploading", }, { id: 4, - label: "Localization", - referTo: "/components/file-mosaic#file-mosaic-localization", + label: "Actions", + referTo: "/components/filecard#actions", }, { id: 5, - label: "Previews", - referTo: "/components/file-mosaic#file-mosaic-previews", + label: "Dark mode", + referTo: "/components/filecard#dark-mode", }, { id: 6, - label: "Actions", - referTo: "/components/file-mosaic#actions", - }, - { - id: 7, - label: "Default previews", - referTo: "/components/file-mosaic#default-previews", - }, - { - id: 8, - label: "Dark mode", - referTo: "/components/file-mosaic#dark-mode", + label: "API", + referTo: "/components/filecard#api", }, ]; diff --git a/src/pages/demo/FileMosaicDemoPage.jsx b/src/pages/demo/FileMosaicDemoPage.jsx index d3a54f2913477d4e2a50890436aadfa231870e6c..af381aaa5403ea9c6abfa032ad8c8543e4d7b264 100644 --- a/src/pages/demo/FileMosaicDemoPage.jsx +++ b/src/pages/demo/FileMosaicDemoPage.jsx @@ -1,6 +1,4 @@ -import Alert from "@mui/material/Alert"; -import Paper from "@mui/material/Paper"; -import AlertTitle from "@mui/material/AlertTitle"; +import { Alert, AlertTitle, Paper } from "@mui/material"; import * as React from "react"; import CodeHighlight from "../../components/codeHighlight/CodeHighlight"; import DescParagraph from "../../components/demo-components/desc-paragraph/DescParagraph"; @@ -27,6 +25,8 @@ import CodeJSFileMosaicLocalization from "../../components/demo-components/filem import DemoFileMosaicDarkMode from "../../components/demo-components/filemosaic-demo/DemoFileMosaicDarkMode"; import CodeJSFileMosaicDarkMode from "../../components/demo-components/filemosaic-demo/CodeJSFileMosaicDarkMode"; import DemoFileMosaicFileIcons from "../../components/demo-components/filemosaic-demo/DemoFileMosaicFileIcons"; +import DemoFileCardActions from "../../components/demo-components/filemosaic-demo/DemoFileCradActions"; +import CodeJSFileCardActions from "../../components/demo-components/filemosaic-demo/CodeJSFileCardActions"; const FileMosaicDemoPage = (props) => { return ( @@ -221,6 +221,96 @@ const FileMosaicDemoPage = (props) => { </ul> </Alert> </section> + <section id="actions"> + <SubTitle content="Actions: info, delete, see, watch, download" /> + <DescParagraph> + You can use the following actions. + <ul> + <li> + FileMosaic with the <CodeHighlight>onDelete</CodeHighlight> prop + will display a delete icon. + </li> + <li> + FileMosaic with the <CodeHighlight>info</CodeHighlight> prop + will display an "info" icon that will display an info layer. + </li> + <li> + FileMosaic with the <CodeHighlight>onSee</CodeHighlight> prop + will display the "see" button that can be used to retrieve an + image <TypeHighlight>URI</TypeHighlight> obtained by reading a{" "} + <TypeHighlight>File</TypeHighlight> object if given or just the{" "} + <TypeHighlight>imageUrl</TypeHighlight> prop if given. In the + first case, the file must be an image. + </li> + <li> + FileMosaic with the <CodeHighlight>onWatch</CodeHighlight> prop + will display the "play" icon that can be used to retrieve the + video as a <TypeHighlight>File</TypeHighlight> object if given + or just the <TypeHighlight>videoUrl</TypeHighlight> prop if + given. The file must be an video. + </li> + <li> + FileMosaic with the <CodeHighlight>downloadUrl</CodeHighlight>{" "} + prop will display the "download" icon that can be used to start + the download. If the <CodeHighlight>onDownload</CodeHighlight>{" "} + prop is given, the "download" icon will also be visible, but it + will be taken as the user will perform their own download. + </li> + </ul> + </DescParagraph> + + <Paper + variant="outlined" + style={{ + padding: "25px", + display: "flex", + alignItems: "center", + justifyContent: "center", + gap: "7px", + flexWrap: "wrap", + width: "100%", + //padding: "25px 0", + flexDirection: "row", + }} + > + <DemoFileCardActions /> + </Paper> + <CodeJSFileCardActions /> + <Alert severity="info"> + <AlertTitle> + {" "} + <strong>downloadUrl</strong> and <strong>onDownload</strong> props + </AlertTitle> + {/* This is an info alert — <strong>check it out!</strong> + */} + When only <CodeHighlight>downloadUrl</CodeHighlight> is specifyed, + FileMosaic will perform the download only for{" "} + <AnchorToTab href="https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy"> + same-origin URLs + </AnchorToTab>{" "} + since it uses the{" "} + <AnchorToTab href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a"> + anchor + </AnchorToTab>{" "} + tag under the hood. + <br /> + If the resource is located in any other host, it will open a new tab + and display the content. + <br /> + For avoiding that behaviour you can do you own download + implementation by overriding the download function by setting the{" "} + <CodeHighlight>onDownload</CodeHighlight> prop. + <br /> + You can check an example here: + <ul> + <li> + <AnchorToTab href="/utilities-methods/file-downloader"> + {"Download a File"} + </AnchorToTab> + </li> + </ul> + </Alert> + </section> <section id="dark-mode"> <SubTitle content="Dark mode" /> <DescParagraph> @@ -245,7 +335,8 @@ const FileMosaicDemoPage = (props) => { <CodeJSFileMosaicDarkMode /> </section> - <section id="localization"> + + {/* <section id="localization"> <SubTitle content="Localization" /> <DescParagraph> The <CodeHighlight>FileMosaic</CodeHighlight> component has @@ -285,9 +376,9 @@ const FileMosaicDemoPage = (props) => { <DemoFileMosaicLocalization /> </Paper> <CodeJSFileMosaicLocalization /> - </section> + </section> */} - <section id="file-icon"> + {/* <section id="file-icon"> <SubTitle content="File Icons (extensive list)" /> <DescParagraph> The <CodeHighlight>FileMosaic</CodeHighlight> component supports @@ -308,7 +399,7 @@ const FileMosaicDemoPage = (props) => { > <DemoFileMosaicFileIcons /> </Paper> - </section> + </section> */} <section id="api"> <SubTitle content="API" /> @@ -359,18 +450,18 @@ const rightMenuItems = [ referTo: "/components/filemosaic#uploading", }, { - id: 8, - label: "Dark mode", - referTo: "/components/filemosaic#dark-mode", + id: 4, + label: "Actions", + referTo: "/components/filemosaic#actions", }, { - id: 4, - label: "Localization", - referTo: "/components/filemosaic#localization", + id: 5, + label: "Dark mode", + referTo: "/components/filemosaic#dark-mode", }, { - id: 9, - label: "File Icons", - referTo: "/components/filemosaic#file-icon", + id: 6, + label: "API", + referTo: "/components/filemosaic#api", }, ]; diff --git a/src/pages/download-page/FileDownloadPage.jsx b/src/pages/download-page/FileDownloadPage.jsx new file mode 100644 index 0000000000000000000000000000000000000000..4e4690fbc14b77e0dec4ee24871b43edfa1addcf --- /dev/null +++ b/src/pages/download-page/FileDownloadPage.jsx @@ -0,0 +1,10 @@ +import * as React from "react"; + +const FileDownloadPage = props =>{ + return( + <div> + FileDownloadPage + </div> + ) +} +export default FileDownloadPage; \ No newline at end of file diff --git a/src/routes/MainRouter.jsx b/src/routes/MainRouter.jsx index d5ee59b3c25d5022600756a9d4c4e8bde7d61118..9fbb6588adc5a7b671053236d34818a37bc4f1ee 100644 --- a/src/routes/MainRouter.jsx +++ b/src/routes/MainRouter.jsx @@ -20,6 +20,7 @@ import AvatarDemoPage from "../pages/demo/AvatarDemoPage"; import FileInputButtonApi from "../pages/api/FileInputButtonApi"; import AvatarApi from "../pages/api/AvatarApi"; import FileInputButtonDemoPage from "../pages/demo/FileInputButtonDemoPage"; +import FileDownloadPage from "../pages/download-page/FileDownloadPage"; const router = createBrowserRouter([ { @@ -128,6 +129,10 @@ const router = createBrowserRouter([ path: "/utilities-methods/file-uploader", element: <FileUploaderPage />, }, + { + path: "/utilities-methods/file-downloader", + element: <FileDownloadPage />, + }, ], }, ]); diff --git a/src/tester/AvatarTester.tsx b/src/tester/AvatarTester.tsx index 9e691c9b71ad2823b8f1106a4adfa4bbf6eb1c7c..fd8b8cdf87815abb668fd738da2dcb1a8b8c64cf 100644 --- a/src/tester/AvatarTester.tsx +++ b/src/tester/AvatarTester.tsx @@ -1,12 +1,6 @@ import * as React from "react"; import { Avatar } from "../files-ui"; -import { - ServerResponse, - //UploadPromiseResponse, - //uploadPromiseXHR, -} from "../files-ui/core"; - -import { uploadFile } from "../files-ui/core"; +import { ServerResponse, uploadFile } from "../files-ui"; export const resultURL: string = "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ1YdE-2sQT4na6RwujeATyMBcCXqg0Gf76TYTplRkMkq0wIi_SewCDq6VeUGxPwpK_Qd8&usqp=CAU"; @@ -25,10 +19,10 @@ const rowStyle: React.CSSProperties = { gap: "15px", }; - -const REMOTE = "https://files-ui-express-static-file-storage.vercel.app/39d33dff2d41b522c1ea276c4b82507f96b9699493d2e7b3f5c864ba743d9503"; -const LOCAL = "http://localhost/39d33dff2d41b522c1ea276c4b82507f96b9699493d2e7b3f5c864ba743d9503"; - +const REMOTE = + "https://files-ui-express-static-file-storage.vercel.app/39d33dff2d41b522c1ea276c4b82507f96b9699493d2e7b3f5c864ba743d9503"; +const LOCAL = + "http://localhost/39d33dff2d41b522c1ea276c4b82507f96b9699493d2e7b3f5c864ba743d9503"; interface AvatarTesterProps {} const AvatarTester: React.FC<AvatarTesterProps> = ( @@ -38,7 +32,7 @@ const AvatarTester: React.FC<AvatarTesterProps> = ( undefined ); - /* const handleChange = async (file: File) => { + /* const handleChange = async (file: File) => { const endpoint: string = "http://localhost:2800/upload-avatar"; const response: UploadPromiseResponse = await uploadPromiseXHR( { id: 0, file: file, xhr: new XMLHttpRequest() }, diff --git a/src/utils.ts b/src/utils.ts index b174a3d2c11e1d22669a742facb33213e54e44e4..392989b393e90dd8bcae14416e2027a181427d7d 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,4 @@ -import { ExtFile, ExtFileInstance } from "./files-ui/core"; +import { ExtFile, ExtFileInstance } from "./files-ui"; export const print_manager = (extFileList: ExtFile[] | ExtFileInstance[] | undefined, message: string = ""): void => { if (extFileList)