diff --git a/apps/readest-app/src/app/library/components/Header.tsx b/apps/readest-app/src/app/library/components/LibraryHeader.tsx similarity index 77% rename from apps/readest-app/src/app/library/components/Header.tsx rename to apps/readest-app/src/app/library/components/LibraryHeader.tsx index 3a7ef4c3..45a5851f 100644 --- a/apps/readest-app/src/app/library/components/Header.tsx +++ b/apps/readest-app/src/app/library/components/LibraryHeader.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useRef } from 'react'; import { FaSearch } from 'react-icons/fa'; import { PiPlus } from 'react-icons/pi'; import { PiSelectionAllDuotone } from 'react-icons/pi'; @@ -16,6 +16,7 @@ const LibraryHeader: React.FC = ({ onImportBooks, onToggleSelectMode, }) => { + const headerRef = useRef(null); useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.ctrlKey || e.shiftKey) { @@ -28,9 +29,24 @@ const LibraryHeader: React.FC = ({ }; }, [onToggleSelectMode]); + const handleMinimize = async () => { + const { getCurrentWindow } = await import('@tauri-apps/api/window'); + getCurrentWindow().minimize(); + }; + + const handleToggleMaximize = async () => { + const { getCurrentWindow } = await import('@tauri-apps/api/window'); + getCurrentWindow().toggleMaximize(); + }; + + const handleClose = async () => { + const { getCurrentWindow } = await import('@tauri-apps/api/window'); + getCurrentWindow().close(); + }; + return (
@@ -68,7 +84,12 @@ const LibraryHeader: React.FC = ({
- + ); diff --git a/apps/readest-app/src/app/library/page.tsx b/apps/readest-app/src/app/library/page.tsx index 488e3f85..2ecf522a 100644 --- a/apps/readest-app/src/app/library/page.tsx +++ b/apps/readest-app/src/app/library/page.tsx @@ -7,7 +7,7 @@ import { useEnv } from '@/context/EnvContext'; import { useReaderStore } from '@/store/readerStore'; import Spinner from '@/components/Spinner'; -import LibraryHeader from '@/app/library/components/Header'; +import LibraryHeader from '@/app/library/components/LibraryHeader'; import Bookshelf from '@/app/library/components/Bookshelf'; const LibraryPage = () => { diff --git a/apps/readest-app/src/app/reader/components/HeaderBar.tsx b/apps/readest-app/src/app/reader/components/HeaderBar.tsx index e748f80f..c5e2659d 100644 --- a/apps/readest-app/src/app/reader/components/HeaderBar.tsx +++ b/apps/readest-app/src/app/reader/components/HeaderBar.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useRef } from 'react'; import clsx from 'clsx'; import { VscLayoutSidebarLeft, VscLayoutSidebarLeftOff } from 'react-icons/vsc'; @@ -14,6 +14,7 @@ interface HeaderBarProps { setSideBarVisibility: (visibility: boolean) => void; setSideBarBookKey: (key: string) => void; setHoveredBookKey: (key: string) => void; + onCloseBook: (bookKey: string) => void; } const HeaderBar: React.FC = ({ @@ -26,7 +27,9 @@ const HeaderBar: React.FC = ({ setSideBarVisibility, setSideBarBookKey, setHoveredBookKey, + onCloseBook, }) => { + const headerRef = useRef(null); const toggleSideBar = () => { if (!isSideBarVisible) { setSideBarVisibility(true); @@ -38,7 +41,7 @@ const HeaderBar: React.FC = ({ return (
= ({ onMouseEnter={() => setHoveredBookKey(bookKey)} onMouseLeave={() => setHoveredBookKey('')} > -
-

- {bookTitle} -

-
-
-
-
- + +
+

{bookTitle}

+
+ +
+ onCloseBook(bookKey)} + />
); diff --git a/apps/readest-app/src/app/reader/components/ReaderContent.tsx b/apps/readest-app/src/app/reader/components/ReaderContent.tsx index 6c4a682e..6a6f7afb 100644 --- a/apps/readest-app/src/app/reader/components/ReaderContent.tsx +++ b/apps/readest-app/src/app/reader/components/ReaderContent.tsx @@ -43,7 +43,7 @@ const ReaderContent = () => { (e.altKey && e.key === 'Tab') || (e.metaKey && e.key === 'Tab') ) { - setSideBarBookKey((prevKey) => { + setSideBarBookKey((prevKey: string) => { const index = ids.findIndex((id) => prevKey.startsWith(id)); const nextIndex = (index + 1) % ids.length; return getKey(ids[nextIndex]!, nextIndex); @@ -97,17 +97,43 @@ const ReaderContent = () => { settings.globalReadSettings.isSideBarPinned = !isSideBarPinned; }; - const handleCloseBook = () => { - bookStates.forEach((bookState) => { - const { book, config, isPrimary } = bookState; - if (isPrimary && book && config) { - saveConfig(envConfig, book, config); - } + const closeBook = (bookKey: string) => { + const bookState = books[bookKey]; + if (!bookState) return; + const { book, config, isPrimary } = bookState; + if (isPrimary && book && config) { + saveConfig(envConfig, book, config); + } + }; + + const handleCloseBooks = () => { + ids.forEach((id, index) => { + const key = getKey(id, index); + closeBook(key); }); saveSettings(envConfig, settings); router.back(); }; + const handleCloseBook = (bookKey: string) => { + closeBook(bookKey); + setSideBarBookKey((prevKey: string) => { + if (prevKey === bookKey) { + const index = ids.findIndex((id) => prevKey.startsWith(id)); + const nextIndex = (index + 1) % ids.length; + return getKey(ids[nextIndex]!, nextIndex); + } + return prevKey; + }); + const newIds = ids.filter((id, index) => getKey(id, index) !== bookKey); + if (newIds.length > 0) { + router.replace(`/reader?ids=${newIds.join(',')}`); + } else { + saveSettings(envConfig, settings); + router.back(); + } + }; + const getGridTemplate = () => { const count = ids.length; const aspectRatio = window.innerWidth / window.innerHeight; @@ -157,7 +183,7 @@ const ReaderContent = () => { isPinned={isSideBarPinned} onResize={handleSideBarResize} onTogglePin={handleSideBarTogglePin} - onGoToLibrary={handleCloseBook} + onGoToLibrary={handleCloseBooks} onSetVisibility={(visibility: boolean) => setSideBarVisibility(visibility)} /> @@ -186,6 +212,7 @@ const ReaderContent = () => { setSideBarVisibility={setSideBarVisibility} setSideBarBookKey={setSideBarBookKey} setHoveredBookKey={setHoveredBookKey} + onCloseBook={handleCloseBook} /> { - useEffect(() => { - const handleMouseDown = async (e: MouseEvent) => { - const { getCurrentWindow } = await import('@tauri-apps/api/window'); - if (e.buttons === 1) { - if (e.detail === 2) { - getCurrentWindow().toggleMaximize(); - } else { - getCurrentWindow().startDragging(); - } +interface WindowButtonsProps { + className?: string; + headerRef: React.RefObject; + showMinimize?: boolean; + showMaximize?: boolean; + showClose?: boolean; + onMinimize?: () => void; + onToggleMaximize?: () => void; + onClose?: () => void; +} + +const WindowButtons: React.FC = ({ + className, + headerRef, + showMinimize = true, + showMaximize = true, + showClose = true, + onMinimize, + onToggleMaximize, + onClose, +}) => { + const parentRef = useRef(null); + + const handleMouseDown = async (e: MouseEvent) => { + const { getCurrentWindow } = await import('@tauri-apps/api/window'); + if (e.buttons === 1) { + if (e.detail === 2) { + getCurrentWindow().toggleMaximize(); + } else { + getCurrentWindow().startDragging(); } - }; + } + }; + + useEffect(() => { + const headerElement = headerRef.current; + headerElement?.addEventListener('mousedown', handleMouseDown); - const header = document.getElementById('titlebar'); - header?.addEventListener('mousedown', handleMouseDown); return () => { - header?.removeEventListener('mousedown', handleMouseDown); + headerElement?.removeEventListener('mousedown', handleMouseDown); }; }, []); const handleMinimize = async () => { - const { getCurrentWindow } = await import('@tauri-apps/api/window'); - getCurrentWindow().minimize(); + if (onMinimize) { + onMinimize(); + } }; const handleMaximize = async () => { - const { getCurrentWindow } = await import('@tauri-apps/api/window'); - getCurrentWindow().toggleMaximize(); + if (onToggleMaximize) { + onToggleMaximize(); + } }; const handleClose = async () => { - const { getCurrentWindow } = await import('@tauri-apps/api/window'); - getCurrentWindow().close(); + if (onClose) { + onClose(); + } }; return ( -
- +
+ {showMinimize && ( + + )} - + {showMaximize && ( + + )} - + {showClose && ( + + )}
); };