diff --git a/src/App.tsx b/src/App.tsx index 02c24d5c..bda74e06 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -86,8 +86,9 @@ const MainApp = () => { id="appMainContainer" component="main" sx={{ - flexGrow: 1, minHeight: `calc(100vh - ${bottomBarHeight}px)`, + width: `calc(100vw - (100vw - 100%) - ${navBarWidth}px)`, + minWidth: `calc(100vw - (100vw - 100%) - ${navBarWidth}px)`, maxWidth: `calc(100vw - (100vw - 100%) - ${navBarWidth}px)`, position: 'relative', mb: `${bottomBarHeight}px`, diff --git a/src/components/MangaGrid.tsx b/src/components/MangaGrid.tsx index 7d3baf31..c7f711c0 100644 --- a/src/components/MangaGrid.tsx +++ b/src/components/MangaGrid.tsx @@ -32,7 +32,7 @@ import { AppStorage } from '@/util/AppStorage.ts'; import { MangaCardProps } from '@/components/manga/MangaCard.types.tsx'; import { MangaType } from '@/lib/graphql/generated/graphql.ts'; import { useResizeObserver } from '@/util/useResizeObserver.tsx'; -import { useDebounce } from '@/util/useDebounce.ts'; +import { useNavBarContext } from '@/components/context/NavbarContext.tsx'; const GridContainer = React.forwardRef(({ children, ...props }, ref) => ( @@ -253,12 +253,15 @@ export const MangaGrid: React.FC = ({ }) => { const { t } = useTranslation(); - const gridRef = useRef(null); + const { navBarWidth } = useNavBarContext(); - const [actualDimensions, setDimensions] = useState(document.documentElement.offsetWidth); - const dimensions = useDebounce(actualDimensions, 500); - const [gridItemWidth] = useLocalStorage('ItemWidth', 300); + const gridRef = useRef(null); const gridWrapperRef = useRef(null); + + const [dimensions, setDimensions] = useState( + gridWrapperRef.current?.offsetWidth ?? Math.max(0, document.documentElement.offsetWidth - navBarWidth), + ); + const [gridItemWidth] = useLocalStorage('ItemWidth', 300); const GridItemContainer = useMemo( () => GridItemContainerWithDimension(dimensions, gridItemWidth, gridLayout), [dimensions, gridItemWidth, gridLayout], @@ -300,14 +303,14 @@ export const MangaGrid: React.FC = ({ const gridWidth = gridWrapperRef.current?.offsetWidth; if (!gridWidth) { - return document.documentElement.offsetWidth; + return document.documentElement.offsetWidth - navBarWidth; } return gridWidth; }; setDimensions(getDimensions()); - }, []), + }, [navBarWidth]), ); useResizeObserver( diff --git a/src/components/context/NavbarContext.tsx b/src/components/context/NavbarContext.tsx index a2817179..955a688c 100644 --- a/src/components/context/NavbarContext.tsx +++ b/src/components/context/NavbarContext.tsx @@ -34,6 +34,9 @@ type ContextType = { navBarWidth: number; setNavBarWidth: React.Dispatch>; + readerNavBarWidth: number; + setReaderNavBarWidth: React.Dispatch>; + bottomBarHeight: number; setBottomBarHeight: React.Dispatch>; }; @@ -52,6 +55,8 @@ export const NavBarContext = React.createContext({ setIsCollapsed: (): void => {}, navBarWidth: 0, setNavBarWidth: (): void => {}, + readerNavBarWidth: 0, + setReaderNavBarWidth: (): void => {}, bottomBarHeight: 0, setBottomBarHeight: (): void => {}, }); diff --git a/src/components/navbar/DefaultNavBar.tsx b/src/components/navbar/DefaultNavBar.tsx index bf752343..5b5221fb 100644 --- a/src/components/navbar/DefaultNavBar.tsx +++ b/src/components/navbar/DefaultNavBar.tsx @@ -6,7 +6,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { useCallback, useContext, useEffect, useMemo, useRef } from 'react'; +import { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef } from 'react'; import AppBar from '@mui/material/AppBar'; import Toolbar from '@mui/material/Toolbar'; import Typography from '@mui/material/Typography'; @@ -73,7 +73,7 @@ const navbarItems: Array = [ ]; export function DefaultNavBar() { - const { title, action, override, isCollapsed, setIsCollapsed, setAppBarHeight, navBarWidth } = + const { title, action, override, isCollapsed, setIsCollapsed, setAppBarHeight, navBarWidth, setNavBarWidth } = useContext(NavBarContext); const { pathname } = useLocation(); @@ -98,6 +98,15 @@ export function DefaultNavBar() { ); const NavBarComponent = useMemo(() => (isMobileWidth ? MobileBottomBar : DesktopSideBar), [isMobileWidth]); + useLayoutEffect(() => { + if (!isMobileWidth) { + // do not reset navbar width to prevent grid from jumping due to the changing grid item width + return; + } + + setNavBarWidth(0); + }, [isMobileWidth]); + const navBar = useMemo( () => , [NavBarComponent, visibleNavBarItems], diff --git a/src/components/navbar/NavBarContextProvider.tsx b/src/components/navbar/NavBarContextProvider.tsx index e2692eea..16341a31 100644 --- a/src/components/navbar/NavBarContextProvider.tsx +++ b/src/components/navbar/NavBarContextProvider.tsx @@ -26,6 +26,7 @@ export function NavBarContextProvider({ children }: IProps) { }); const [isCollapsed, setIsCollapsed] = useLocalStorage('NavBar::isCollapsed', false); const [navBarWidth, setNavBarWidth] = useState(0); + const [readerNavBarWidth, setReaderNavBarWidth] = useState(0); const [bottomBarHeight, setBottomBarHeight] = useState(0); const history = useHistory(); @@ -53,6 +54,8 @@ export function NavBarContextProvider({ children }: IProps) { setIsCollapsed, navBarWidth, setNavBarWidth, + readerNavBarWidth, + setReaderNavBarWidth, bottomBarHeight, setBottomBarHeight, }), @@ -70,6 +73,8 @@ export function NavBarContextProvider({ children }: IProps) { setIsCollapsed, navBarWidth, setNavBarWidth, + readerNavBarWidth, + setReaderNavBarWidth, bottomBarHeight, setBottomBarHeight, ], diff --git a/src/components/navbar/ReaderNavBar.tsx b/src/components/navbar/ReaderNavBar.tsx index 347e0b6c..8c442f26 100644 --- a/src/components/navbar/ReaderNavBar.tsx +++ b/src/components/navbar/ReaderNavBar.tsx @@ -12,7 +12,7 @@ import KeyboardArrowLeftIcon from '@mui/icons-material/KeyboardArrowLeft'; import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; import Typography from '@mui/material/Typography'; import { useLocation, useNavigate } from 'react-router-dom'; import Slide from '@mui/material/Slide'; @@ -138,7 +138,7 @@ interface IProps { export function ReaderNavBar(props: IProps) { const { t } = useTranslation(); - const { setNavBarWidth } = useNavBarContext(); + const { setReaderNavBarWidth } = useNavBarContext(); const navigate = useNavigate(); const location = useLocation<{ @@ -155,10 +155,10 @@ export function ReaderNavBar(props: IProps) { return; } - setNavBarWidth(navBarRef.current.offsetWidth); + setReaderNavBarWidth(navBarRef.current.offsetWidth); }, [navBarRef.current]), ); - useEffect(() => () => setNavBarWidth(0), [navBarRef]); + useLayoutEffect(() => () => setReaderNavBarWidth(0), [navBarRef]); const { settings, diff --git a/src/components/navbar/navigation/DesktopSideBar.tsx b/src/components/navbar/navigation/DesktopSideBar.tsx index fc0bcb6e..de188ff3 100644 --- a/src/components/navbar/navigation/DesktopSideBar.tsx +++ b/src/components/navbar/navigation/DesktopSideBar.tsx @@ -17,7 +17,7 @@ import ChevronLeftIcon from '@mui/icons-material/ChevronLeft'; import ChevronRightIcon from '@mui/icons-material/ChevronRight'; import Divider from '@mui/material/Divider'; import { styled, useTheme } from '@mui/material/styles'; -import { useCallback, useEffect, useMemo, useRef } from 'react'; +import { useCallback, useMemo, useRef } from 'react'; import Box from '@mui/material/Box'; import ListItem from '@mui/material/ListItem'; import Tooltip from '@mui/material/Tooltip'; @@ -92,7 +92,6 @@ export const DesktopSideBar = ({ navBarItems }: { navBarItems: NavbarItem[] }) = ref, useCallback(() => setNavBarWidth(ref.current?.clientWidth ?? 0), [ref.current]), ); - useEffect(() => () => setNavBarWidth(0), []); return ( diff --git a/src/components/navbar/navigation/MobileBottomBar.tsx b/src/components/navbar/navigation/MobileBottomBar.tsx index 4c9e388a..737e0de6 100644 --- a/src/components/navbar/navigation/MobileBottomBar.tsx +++ b/src/components/navbar/navigation/MobileBottomBar.tsx @@ -10,7 +10,7 @@ import BottomNavigation from '@mui/material/BottomNavigation'; import BottomNavigationAction from '@mui/material/BottomNavigationAction'; import { useTranslation } from 'react-i18next'; import Paper from '@mui/material/Paper'; -import { useCallback, useEffect, useRef, useState } from 'react'; +import { useCallback, useLayoutEffect, useRef, useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; import { NavbarItem } from '@/typings.ts'; import { useResizeObserver } from '@/util/useResizeObserver.tsx'; @@ -27,7 +27,7 @@ export const MobileBottomBar = ({ navBarItems }: { navBarItems: NavbarItem[] }) ref, useCallback(() => setBottomBarHeight(ref.current?.clientHeight ?? 0), [ref.current]), ); - useEffect(() => () => setBottomBarHeight(0), []); + useLayoutEffect(() => () => setBottomBarHeight(0), []); const [selectedNavBarItem, setSelectedNavBarItem] = useState( navBarItems.find((navBarItem) => navBarItem.path === location.pathname)?.path, diff --git a/src/components/tabs/TabsMenu.tsx b/src/components/tabs/TabsMenu.tsx index 86e51cf7..63c8eddb 100644 --- a/src/components/tabs/TabsMenu.tsx +++ b/src/components/tabs/TabsMenu.tsx @@ -8,8 +8,7 @@ import Tabs, { TabsProps } from '@mui/material/Tabs'; import { styled } from '@mui/material/styles'; -import { ForwardedRef, forwardRef, useCallback, useImperativeHandle, useRef, useState } from 'react'; -import { useResizeObserver } from '@/util/useResizeObserver.tsx'; +import { ForwardedRef, forwardRef } from 'react'; import { useNavBarContext } from '@/components/context/NavbarContext.tsx'; const StyledTabsMenu = styled(Tabs)(({ theme }) => ({ @@ -27,33 +26,21 @@ const StyledTabsMenu = styled(Tabs)(({ theme }) => ({ export const TabsMenu = forwardRef( ( - { children, tabsCount, ...props }: TabsProps & { tabsCount: number }, + { children, tabsCount, sx, ...props }: TabsProps & { tabsCount: number }, ref: ForwardedRef, ) => { const { appBarHeight } = useNavBarContext(); - const tabsMenuRef = useRef(null); - useImperativeHandle(ref, () => tabsMenuRef.current!); - const [width, setWidth] = useState(); - useResizeObserver( - tabsMenuRef, - useCallback(() => setWidth(tabsMenuRef.current?.clientWidth), [tabsMenuRef.current]), - ); - - // Visual Hack: 160px is min-width for viewport width of >600 - const scrollableTabs = !width ? false : width < tabsCount * 160; - return ( {children} diff --git a/src/screens/Browse.tsx b/src/screens/Browse.tsx index 8a1b6a64..7d458c73 100644 --- a/src/screens/Browse.tsx +++ b/src/screens/Browse.tsx @@ -37,7 +37,13 @@ export function Browse() { return ( - setTabNum(newTab)}> + setTabNum(newTab)} + > diff --git a/src/screens/Reader.tsx b/src/screens/Reader.tsx index a204eb42..233b2746 100644 --- a/src/screens/Reader.tsx +++ b/src/screens/Reader.tsx @@ -194,7 +194,7 @@ export function Reader() { const isLastPage = curPage === chapter.pageCount - 1; const curPageDebounced = useDebounce(curPage, isLastPage ? 0 : 1000); const [pageToScrollTo, setPageToScrollTo] = useState(undefined); - const { setOverride, setTitle, navBarWidth: currentNavBarWidth } = useContext(NavBarContext); + const { setOverride, setTitle, readerNavBarWidth: currentNavBarWidth } = useContext(NavBarContext); const [retrievingNextChapter, setRetrievingNextChapter] = useState(false); const { data: mangaChaptersData,