From 969af30289864558423992a2747c130bc874b833 Mon Sep 17 00:00:00 2001 From: Saifullah-dev Date: Fri, 13 Sep 2024 23:09:39 +0500 Subject: [PATCH 1/2] Hide folders on overflow --- .../src/FileManager/BreadCrumb/BreadCrumb.jsx | 101 ++++++++++++++++-- .../FileManager/BreadCrumb/BreadCrumb.scss | 6 +- .../src/FileManager/FileList/FileItem.jsx | 2 +- frontend/src/FileManager/FileManager.jsx | 2 + 4 files changed, 100 insertions(+), 11 deletions(-) diff --git a/frontend/src/FileManager/BreadCrumb/BreadCrumb.jsx b/frontend/src/FileManager/BreadCrumb/BreadCrumb.jsx index 812b90a..bc0fb6b 100644 --- a/frontend/src/FileManager/BreadCrumb/BreadCrumb.jsx +++ b/frontend/src/FileManager/BreadCrumb/BreadCrumb.jsx @@ -1,30 +1,117 @@ -import { useEffect, useState } from "react"; -import { MdHome, MdOutlineNavigateNext } from "react-icons/md"; -import "./BreadCrumb.scss"; +import { useEffect, useRef, useState } from "react"; +import { MdHome, MdMoreHoriz, MdOutlineNavigateNext } from "react-icons/md"; import { useFileNavigation } from "../../contexts/FileNavigationContext"; +import "./BreadCrumb.scss"; const BreadCrumb = () => { const [folders, setFolders] = useState([]); + const [hiddenFoldersIndexes, setHiddenFoldersIndexes] = useState([]); const { currentPath, setCurrentPath } = useFileNavigation(); + const breadCrumbRef = useRef(null); + const foldersRef = useRef([]); + + const [hiddenFolders, setHiddenFolders] = useState([]); + const [hiddenFoldersWidth, setHiddenFoldersWidth] = useState([]); + + const getBreadCrumbWidth = () => { + const containerWidth = breadCrumbRef.current.clientWidth; + const containerStyles = getComputedStyle(breadCrumbRef.current); + const paddingLeft = parseFloat(containerStyles.paddingLeft); + const flexGap = parseFloat(containerStyles.gap) * (folders.length - 1); + return containerWidth - (paddingLeft + flexGap); + }; + + const isBreadCrumbOverflowing = () => { + return breadCrumbRef.current.scrollWidth > breadCrumbRef.current.clientWidth; + }; + + // Change Hidden Folders on resize + // useEffect(() => { + // if (folders.length > 0) { + // const homeFolderWidth = foldersRef.current[0].clientWidth; + // const availableSpace = getBreadCrumbWidth() - homeFolderWidth; + + // setHiddenFoldersIndexes(() => { + // return folders.map((_, index) => { + // if (index === 0) return false; + + // const remainingFolders = foldersRef.current.slice(index); + // const remainingFoldersWidth = remainingFolders.reduce((prev, curr) => { + // if (!curr) return prev; + // return prev + curr.clientWidth; + // }, 0); + + // if (remainingFoldersWidth > availableSpace) return true; + // else return false; + // }); + // }); + // } + // }, [folders]); + // useEffect(() => { setFolders(currentPath?.split("/")); + setHiddenFolders([]); + setHiddenFoldersWidth([]); }, [currentPath]); const switchPath = (index) => { if (index < folders.length - 1) { setCurrentPath(() => { - const toSlice = folders.length - (index + 1); - const switchFolders = folders.slice(0, -toSlice); + const toSlice = folders.length + hiddenFolders.length - (index + 1 + hiddenFolders.length); + const copyFolders = folders.slice(1); + const switchFolders = ["", ...hiddenFolders, ...copyFolders].slice(0, -toSlice); + foldersRef.current = foldersRef.current.slice(0, -toSlice); return switchFolders.join("/"); }); } }; + const checkAvailableSpace = () => { + const availableSpace = getBreadCrumbWidth(); + const remainingFoldersWidth = foldersRef.current.reduce((prev, curr) => { + if (!curr) return prev; + return prev + curr.clientWidth; + }, 0); + + return availableSpace - remainingFoldersWidth; + }; + + useEffect(() => { + if (isBreadCrumbOverflowing()) { + const hiddenFolder = folders[1]; + setHiddenFoldersWidth((prev) => [...prev, foldersRef.current[1].clientWidth]); + setHiddenFolders((prev) => [...prev, hiddenFolder]); + setFolders((prev) => prev.filter((_, index) => index !== 1)); + foldersRef.current = foldersRef.current.filter((_, index) => index !== 1); + } + }, [isBreadCrumbOverflowing]); + + // useEffect(() => { + // if (checkAvailableSpace() >= hiddenFoldersWidth.at(-1) && hiddenFolders.length > 0) { + // setFolders((prev) => { + // const prevFolders = prev.slice(hiddenFolders.length); + // const newFolders = ["", hiddenFolders.at(-1), ...prevFolders]; + // return newFolders; + // }); + // setHiddenFolders((prev) => prev.slice(0, -1)); + // setHiddenFoldersWidth((prev) => prev.slice(0, -1)); + // } + // }, [breadCrumbRef.current?.clientWidth, currentPath]); + + console.table(folders, "Folders"); + console.table(hiddenFolders, "Hidden Folders"); + console.table(hiddenFoldersWidth, "Hidden Folders Width"); + return ( -
+
{folders.map((folder, index) => ( - switchPath(index)}> + switchPath(index)} + ref={(el) => (foldersRef.current[index] = el)} + > {index === 0 ? ( <> Home diff --git a/frontend/src/FileManager/BreadCrumb/BreadCrumb.scss b/frontend/src/FileManager/BreadCrumb/BreadCrumb.scss index da2a02f..7a778c5 100644 --- a/frontend/src/FileManager/BreadCrumb/BreadCrumb.scss +++ b/frontend/src/FileManager/BreadCrumb/BreadCrumb.scss @@ -5,8 +5,8 @@ display: flex; gap: 0.5rem; border-bottom: 1px solid #dddddd; - padding: 10px 15px; - overflow-x: scroll; + padding: 10px 0 10PX 15px; + overflow-x: hidden; &::-webkit-scrollbar { height: 3px; @@ -15,4 +15,4 @@ &::-webkit-scrollbar-thumb { background: $primary-color !important; } -} +} \ No newline at end of file diff --git a/frontend/src/FileManager/FileList/FileItem.jsx b/frontend/src/FileManager/FileList/FileItem.jsx index abbe1d1..777ae84 100644 --- a/frontend/src/FileManager/FileList/FileItem.jsx +++ b/frontend/src/FileManager/FileList/FileItem.jsx @@ -83,7 +83,7 @@ const FileItem = ({ const handleFileAccess = () => { setVisible(false); if (file.isDirectory) { - setCurrentPath((prev) => prev + "/" + file.name); + setCurrentPath(file.path); setSelectedFileIndex(null); setSelectedFile(null); } else { diff --git a/frontend/src/FileManager/FileManager.jsx b/frontend/src/FileManager/FileManager.jsx index 87e7c39..1e76913 100644 --- a/frontend/src/FileManager/FileManager.jsx +++ b/frontend/src/FileManager/FileManager.jsx @@ -93,6 +93,8 @@ const FileManager = ({ ); }; +FileManager.displayName = "FileManager"; + FileManager.propTypes = { files: PropTypes.arrayOf( PropTypes.shape({ From c522dd34cf9e0e3b8ccfcf18023ef905cda52560 Mon Sep 17 00:00:00 2001 From: Saifullah-dev Date: Sat, 14 Sep 2024 14:23:16 +0500 Subject: [PATCH 2/2] feat(Breadcrumb): Handle overflow with more button for hidden folders path --- .../src/FileManager/BreadCrumb/BreadCrumb.jsx | 169 +++++++++--------- .../FileManager/BreadCrumb/BreadCrumb.scss | 83 +++++++-- frontend/src/FileManager/FileManager.scss | 13 -- frontend/src/FileManager/Toolbar/Toolbar.scss | 10 +- 4 files changed, 159 insertions(+), 116 deletions(-) diff --git a/frontend/src/FileManager/BreadCrumb/BreadCrumb.jsx b/frontend/src/FileManager/BreadCrumb/BreadCrumb.jsx index bc0fb6b..c3883c9 100644 --- a/frontend/src/FileManager/BreadCrumb/BreadCrumb.jsx +++ b/frontend/src/FileManager/BreadCrumb/BreadCrumb.jsx @@ -1,128 +1,121 @@ import { useEffect, useRef, useState } from "react"; import { MdHome, MdMoreHoriz, MdOutlineNavigateNext } from "react-icons/md"; import { useFileNavigation } from "../../contexts/FileNavigationContext"; +import { useDetectOutsideClick } from "../../hooks/useDetectOutsideClick"; import "./BreadCrumb.scss"; const BreadCrumb = () => { const [folders, setFolders] = useState([]); - const [hiddenFoldersIndexes, setHiddenFoldersIndexes] = useState([]); + const [hiddenFolders, setHiddenFolders] = useState([]); + const [hiddenFoldersWidth, setHiddenFoldersWidth] = useState([]); + const [showHiddenFolders, setShowHiddenFolders] = useState(false); + const { currentPath, setCurrentPath } = useFileNavigation(); const breadCrumbRef = useRef(null); const foldersRef = useRef([]); + const moreBtnRef = useRef(null); + const popoverRef = useDetectOutsideClick(() => { + setShowHiddenFolders(false); + }); - const [hiddenFolders, setHiddenFolders] = useState([]); - const [hiddenFoldersWidth, setHiddenFoldersWidth] = useState([]); + useEffect(() => { + setFolders(() => { + let path = ""; + return currentPath?.split("/").map((item) => { + return { + name: item || "Home", + path: item === "" ? item : (path += `/${item}`), + }; + }); + }); + setHiddenFolders([]); + setHiddenFoldersWidth([]); + }, [currentPath]); + + const switchPath = (path) => { + setCurrentPath(path); + }; const getBreadCrumbWidth = () => { const containerWidth = breadCrumbRef.current.clientWidth; const containerStyles = getComputedStyle(breadCrumbRef.current); const paddingLeft = parseFloat(containerStyles.paddingLeft); - const flexGap = parseFloat(containerStyles.gap) * (folders.length - 1); + const moreBtnGap = hiddenFolders.length > 0 ? 1 : 0; + const flexGap = parseFloat(containerStyles.gap) * (folders.length + moreBtnGap); return containerWidth - (paddingLeft + flexGap); }; - const isBreadCrumbOverflowing = () => { - return breadCrumbRef.current.scrollWidth > breadCrumbRef.current.clientWidth; - }; - - // Change Hidden Folders on resize - // useEffect(() => { - // if (folders.length > 0) { - // const homeFolderWidth = foldersRef.current[0].clientWidth; - // const availableSpace = getBreadCrumbWidth() - homeFolderWidth; - - // setHiddenFoldersIndexes(() => { - // return folders.map((_, index) => { - // if (index === 0) return false; - - // const remainingFolders = foldersRef.current.slice(index); - // const remainingFoldersWidth = remainingFolders.reduce((prev, curr) => { - // if (!curr) return prev; - // return prev + curr.clientWidth; - // }, 0); - - // if (remainingFoldersWidth > availableSpace) return true; - // else return false; - // }); - // }); - // } - // }, [folders]); - // - - useEffect(() => { - setFolders(currentPath?.split("/")); - setHiddenFolders([]); - setHiddenFoldersWidth([]); - }, [currentPath]); - - const switchPath = (index) => { - if (index < folders.length - 1) { - setCurrentPath(() => { - const toSlice = folders.length + hiddenFolders.length - (index + 1 + hiddenFolders.length); - const copyFolders = folders.slice(1); - const switchFolders = ["", ...hiddenFolders, ...copyFolders].slice(0, -toSlice); - foldersRef.current = foldersRef.current.slice(0, -toSlice); - return switchFolders.join("/"); - }); - } - }; - const checkAvailableSpace = () => { const availableSpace = getBreadCrumbWidth(); const remainingFoldersWidth = foldersRef.current.reduce((prev, curr) => { if (!curr) return prev; return prev + curr.clientWidth; }, 0); + const moreBtnWidth = moreBtnRef.current?.clientWidth || 0; + return availableSpace - (remainingFoldersWidth + moreBtnWidth); + }; - return availableSpace - remainingFoldersWidth; + const isBreadCrumbOverflowing = () => { + return breadCrumbRef.current.scrollWidth > breadCrumbRef.current.clientWidth; }; useEffect(() => { if (isBreadCrumbOverflowing()) { const hiddenFolder = folders[1]; - setHiddenFoldersWidth((prev) => [...prev, foldersRef.current[1].clientWidth]); + const hiddenFolderWidth = foldersRef.current[1]?.clientWidth; + setHiddenFoldersWidth((prev) => [...prev, hiddenFolderWidth]); setHiddenFolders((prev) => [...prev, hiddenFolder]); setFolders((prev) => prev.filter((_, index) => index !== 1)); - foldersRef.current = foldersRef.current.filter((_, index) => index !== 1); + } else if (hiddenFolders.length > 0 && checkAvailableSpace() > hiddenFoldersWidth.at(-1)) { + const newFolders = [folders[0], hiddenFolders.at(-1), ...folders.slice(1)]; + setFolders(newFolders); + setHiddenFolders((prev) => prev.slice(0, -1)); + setHiddenFoldersWidth((prev) => prev.slice(0, -1)); } }, [isBreadCrumbOverflowing]); - // useEffect(() => { - // if (checkAvailableSpace() >= hiddenFoldersWidth.at(-1) && hiddenFolders.length > 0) { - // setFolders((prev) => { - // const prevFolders = prev.slice(hiddenFolders.length); - // const newFolders = ["", hiddenFolders.at(-1), ...prevFolders]; - // return newFolders; - // }); - // setHiddenFolders((prev) => prev.slice(0, -1)); - // setHiddenFoldersWidth((prev) => prev.slice(0, -1)); - // } - // }, [breadCrumbRef.current?.clientWidth, currentPath]); - - console.table(folders, "Folders"); - console.table(hiddenFolders, "Hidden Folders"); - console.table(hiddenFoldersWidth, "Hidden Folders Width"); - return ( -
- {folders.map((folder, index) => ( - switchPath(index)} - ref={(el) => (foldersRef.current[index] = el)} - > - {index === 0 ? ( - <> - Home - - ) : ( - <> - {folder} - - )} - - ))} +
+
+ {folders.map((folder, index) => ( +
+ switchPath(folder.path)} + ref={(el) => (foldersRef.current[index] = el)} + > + {index === 0 ? : } + {folder.name} + + {hiddenFolders?.length > 0 && index === 0 && ( + + )} +
+ ))} +
+ + {showHiddenFolders && ( +
    + {hiddenFolders.map((folder, index) => ( +
  • { + switchPath(folder.path); + setShowHiddenFolders(false); + }} + > + {folder.name} +
  • + ))} +
+ )}
); }; diff --git a/frontend/src/FileManager/BreadCrumb/BreadCrumb.scss b/frontend/src/FileManager/BreadCrumb/BreadCrumb.scss index 7a778c5..722ba53 100644 --- a/frontend/src/FileManager/BreadCrumb/BreadCrumb.scss +++ b/frontend/src/FileManager/BreadCrumb/BreadCrumb.scss @@ -1,18 +1,79 @@ @import "../../styles/variables"; -.breadcrumb { - height: calc(5.8% - 21px); - display: flex; - gap: 0.5rem; - border-bottom: 1px solid #dddddd; - padding: 10px 0 10PX 15px; - overflow-x: hidden; +.bread-crumb-container { + position: relative; + + .breadcrumb { + height: calc(5.8% - 21px); + display: flex; + gap: 0.5rem; + border-bottom: 1px solid #dddddd; + padding: 10px 0 10PX 15px; + overflow-x: hidden; + + &::-webkit-scrollbar { + height: 3px; + } + + &::-webkit-scrollbar-thumb { + background: $primary-color !important; + } + + .folder-name { + display: flex; + align-items: center; + gap: 0.25rem; + font-weight: 500; + min-width: fit-content; + + &:hover { + cursor: pointer; + color: $secondary-color; + } + } + + .hidden-folders { + padding: 0 4px; + } + + .folder-name-btn { + background-color: transparent; + border: none; + padding: 0; + + &:hover, + &:focus { + cursor: pointer; + color: $primary-color; + background-color: #dddcdc; + border-radius: 5px; + } + } - &::-webkit-scrollbar { - height: 3px; } +} + +.hidden-folders-container { + position: absolute; + margin: 0; + z-index: 2; + background-color: rgb(99, 99, 99); + color: white; + padding: 4px; + border-radius: 5px; + font-size: .9em; + left: 3rem; + display: flex; + flex-direction: column; + gap: 5px; + + li { + padding: 5px 10px; + border-radius: 4px; - &::-webkit-scrollbar-thumb { - background: $primary-color !important; + &:hover { + cursor: pointer; + background-color: rgb(117, 117, 117); + } } } \ No newline at end of file diff --git a/frontend/src/FileManager/FileManager.scss b/frontend/src/FileManager/FileManager.scss index 556d50e..b1d283d 100644 --- a/frontend/src/FileManager/FileManager.scss +++ b/frontend/src/FileManager/FileManager.scss @@ -89,19 +89,6 @@ svg { padding-left: 0px; border-bottom-right-radius: 8px; } - - .folder-name { - display: flex; - align-items: center; - gap: 0.25rem; - font-weight: 500; - min-width: fit-content; - - &:hover { - cursor: pointer; - color: $secondary-color; - } - } } } diff --git a/frontend/src/FileManager/Toolbar/Toolbar.scss b/frontend/src/FileManager/Toolbar/Toolbar.scss index 8527fa4..93c8fd2 100644 --- a/frontend/src/FileManager/Toolbar/Toolbar.scss +++ b/frontend/src/FileManager/Toolbar/Toolbar.scss @@ -10,7 +10,7 @@ display: flex; justify-content: space-between; - > div { + >div { display: flex; } @@ -39,7 +39,7 @@ display: flex; justify-content: space-between; - > div { + >div { display: flex; position: relative; } @@ -57,6 +57,7 @@ margin: 0; border: 1px solid #c4c4c4; border-radius: 5px; + ul { list-style: none; padding-left: 0; @@ -113,10 +114,11 @@ } .icon-only { - padding: 8px !important; + padding: 0 8px !important; &:focus { background-color: rgb(0 0 0 / 12%); + border-radius: 3px; } } @@ -126,4 +128,4 @@ width: 1px; margin: 0 5px; } -} +} \ No newline at end of file