fix(layout): persistent location for the sidebar toggle, closes #3193 (#3269)

This commit is contained in:
Huang Xin
2026-02-11 23:29:26 +08:00
committed by GitHub
parent 411f9e236d
commit a47a480aa2
8 changed files with 38 additions and 26 deletions
@@ -103,7 +103,9 @@ const LibraryHeader: React.FC<LibraryHeaderProps> = ({
style={{
marginTop: appService?.hasSafeAreaInset
? `max(${insets.top}px, ${systemUIVisible ? statusBarHeight : 0}px)`
: '0px',
: appService?.hasTrafficLight
? '-2px'
: '0px',
}}
>
<div className='flex w-full items-center justify-between space-x-6 sm:space-x-12'>
@@ -27,9 +27,10 @@ import DoubleBorder from './DoubleBorder';
interface BooksGridProps {
bookKeys: string[];
onCloseBook: (bookKey: string) => void;
onGoToLibrary: () => void;
}
const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook, onGoToLibrary }) => {
const _ = useTranslation();
const { appService } = useEnv();
const { getConfig, getBookData } = useBookDataStore();
@@ -124,6 +125,7 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
isTopLeft={index === 0}
isHoveredAnim={bookKeys.length > 2}
onCloseBook={onCloseBook}
onGoToLibrary={onGoToLibrary}
gridInsets={gridInsets}
onDropdownOpenChange={(isOpen) => setDropdownOpenBook(isOpen ? bookKey : '')}
/>
@@ -1,6 +1,7 @@
import clsx from 'clsx';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { PiDotsThreeVerticalBold } from 'react-icons/pi';
import { VscLibrary } from 'react-icons/vsc';
import { Insets } from '@/types/misc';
import { useEnv } from '@/context/EnvContext';
@@ -34,6 +35,7 @@ interface HeaderBarProps {
isHoveredAnim: boolean;
gridInsets: Insets;
onCloseBook: (bookKey: string) => void;
onGoToLibrary: () => void;
onDropdownOpenChange?: (isOpen: boolean) => void;
}
@@ -44,6 +46,7 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
isHoveredAnim,
gridInsets,
onCloseBook,
onGoToLibrary,
onDropdownOpenChange,
}) => {
const _ = useTranslation();
@@ -60,6 +63,7 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const view = getView(bookKey);
const iconSize16 = useResponsiveSize(16);
const iconSize18 = useResponsiveSize(18);
const headerRef = useRef<HTMLDivElement>(null);
const windowButtonVisible = appService?.hasWindowBar && !isTrafficLightVisible;
@@ -147,7 +151,7 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
className={clsx(
`header-bar bg-base-100 absolute top-0 z-10 flex h-11 w-full items-center pr-4`,
`shadow-xs transition-[opacity,margin-top] duration-300`,
trafficLightInHeader ? 'pl-20' : 'pl-4',
trafficLightInHeader ? 'pl-20' : isSideBarVisible ? 'ps-4' : 'ps-4 sm:ps-1.5',
appService?.hasRoundedWindow && 'rounded-window-top-right',
!isSideBarVisible && appService?.hasRoundedWindow && 'rounded-window-top-left',
isHoveredAnim && 'hover-bar-anim',
@@ -167,9 +171,18 @@ 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'>
<div className='hidden sm:flex'>
<SidebarToggler bookKey={bookKey} />
</div>
{!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} />
{enableAnnotationQuickActions && (
@@ -224,8 +224,12 @@ const ReaderContent: React.FC<{ ids?: string; settings: SystemSettings }> = ({ i
return (
<div className='reader-content full-height flex'>
<SideBar onGoToLibrary={handleCloseBooksToLibrary} />
<BooksGrid bookKeys={bookKeys} onCloseBook={handleCloseBook} />
<SideBar />
<BooksGrid
bookKeys={bookKeys}
onCloseBook={handleCloseBook}
onGoToLibrary={handleCloseBooksToLibrary}
/>
{isSettingsDialogOpen && <SettingsDialog bookKey={settingsDialogBookKey} />}
<Notebook />
{showDetailsBook && (
@@ -1,7 +1,6 @@
import React from 'react';
import { TbLayoutSidebar, TbLayoutSidebarFilled } from 'react-icons/tb';
import { useReaderStore } from '@/store/readerStore';
import { useSidebarStore } from '@/store/sidebarStore';
import { useTranslation } from '@/hooks/useTranslation';
import Button from '@/components/Button';
@@ -13,7 +12,6 @@ interface SidebarTogglerProps {
const SidebarToggler: React.FC<SidebarTogglerProps> = ({ bookKey }) => {
const _ = useTranslation();
const { sideBarBookKey, isSideBarVisible, setSideBarBookKey, toggleSideBar } = useSidebarStore();
const { setHoveredBookKey } = useReaderStore();
const handleToggleSidebar = () => {
if (sideBarBookKey === bookKey) {
toggleSideBar();
@@ -21,7 +19,6 @@ const SidebarToggler: React.FC<SidebarTogglerProps> = ({ bookKey }) => {
setSideBarBookKey(bookKey);
if (!isSideBarVisible) toggleSideBar();
}
setHoveredBookKey('');
};
return (
<Button
@@ -1,6 +1,5 @@
import clsx from 'clsx';
import React from 'react';
import { GiBookshelf } from 'react-icons/gi';
import { FiSearch } from 'react-icons/fi';
import { MdOutlineMenu, MdOutlinePushPin, MdPushPin } from 'react-icons/md';
import { MdArrowBackIosNew } from 'react-icons/md';
@@ -9,15 +8,16 @@ import { useTrafficLight } from '@/hooks/useTrafficLight';
import { useResponsiveSize } from '@/hooks/useResponsiveSize';
import Dropdown from '@/components/Dropdown';
import BookMenu from './BookMenu';
import SidebarToggler from '../SidebarToggler';
const SidebarHeader: React.FC<{
bookKey: string;
isPinned: boolean;
isSearchBarVisible: boolean;
onGoToLibrary: () => void;
onClose: () => void;
onTogglePin: () => void;
onToggleSearchBar: () => void;
}> = ({ isPinned, isSearchBarVisible, onGoToLibrary, onClose, onTogglePin, onToggleSearchBar }) => {
}> = ({ bookKey, isPinned, isSearchBarVisible, onClose, onTogglePin, onToggleSearchBar }) => {
const _ = useTranslation();
const { isTrafficLightVisible } = useTrafficLight();
const iconSize14 = useResponsiveSize(14);
@@ -40,13 +40,9 @@ const SidebarHeader: React.FC<{
>
<MdArrowBackIosNew size={iconSize22} />
</button>
<button
title={_('Go to Library')}
className='btn btn-ghost hidden h-8 min-h-8 w-8 p-0 sm:flex'
onClick={onGoToLibrary}
>
<GiBookshelf className='fill-base-content' />
</button>
<div className='hidden sm:flex'>
<SidebarToggler bookKey={bookKey} />
</div>
</div>
<div className='flex min-w-24 max-w-32 items-center justify-between sm:size-[70%]'>
<button
@@ -26,9 +26,7 @@ const MAX_SIDEBAR_WIDTH = 0.45;
const VELOCITY_THRESHOLD = 0.5;
const SideBar: React.FC<{
onGoToLibrary: () => void;
}> = ({ onGoToLibrary }) => {
const SideBar = ({}) => {
const _ = useTranslation();
const { appService } = useEnv();
const { updateAppTheme, safeAreaInsets } = useThemeStore();
@@ -302,9 +300,9 @@ const SideBar: React.FC<{
</div>
)}
<SidebarHeader
bookKey={sideBarBookKey!}
isPinned={isSideBarPinned}
isSearchBarVisible={isSearchBarVisible}
onGoToLibrary={onGoToLibrary}
onClose={() => setSideBarVisible(false)}
onTogglePin={handleSideBarTogglePin}
onToggleSearchBar={handleToggleSearchBar}
@@ -18,7 +18,7 @@ export const useTrafficLight = () => {
initializeTrafficLightStore(appService);
initializeTrafficLightListeners();
setTrafficLightVisibility(true);
setTrafficLightVisibility(true, { x: 10, y: 20 });
return () => {
cleanupTrafficLightListeners();
};