diff --git a/apps/readest-app/src/app/reader/components/FootnotePopup.tsx b/apps/readest-app/src/app/reader/components/FootnotePopup.tsx index 6ba27168..26993687 100644 --- a/apps/readest-app/src/app/reader/components/FootnotePopup.tsx +++ b/apps/readest-app/src/app/reader/components/FootnotePopup.tsx @@ -1,10 +1,13 @@ +import clsx from 'clsx'; import React, { useEffect, useRef, useState } from 'react'; +import { MdArrowBack } from 'react-icons/md'; import { BookDoc } from '@/libs/document'; import { useReaderStore } from '@/store/readerStore'; import { useBookDataStore } from '@/store/bookDataStore'; import { useFoliateEvents } from '../hooks/useFoliateEvents'; import { useCustomFontStore } from '@/store/customFontStore'; +import { useResponsiveSize } from '@/hooks/useResponsiveSize'; import { getFootnoteStyles, getStyles, getThemeCode } from '@/utils/style'; import { getPopupPosition, getPosition, Position } from '@/utils/sel'; import { FootnoteHandler } from 'foliate-js/footnotes.js'; @@ -36,12 +39,20 @@ const FootnotePopup: React.FC = ({ bookKey, bookDoc }) => { const { getLoadedFonts } = useCustomFontStore(); const view = getView(bookKey); const viewSettings = getViewSettings(bookKey)!; - const footnoteHandler = new FootnoteHandler(); + const [footnoteHandler] = useState(() => new FootnoteHandler()); const containerRef = useRef(null); + const footnoteHrefRef = useRef(null); + const historyRef = useRef<{ items: Record[]; index: number }>({ + items: [], + index: -1, + }); + const [canGoBack, setCanGoBack] = useState(false); + const canGoBackRef = useRef(canGoBack); const [gridRect, setGridRect] = useState(null); const [responsiveWidth, setResponsiveWidth] = useState(popupWidth); const [responsiveHeight, setResponsiveHeight] = useState(popupHeight); + const size18 = useResponsiveSize(18); const getResponsivePopupSize = (size: number, isVertical: boolean) => { const maxSize = isVertical ? window.innerWidth / 2 : window.innerHeight / 2; @@ -57,16 +68,34 @@ const FootnotePopup: React.FC = ({ bookKey, bookDoc }) => { }; useEffect(() => { + const getHashFromHref = (href: string | null) => { + if (!href) return null; + const hashIndex = href.indexOf('#'); + if (hashIndex !== -1) { + return href.substring(hashIndex + 1); + } + return null; + }; const handleBeforeRender = (e: Event) => { const detail = (e as CustomEvent).detail; const { view } = detail; view.addEventListener('link', (e: Event) => { e.preventDefault(); const { detail: popupLinkDetail } = e as CustomEvent; + const footnoteAnchorId = getHashFromHref(footnoteHrefRef.current); + const linkAnchor = popupLinkDetail.a as HTMLAnchorElement; + if (linkAnchor && linkAnchor.getAttribute('id') === footnoteAnchorId) return; + popupLinkDetail['follow'] = true; + const history = historyRef.current; + const items = [...history.items.slice(0, history.index + 1), popupLinkDetail]; + historyRef.current = { items, index: items.length - 1 }; + setCanGoBack(true); + canGoBackRef.current = true; footnoteHandler.handle(bookDoc, e)?.catch((err) => { console.warn(err); getView(bookKey)?.goTo(popupLinkDetail.href); + setShowPopup(false); }); }); view.addEventListener('load', (e: CustomEvent) => { @@ -80,13 +109,14 @@ const FootnotePopup: React.FC = ({ bookKey, bookDoc }) => { footnoteViewRef.current = view; footnoteRef.current?.replaceChildren(view); const { renderer } = view; + const viewSettings = getViewSettings(bookKey)!; + const backButtonMargin = canGoBackRef.current ? 32 : 0; renderer.setAttribute('flow', 'scrolled'); - renderer.setAttribute('margin-top', '0px'); - renderer.setAttribute('margin-right', '0px'); + renderer.setAttribute('margin-top', `${viewSettings.vertical ? 0 : backButtonMargin}px`); + renderer.setAttribute('margin-right', `${viewSettings.vertical ? backButtonMargin : 0}px`); renderer.setAttribute('margin-bottom', '0px'); renderer.setAttribute('margin-left', '0px'); renderer.setAttribute('gap', '0%'); - const viewSettings = getViewSettings(bookKey)!; const themeCode = getThemeCode(); const popupTheme = { ...themeCode }; const popupContainer = document.getElementById('popup-container'); @@ -102,7 +132,8 @@ const FootnotePopup: React.FC = ({ bookKey, bookDoc }) => { const handleRender = (e: Event) => { const detail = (e as CustomEvent).detail; // console.log('render footnote', detail); - const { view } = detail; + const { view, href } = detail; + footnoteHrefRef.current = href; view.addEventListener('relocate', () => { const { renderer } = view as FoliateView; const viewSettings = getViewSettings(bookKey)!; @@ -169,6 +200,9 @@ const FootnotePopup: React.FC = ({ bookKey, bookDoc }) => { if (footnoteClasses.some((cls) => anchor.classList.contains(cls))) { detail['follow'] = true; } + historyRef.current = { items: [detail], index: 0 }; + setCanGoBack(false); + canGoBackRef.current = false; footnoteHandler.handle(bookDoc, event)?.catch((err) => { console.warn(err); const detail = (event as CustomEvent).detail; @@ -176,6 +210,21 @@ const FootnotePopup: React.FC = ({ bookKey, bookDoc }) => { }); }; + const handleBack = () => { + const history = historyRef.current; + if (history.index <= 0) return; + const newIndex = history.index - 1; + historyRef.current = { ...history, index: newIndex }; + setCanGoBack(newIndex > 0); + canGoBackRef.current = newIndex > 0; + const detail = history.items[newIndex]!; + const syntheticEvent = new CustomEvent('link', { + detail: { ...detail, follow: true }, + cancelable: true, + }); + footnoteHandler.handle(bookDoc, syntheticEvent); + }; + const closePopup = () => { const view = footnoteRef.current?.querySelector('foliate-view') as FoliateView; view?.close(); @@ -184,6 +233,9 @@ const FootnotePopup: React.FC = ({ bookKey, bookDoc }) => { const handleDismissPopup = () => { closePopup(); + historyRef.current = { items: [], index: -1 }; + setCanGoBack(false); + canGoBackRef.current = false; setGridRect(null); setPopupPosition(null); setTrianglePosition(null); @@ -257,6 +309,25 @@ const FootnotePopup: React.FC = ({ bookKey, bookDoc }) => { className='select-text overflow-y-auto' onDismiss={handleDismissPopup} > + {canGoBack && ( +
+ +
+ )}
` } a:any-link { - cursor: default; - pointer-events: none; text-decoration: none; padding: unset; margin: unset; diff --git a/packages/foliate-js b/packages/foliate-js index 423edcb3..9545e4dd 160000 --- a/packages/foliate-js +++ b/packages/foliate-js @@ -1 +1 @@ -Subproject commit 423edcb3a83628a919aa168bfcd1d432f696ef5c +Subproject commit 9545e4dd56d6a279f7763c3522bcaa5da4725b08