diff --git a/src/components/demo-components/filemosaic-demo/DemoFileMosaicUploadStatus.tsx b/src/components/demo-components/filemosaic-demo/DemoFileMosaicUploadStatus.tsx index ed044dff66fa76e7a871647996712dd8e1c5edb2..de2f6b16043c084ce4a3a91b842cc90d0401872b 100644 --- a/src/components/demo-components/filemosaic-demo/DemoFileMosaicUploadStatus.tsx +++ b/src/components/demo-components/filemosaic-demo/DemoFileMosaicUploadStatus.tsx @@ -4,9 +4,10 @@ import { useFakeProgress, ExtFile, UPLOADSTATUS, + FileCard, } from "../../../files-ui"; -const DemoFileMosaicUploadStatus = () => { +const DemoFileMosaicUploadStatus = (props:{card?:boolean}) => { const progress = useFakeProgress(); const [status1, setStatus1] = React.useState<UPLOADSTATUS>("uploading"); @@ -35,6 +36,32 @@ const DemoFileMosaicUploadStatus = () => { const handleAbort = (id: string | number | undefined) => { console.log("Upload aborted in file: " + id); }; + if(props.card) + return ( +<> + <FlexRowContainer card> + <FileCard {...preparingFile} /> + <FileCard {...preparingFile} onCancel={handleCancel} /> + </FlexRowContainer> + + <FlexRowContainer card> + <FileCard {...uploadingFile} /> + <FileCard {...uploadingFile} progress={progress} /> + <FileCard {...uploadingFile} onAbort={handleAbort} /> + <FileCard + {...uploadingFile} + onAbort={handleAbort} + progress={progress} + /> + </FlexRowContainer> + + <FlexRowContainer card> + <FileCard {...uploadResultFiles[0]} uploadStatus={status1} /> + <FileCard {...uploadResultFiles[1]} uploadStatus={status2} /> + <FileCard {...uploadResultFiles[2]} uploadStatus={status3} /> + </FlexRowContainer> + </> + ) return ( <> <FlexRowContainer> @@ -63,7 +90,7 @@ const DemoFileMosaicUploadStatus = () => { }; export default DemoFileMosaicUploadStatus; -const FlexRowContainer = (props: { children: React.ReactNode }) => { +const FlexRowContainer = (props: { children: React.ReactNode, card?:boolean }) => { return ( <div style={{ @@ -71,6 +98,7 @@ const FlexRowContainer = (props: { children: React.ReactNode }) => { flexWrap: "wrap", justifyContent: "space-evenly", width: "100%", + flexDirection:props.card?"column":"row" }} > {props.children} 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/files-ui/components/file-card/FileCard.scss b/src/files-ui/components/file-card/FileCard.scss index 4c3c8d0e869f0305ff02bdc8023d3833937ad67b..51b3b7dbe02a0d71a9aad3f4cc6a75e52f27411d 100644 --- a/src/files-ui/components/file-card/FileCard.scss +++ b/src/files-ui/components/file-card/FileCard.scss @@ -12,7 +12,7 @@ position: relative; font-size: 15px; font-weight: 400; - width: 380px; + width: 350px; .files-ui-file-card-main-layer-container { width: 380px; box-sizing: border-box; @@ -35,7 +35,7 @@ display: flex; align-items: center; justify-content: flex-start; - gap:5px; + gap: 5px; .file-card-icon-container { //margin: 0 10px; width: 100px; @@ -120,16 +120,11 @@ justify-content: space-between; flex-direction: column; padding: 3px; - width: 60px; + width: 36px; //background-color: aquamarine; height: 100%; } .file-card-upload-layer { - position: absolute; - left: 0; - right: 0; - width: 100%; - height: 100%; display: flex; box-sizing: border-box; //background-color: rgba(0, 0, 0, 0.5); @@ -153,5 +148,64 @@ align-items: center; justify-content: flex-end; } + .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.3), + rgba(0, 0, 0, 0.5), + 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) + ); + 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: 250px; + height: 100px; + text-align: left; + scrollbar-width: thin; + overflow: auto; + scrollbar-color: #646c7fa9 transparent; + + &::-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; + } + .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 index 2dabd5a73900227b0df2133dc461307129723671..7e192590fbba1b419a88bed7069e9fa14e94a7da 100644 --- a/src/files-ui/components/file-card/FileCard.tsx +++ b/src/files-ui/components/file-card/FileCard.tsx @@ -1,23 +1,17 @@ import * as React from "react"; -import { FileCardProps, FileCardPropsDefault } from "./FileCardProps"; +import { FileCardProps } from "./FileCardProps"; import "./FileCard.scss"; -import "./FileCardPaper.scss"; -import FileItemImage from "../file-item/components/FileItemImage/FileItemImage"; -import useFileItemInitializer from "../file-item/hooks/useFileItemInitializer"; +import "./components/FileCardPaper.scss"; import { getLocalFileItemData } from "../file-item/utils/getLocalFileItemData"; -import { Clear } from "../icons"; import { fileSizeFormater, shrinkWord } from "../../core"; -import { mergeProps } from "../overridable"; -import { showFileItemComponent } from "../file-item/utils/showFileItemComponent"; -import useFileItemProgress from "../file-item/hooks/useFileItemProgress"; -import MainLayerBodyNeo from "../file-item/components/FileItemMainLayer/MainLayerBody/MainLayerBodyNeo"; 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 FileCardRightLayer from "./FileCardRightLayer"; +import FileCardRightLayer from "./components/FileCardRightLayer"; +import FileCardInfoLayer from "./components/FileCardInfoLayer"; const setFinalElevation = (elevation: string | number): number => { // let finalElevation: number = ""; @@ -319,6 +313,16 @@ const FileCard: React.FC<FileCardProps> = (props: FileCardProps) => { isActive={alwaysActive || hovering} /> </Layer> + <Layer className="file-card-info-layer-container" visible={showInfo}> + <FileCardInfoLayer + onCloseInfo={handleCloseInfo} + valid={valid} + localization={localization} + localName={localName} + sizeFormatted={sizeFormatted} + localType={localType} + /> + </Layer> <Layer className="file-card-upload-layer" visible={isUploading}> Upload Layer </Layer> 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..bd548c3644ed55ef55aaa8bf8ffc76269a3d39ee --- /dev/null +++ b/src/files-ui/components/file-card/components/FileCardInfoLayer.tsx @@ -0,0 +1,42 @@ +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"> + <div className="files-ui-file-card-info-layer-header"> + <Cancel + style={{ margin: 0, right: 0, top: 0 }} + color="rgba(255,255,255,0.8)" + onClick={onCloseInfo} + colorFill="black" + /> + <FileMosaicStatus + valid={valid} + uploadStatus={uploadStatus} + localization={localization} + /> + </div> + <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-card/FileCardPaper.scss b/src/files-ui/components/file-card/components/FileCardPaper.scss similarity index 100% rename from src/files-ui/components/file-card/FileCardPaper.scss rename to src/files-ui/components/file-card/components/FileCardPaper.scss diff --git a/src/files-ui/components/file-card/FileCardRightLayer.tsx b/src/files-ui/components/file-card/components/FileCardRightLayer.tsx similarity index 97% rename from src/files-ui/components/file-card/FileCardRightLayer.tsx rename to src/files-ui/components/file-card/components/FileCardRightLayer.tsx index c2e766d4890db4cb7dff263a2bffd0a472da28ec..0ef7bc96dffe88f52ae9c751b0c52ac40f70e269 100644 --- a/src/files-ui/components/file-card/FileCardRightLayer.tsx +++ b/src/files-ui/components/file-card/components/FileCardRightLayer.tsx @@ -1,12 +1,12 @@ import * as React from "react"; -import { Localization, UPLOADSTATUS } from "../../core"; +import { Localization, UPLOADSTATUS } from "../../../core"; import { Clear, DownloadFile, InfoDisney, PlayIcon, Visibility, -} from "../icons"; +} from "../../icons"; import "./FileMosaicRightLayer.scss"; declare type FileCardRightLayerProps = { darkMode?: boolean; diff --git a/src/files-ui/components/file-card/FileMosaicRightLayer.scss b/src/files-ui/components/file-card/components/FileMosaicRightLayer.scss similarity index 100% rename from src/files-ui/components/file-card/FileMosaicRightLayer.scss rename to src/files-ui/components/file-card/components/FileMosaicRightLayer.scss 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/pages/demo/FileCardDemoPage.jsx b/src/pages/demo/FileCardDemoPage.jsx index 58f248a21ee1a13a656496f9bc15a54f0cc5f594..ba70091ed5de0e6d7c3cc1502a61237b90416d1a 100644 --- a/src/pages/demo/FileCardDemoPage.jsx +++ b/src/pages/demo/FileCardDemoPage.jsx @@ -7,7 +7,6 @@ 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"; @@ -21,6 +20,10 @@ import DemoContainerFileMosaic from "../../components/demo-components/filemosaic 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"; const FileCardDemoPage = (props) => { return ( @@ -28,42 +31,42 @@ const FileCardDemoPage = (props) => { <MainContentContainer> <MainTitle>FileCard</MainTitle> <MainParagraph> - File cards, as well as 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 allow the user to interact with them. + 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-filecard"> <SubTitle content="Basic FileCard" /> <DescParagraph> - The <CodeHighlight>FileCard</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"}> - <DemoFileCardBasic /> - </Stack> - </Paper> + <DemoContainerFileMosaic> + <DemoFileCardBasic /> + </DemoContainerFileMosaic> <CodeJSFileCardBasic /> + <Alert severity="info"> <AlertTitle> FileInputButton </AlertTitle> For completeness, some of these examples include{" "} @@ -106,7 +109,7 @@ const FileCardDemoPage = (props) => { <DemoFileMosaicImagePreview card /> </DemoContainerFileMosaic> - <CodeJSFileMosaicImagePreview card/> + <CodeJSFileMosaicImagePreview card /> <Alert severity="info"> As you can notice, when <CodeHighlight>{`imageUrl`}</CodeHighlight> prop is present, the{" "} @@ -120,12 +123,107 @@ const FileCardDemoPage = (props) => { </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>. + </DescParagraph> + + <DemoContainerFileMosaic> + <DemoFileMosaicValidation card /> + </DemoContainerFileMosaic> + + <CodeJSFileMosaicValidation /> + <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 FileMosaic component. + </DescParagraph> + + <Paper + variant="outlined" + style={{ + padding: "25px", + display: "flex", + alignItems: "center", + justifyContent: "center", + flexDirection: "row", + gap: "7px", + flexWrap: "wrap", + }} + > + <DemoFileMosaicUploadStatus card /> + </Paper> + + <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="dark-mode"> <SubTitle content="Dark mode" /> <DescParagraph> - The <CodeHighlight>FileCard</CodeHighlight> component supports - dark mode by setting the prop{" "} - <TypeHighlight>darkMode</TypeHighlight> to{" "} + The <CodeHighlight>FileCard</CodeHighlight> component supports dark + mode by setting the prop <TypeHighlight>darkMode</TypeHighlight> to{" "} <TypeHighlight>true</TypeHighlight>. </DescParagraph> @@ -144,7 +242,7 @@ const FileCardDemoPage = (props) => { <CodeJSFileMosaicDarkMode card /> </section> - {/* <section id="localization"> + {/* <section id="localization"> <SubTitle content="Localization" /> <DescParagraph> The <CodeHighlight>FileMosaic</CodeHighlight> component has @@ -217,9 +315,7 @@ const FileCardDemoPage = (props) => { </DescParagraph> <ul> <li> - <AnchorToTab href="/api/filecard"> - {"<FileMosaic/>"} - </AnchorToTab> + <AnchorToTab href="/api/filecard">{"<FileMosaic/>"}</AnchorToTab> </li> <li> <AnchorToTab href="/api/fileinputbuttom">