Add bookmark toggler and ribbon

This commit is contained in:
chrox
2024-11-04 15:36:17 +01:00
parent 1cbceeee49
commit ff71497209
7 changed files with 162 additions and 51 deletions
@@ -1,13 +1,15 @@
import React from 'react';
import { BookState, useReaderStore } from '@/store/readerStore';
import HeaderBar from './HeaderBar';
import FoliateViewer from './FoliateViewer';
import PageInfo from './PageInfo';
import FooterBar from './FooterBar';
import SectionInfo from './SectionInfo';
import getGridTemplate from '@/utils/grid';
import SettingsDialog from './settings/SettingsDialog';
import FoliateViewer from './FoliateViewer';
import getGridTemplate from '@/utils/grid';
import SectionInfo from './SectionInfo';
import HeaderBar from './HeaderBar';
import FooterBar from './FooterBar';
import PageInfo from './PageInfo';
import Ribbon from './Ribbon';
interface BookGridProps {
bookKeys: string[];
@@ -16,7 +18,7 @@ interface BookGridProps {
}
const BookGrid: React.FC<BookGridProps> = ({ bookKeys, bookStates, onCloseBook }) => {
const { isSideBarPinned, isSideBarVisible, sideBarWidth } = useReaderStore();
const { isSideBarPinned, isSideBarVisible, sideBarWidth, bookmarkRibbons } = useReaderStore();
const { isFontLayoutSettingsDialogOpen, setFontLayoutSettingsDialogOpen } = useReaderStore();
const gridWidth = isSideBarPinned && isSideBarVisible ? `calc(100% - ${sideBarWidth})` : '100%';
const gridTemplate = getGridTemplate(bookKeys.length, window.innerWidth / window.innerHeight);
@@ -32,6 +34,7 @@ const BookGrid: React.FC<BookGridProps> = ({ bookKeys, bookStates, onCloseBook }
>
{bookStates.map((bookState, index) => {
const bookKey = bookKeys[index]!;
const isBookmarked = bookmarkRibbons[bookKey];
const { book, config, bookDoc } = bookState;
if (!book || !config || !bookDoc) return null;
const { section, pageinfo, progress, chapter } = config;
@@ -39,6 +42,7 @@ const BookGrid: React.FC<BookGridProps> = ({ bookKeys, bookStates, onCloseBook }
return (
<div key={bookKey} className='relative h-full w-full overflow-hidden'>
{isBookmarked && <Ribbon width={marginGap} />}
<HeaderBar
bookKey={bookKey}
bookTitle={book.title}
@@ -0,0 +1,63 @@
import React, { useEffect, useState } from 'react';
import { MdOutlineBookmarkAdd, MdOutlineBookmark } from 'react-icons/md';
import * as CFI from 'foliate-js/epubcfi.js';
import { useReaderStore } from '@/store/readerStore';
import { BookNote } from '@/types/book';
interface BookmarkTogglerProps {
bookKey: string;
}
const BookmarkToggler: React.FC<BookmarkTogglerProps> = ({ bookKey }) => {
const { books, updateBookmarks, setBookmarkRibbonVisibility } = useReaderStore();
const bookState = books[bookKey]!;
const config = bookState.config!;
const [isBookmarked, setIsBookmarked] = useState(false);
const toggleBookmark = () => {
const { location: cfi, bookmarks = [] } = config;
if (!cfi) return;
if (!isBookmarked) {
setIsBookmarked(true);
const bookmark: BookNote = {
type: 'bookmark',
cfi,
note: '',
created: Date.now(),
};
bookmarks.push(bookmark);
updateBookmarks(bookKey, bookmarks);
} else {
setIsBookmarked(false);
const start = CFI.collapse(cfi);
const end = CFI.collapse(cfi, true);
updateBookmarks(
bookKey,
bookmarks.filter((item) => CFI.compare(start, item.cfi) * CFI.compare(end, item.cfi) > 0),
);
}
};
useEffect(() => {
const { location: cfi, bookmarks = [] } = config;
if (!cfi) return;
const start = CFI.collapse(cfi);
const end = CFI.collapse(cfi, true);
const locationBookmarked = bookmarks.some(
(item) => CFI.compare(start, item.cfi) * CFI.compare(end, item.cfi) <= 0,
);
setIsBookmarked(locationBookmarked);
setBookmarkRibbonVisibility(bookKey, locationBookmarked);
}, [config]);
return (
<button onClick={toggleBookmark} className='p-2'>
{isBookmarked ? <MdOutlineBookmark size={20} /> : <MdOutlineBookmarkAdd size={20} />}
</button>
);
};
export default BookmarkToggler;
@@ -2,11 +2,12 @@ import clsx from 'clsx';
import React, { useRef, useState } from 'react';
import { PiDotsThreeVerticalBold } from 'react-icons/pi';
import { useReaderStore } from '@/store/readerStore';
import WindowButtons from '@/components/WindowButtons';
import Dropdown from '@/components/Dropdown';
import SidebarToggler from './SidebarToggler';
import BookmarkToggler from './BookmarkToggler';
import ViewMenu from './ViewMenu';
import { useReaderStore } from '@/store/readerStore';
interface HeaderBarProps {
bookKey: string;
@@ -25,17 +26,7 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
}) => {
const headerRef = useRef<HTMLDivElement>(null);
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const { hoveredBookKey, setHoveredBookKey, sideBarBookKey, setSideBarBookKey } = useReaderStore();
const { isSideBarVisible, toggleSideBar } = useReaderStore();
const toggleSidebarForBook = (bookKey: string) => {
if (sideBarBookKey === bookKey) {
toggleSideBar();
} else {
setSideBarBookKey(bookKey);
if (!isSideBarVisible) toggleSideBar();
}
};
const { hoveredBookKey, isSideBarVisible, setHoveredBookKey } = useReaderStore();
const handleToggleDropdown = (isOpen: boolean) => {
setIsDropdownOpen(isOpen);
@@ -56,12 +47,9 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
onMouseEnter={() => setHoveredBookKey(bookKey)}
onMouseLeave={() => setHoveredBookKey('')}
>
<div className='sidebar-toggler mr-auto flex h-full items-center'>
<SidebarToggler
isSidebarVisible={isSideBarVisible}
isCurrentBook={sideBarBookKey === bookKey}
toggleSidebar={() => toggleSidebarForBook(bookKey)}
></SidebarToggler>
<div className='sidebar-bookmark-toggler mr-auto flex h-full items-center'>
<SidebarToggler bookKey={bookKey} />
<BookmarkToggler bookKey={bookKey} />
</div>
<div className='header-title flex flex-1 items-center justify-center'>
@@ -0,0 +1,25 @@
import React from 'react';
interface RibbonProps {
width: string;
}
const Ribbon: React.FC<RibbonProps> = ({ width }) => {
return (
<div className='absolute inset-0 z-10 flex h-11 justify-center' style={{ width }}>
<svg
stroke='currentColor'
fill='currentColor'
strokeWidth='0'
version='1'
viewBox='0 0 20 45'
enableBackground='new 0 0 20 45'
xmlns='http://www.w3.org/2000/svg'
>
<path fill='#F44336' d='M 20 45 L 10 35 L 0 45 L 0 0 L 20 0'></path>
</svg>
</div>
);
};
export default Ribbon;
@@ -1,24 +1,31 @@
import React from 'react';
import { VscLayoutSidebarLeft, VscLayoutSidebarLeftOff } from 'react-icons/vsc';
import { useReaderStore } from '@/store/readerStore';
interface SidebarTogglerProps {
isSidebarVisible: boolean;
isCurrentBook: boolean;
toggleSidebar: () => void;
bookKey: string;
}
const SidebarToggler: React.FC<SidebarTogglerProps> = ({
isSidebarVisible,
isCurrentBook,
toggleSidebar,
}) => (
<button onClick={toggleSidebar} className='p-2'>
{isCurrentBook && isSidebarVisible ? (
<VscLayoutSidebarLeft size={16} />
) : (
<VscLayoutSidebarLeftOff size={16} />
)}
</button>
);
const SidebarToggler: React.FC<SidebarTogglerProps> = ({ bookKey }) => {
const { isSideBarVisible, setSideBarBookKey, sideBarBookKey, toggleSideBar } = useReaderStore();
const handleToggleSidebar = () => {
if (sideBarBookKey === bookKey) {
toggleSideBar();
} else {
setSideBarBookKey(bookKey);
if (!isSideBarVisible) toggleSideBar();
}
};
return (
<button onClick={handleToggleSidebar} className='p-2'>
{sideBarBookKey == bookKey && isSideBarVisible ? (
<VscLayoutSidebarLeft size={16} />
) : (
<VscLayoutSidebarLeftOff size={16} />
)}
</button>
);
};
export default SidebarToggler;