fix(layout): fix parallel read layout on smaller screens (#3549)
This commit is contained in:
@@ -121,12 +121,13 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook, onGoToLibr
|
||||
{isBookmarked && !hoveredBookKey && <Ribbon width={`${horizontalGapPercent}%`} />}
|
||||
<HeaderBar
|
||||
bookKey={bookKey}
|
||||
gridInsets={gridInsets}
|
||||
screenInsets={screenInsets}
|
||||
bookTitle={book.title}
|
||||
isTopLeft={index === 0}
|
||||
isHoveredAnim={bookKeys.length > 2}
|
||||
onCloseBook={onCloseBook}
|
||||
onGoToLibrary={onGoToLibrary}
|
||||
gridInsets={gridInsets}
|
||||
onDropdownOpenChange={(isOpen) => setDropdownOpenBook(isOpen ? bookKey : '')}
|
||||
/>
|
||||
<PageNavigationButtons
|
||||
|
||||
@@ -34,6 +34,7 @@ interface HeaderBarProps {
|
||||
isTopLeft: boolean;
|
||||
isHoveredAnim: boolean;
|
||||
gridInsets: Insets;
|
||||
screenInsets: Insets;
|
||||
onCloseBook: (bookKey: string) => void;
|
||||
onGoToLibrary: () => void;
|
||||
onDropdownOpenChange?: (isOpen: boolean) => void;
|
||||
@@ -45,6 +46,7 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
|
||||
isTopLeft,
|
||||
isHoveredAnim,
|
||||
gridInsets,
|
||||
screenInsets,
|
||||
onCloseBook,
|
||||
onGoToLibrary,
|
||||
onDropdownOpenChange,
|
||||
@@ -61,6 +63,7 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
|
||||
const viewSettings = getViewSettings(bookKey);
|
||||
|
||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||
const [headerWidth, setHeaderWidth] = useState(0);
|
||||
const view = getView(bookKey);
|
||||
const iconSize16 = useResponsiveSize(16);
|
||||
const iconSize18 = useResponsiveSize(18);
|
||||
@@ -107,6 +110,16 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [appService, hoveredBookKey]);
|
||||
|
||||
useEffect(() => {
|
||||
const header = headerRef.current;
|
||||
if (!header) return;
|
||||
const observer = new ResizeObserver(([entry]) => {
|
||||
if (entry) setHeaderWidth(entry.contentRect.width);
|
||||
});
|
||||
observer.observe(header);
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
|
||||
// Check if mouse is outside header area to avoid false positive event of MouseLeave when clicking inside header on Windows
|
||||
const isMouseOutsideHeader = useCallback((clientX: number, clientY: number) => {
|
||||
if (!headerRef.current) return true;
|
||||
@@ -117,15 +130,20 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
|
||||
);
|
||||
}, []);
|
||||
|
||||
const isHeaderCompact = headerWidth > 0 && headerWidth < 350;
|
||||
const insets = window.innerWidth < 640 ? screenInsets : gridInsets;
|
||||
const isHeaderVisible = hoveredBookKey === bookKey || isDropdownOpen;
|
||||
const trafficLightInHeader =
|
||||
appService?.hasTrafficLight && !trafficLightInFullscreen && !isSideBarVisible && isTopLeft;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx('bg-base-100 absolute top-0 w-full')}
|
||||
className={clsx(
|
||||
'bg-base-100 left-0 top-0 w-full',
|
||||
window.innerWidth < 640 ? 'fixed z-20' : 'absolute',
|
||||
)}
|
||||
style={{
|
||||
paddingTop: appService?.hasSafeAreaInset ? `${gridInsets.top}px` : '0px',
|
||||
paddingTop: appService?.hasSafeAreaInset ? `${insets.top}px` : '0px',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
@@ -142,7 +160,7 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
|
||||
isHeaderVisible ? 'visible' : 'hidden',
|
||||
)}
|
||||
style={{
|
||||
height: systemUIVisible ? `${Math.max(gridInsets.top, statusBarHeight)}px` : '0px',
|
||||
height: systemUIVisible ? `${Math.max(insets.top, statusBarHeight)}px` : '0px',
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
@@ -161,8 +179,8 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
|
||||
)}
|
||||
style={{
|
||||
marginTop: systemUIVisible
|
||||
? `${Math.max(gridInsets.top, statusBarHeight)}px`
|
||||
: `${gridInsets.top}px`,
|
||||
? `${Math.max(insets.top, statusBarHeight)}px`
|
||||
: `${insets.top}px`,
|
||||
}}
|
||||
onFocus={() => !appService?.isMobile && setHoveredBookKey(bookKey)}
|
||||
onMouseLeave={(e) => {
|
||||
@@ -171,21 +189,26 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className='header-tools-start bg-base-100 sidebar-bookmark-toggler z-20 flex h-full items-center gap-x-4 pe-2 max-[350px]:gap-x-2'>
|
||||
{!isSideBarVisible && (
|
||||
<div className='hidden sm:flex'>
|
||||
<SidebarToggler bookKey={bookKey} />
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
title={_('Go to Library')}
|
||||
className='btn btn-ghost hidden h-8 min-h-8 w-8 p-0 sm:flex'
|
||||
onClick={onGoToLibrary}
|
||||
<div className='header-tools-start bg-base-100 sidebar-bookmark-toggler z-20 flex h-full min-w-0 items-center gap-x-4 pe-2 max-[350px]:gap-x-2'>
|
||||
<div
|
||||
className='flex min-w-0 items-center gap-x-4 overflow-x-auto max-[350px]:gap-x-2'
|
||||
style={{ scrollbarWidth: 'none', msOverflowStyle: 'none' }}
|
||||
>
|
||||
<VscLibrary size={iconSize18} className='fill-base-content' />
|
||||
</button>
|
||||
<BookmarkToggler bookKey={bookKey} />
|
||||
<TranslationToggler bookKey={bookKey} />
|
||||
{!isSideBarVisible && (
|
||||
<div className='hidden sm:flex'>
|
||||
<SidebarToggler bookKey={bookKey} />
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
title={_('Go to Library')}
|
||||
className='btn btn-ghost hidden h-8 min-h-8 w-8 p-0 sm:flex'
|
||||
onClick={onGoToLibrary}
|
||||
>
|
||||
<VscLibrary size={iconSize18} className='fill-base-content' />
|
||||
</button>
|
||||
<BookmarkToggler bookKey={bookKey} />
|
||||
<TranslationToggler bookKey={bookKey} />
|
||||
</div>
|
||||
{enableAnnotationQuickActions && (
|
||||
<Dropdown
|
||||
label={
|
||||
@@ -229,6 +252,7 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
|
||||
className={clsx(
|
||||
'header-title z-15 bg-base-100 pointer-events-none hidden flex-1 items-center justify-center sm:flex',
|
||||
!windowButtonVisible && 'absolute inset-0',
|
||||
isHeaderCompact && '!hidden',
|
||||
)}
|
||||
>
|
||||
<div
|
||||
@@ -242,8 +266,8 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='header-tools-end bg-base-100 z-20 ms-auto flex h-full items-center gap-x-4 ps-2 max-[350px]:gap-x-2'>
|
||||
<SettingsToggler bookKey={bookKey} />
|
||||
<div className='header-tools-end bg-base-100 z-20 ms-auto flex h-full min-w-max items-center gap-x-4 ps-2 max-[350px]:gap-x-2'>
|
||||
{!isHeaderCompact && <SettingsToggler bookKey={bookKey} />}
|
||||
<NotebookToggler bookKey={bookKey} />
|
||||
<Dropdown
|
||||
label={_('View Options')}
|
||||
@@ -254,9 +278,8 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
|
||||
>
|
||||
<ViewMenu bookKey={bookKey} />
|
||||
</Dropdown>
|
||||
|
||||
<WindowButtons
|
||||
className='window-buttons flex h-full items-center'
|
||||
className='window-buttons flex items-center'
|
||||
headerRef={headerRef}
|
||||
showMinimize={bookKeys.length == 1 && windowButtonVisible}
|
||||
showMaximize={bookKeys.length == 1 && windowButtonVisible}
|
||||
|
||||
@@ -88,7 +88,9 @@ const SectionInfo: React.FC<SectionInfoProps> = ({
|
||||
className={clsx(
|
||||
'text-center',
|
||||
isVertical ? '' : 'line-clamp-1',
|
||||
!isVertical && hoveredBookKey == bookKey && 'hidden',
|
||||
!isVertical &&
|
||||
(hoveredBookKey == bookKey || (hoveredBookKey && appService?.isMobile)) &&
|
||||
'hidden',
|
||||
)}
|
||||
>
|
||||
{section || ''}
|
||||
|
||||
@@ -47,10 +47,10 @@ const StatusInfo: React.FC<StatusInfoProps> = ({
|
||||
opacity={isEink ? 1.0 : 0.75}
|
||||
/>
|
||||
<rect
|
||||
x='1.5'
|
||||
y='1.5'
|
||||
width={(batteryLevel / 100) * 19}
|
||||
height='9'
|
||||
x='0.5'
|
||||
y='0.5'
|
||||
width={(batteryLevel / 100) * 21}
|
||||
height='11'
|
||||
rx='1'
|
||||
fill='currentColor'
|
||||
opacity={isEink ? 1.0 : 0.3}
|
||||
|
||||
@@ -67,8 +67,12 @@ const DesktopFooterBar: React.FC<FooterBarChildProps> = ({
|
||||
|
||||
return (
|
||||
<div
|
||||
className='hidden h-8 w-full items-center gap-x-4 px-4 sm:flex'
|
||||
style={{ bottom: isMobile ? `${gridInsets.bottom * 0.33}px` : '0px' }}
|
||||
className='hidden h-8 w-full items-center gap-x-4 overflow-x-auto px-4 sm:flex'
|
||||
style={{
|
||||
bottom: isMobile ? `${gridInsets.bottom * 0.33}px` : '0px',
|
||||
scrollbarWidth: 'none',
|
||||
msOverflowStyle: 'none',
|
||||
}}
|
||||
>
|
||||
{!viewSettings?.showPaginationButtons && (
|
||||
<Button
|
||||
@@ -112,7 +116,7 @@ const DesktopFooterBar: React.FC<FooterBarChildProps> = ({
|
||||
<span
|
||||
title={_('Reading Progress')}
|
||||
aria-label={`${_('Reading Progress')}: ${Math.round(progressFraction * 100)}%`}
|
||||
className='mx-2 text-center text-sm'
|
||||
className='mx-2 text-nowrap text-center text-sm'
|
||||
>
|
||||
<span aria-hidden='true'>{progressInfo}</span>
|
||||
</span>
|
||||
|
||||
@@ -204,7 +204,7 @@ const FooterBar: React.FC<FooterBarProps> = ({
|
||||
(bookData?.isFixedLayout && viewSettings?.zoomLevel && viewSettings.zoomLevel > 100);
|
||||
|
||||
const containerClasses = clsx(
|
||||
'footer-bar shadow-xs bottom-0 z-10 flex w-full flex-col sm:h-[52px]',
|
||||
'footer-bar shadow-xs bottom-0 left-0 z-10 flex w-full flex-col sm:h-[52px]',
|
||||
'sm:bg-base-100 border-base-300/50 border-t sm:border-none',
|
||||
'transition-[opacity,transform] duration-300',
|
||||
window.innerWidth < 640 ? 'fixed' : 'absolute',
|
||||
|
||||
@@ -124,15 +124,8 @@ const BookMenu: React.FC<BookMenuProps> = ({ menuClassName, setIsDropdownOpen })
|
||||
<MenuItem
|
||||
label={_('Parallel Read')}
|
||||
buttonClass={bookKeys.length > 1 ? 'lg:tooltip lg:tooltip-bottom' : ''}
|
||||
tooltip={parallelViews.length > 0 ? _('Disable') : bookKeys.length > 1 ? _('Enable') : ''}
|
||||
tooltip={parallelViews.length > 0 ? _('Disable') : _('Enable')}
|
||||
Icon={parallelViews.length > 0 && bookKeys.length > 1 ? MdCheck : undefined}
|
||||
onClick={
|
||||
parallelViews.length > 0
|
||||
? handleUnsetParallel
|
||||
: bookKeys.length > 1
|
||||
? handleSetParallel
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<ul className='max-h-60 overflow-y-auto'>
|
||||
{getVisibleLibrary()
|
||||
|
||||
Reference in New Issue
Block a user