diff --git a/apps/readest-app/src/app/layout.tsx b/apps/readest-app/src/app/layout.tsx index 87fae059..25abcbd2 100644 --- a/apps/readest-app/src/app/layout.tsx +++ b/apps/readest-app/src/app/layout.tsx @@ -16,6 +16,8 @@ export default function RootLayout({ children }: { children: React.ReactNode }) {title} + + diff --git a/apps/readest-app/src/app/reader/components/BooksGrid.tsx b/apps/readest-app/src/app/reader/components/BooksGrid.tsx index f7552a03..bddf1689 100644 --- a/apps/readest-app/src/app/reader/components/BooksGrid.tsx +++ b/apps/readest-app/src/app/reader/components/BooksGrid.tsx @@ -12,6 +12,7 @@ import Ribbon from './Ribbon'; import SettingsDialog from './settings/SettingsDialog'; import Annotator from './annotator/Annotator'; import { useBookDataStore } from '@/store/bookDataStore'; +import FootnotePopup from './FootnotePopup'; interface BooksGridProps { bookKeys: string[]; @@ -60,6 +61,7 @@ const BooksGrid: React.FC = ({ bookKeys, onCloseBook }) => { onSetSettingsDialogOpen={setFontLayoutSettingsDialogOpen} /> + {viewSettings.scrolled ? null : ( <> diff --git a/apps/readest-app/src/app/reader/components/FootnotePopup.tsx b/apps/readest-app/src/app/reader/components/FootnotePopup.tsx new file mode 100644 index 00000000..103d8700 --- /dev/null +++ b/apps/readest-app/src/app/reader/components/FootnotePopup.tsx @@ -0,0 +1,132 @@ +import React, { useEffect, useRef, useState } from 'react'; + +import { BookDoc } from '@/libs/document'; +import { useReaderStore } from '@/store/readerStore'; +import { useFoliateEvents } from '../hooks/useFoliateEvents'; +import { useTheme } from '@/hooks/useTheme'; +import { getStyles } from '@/utils/style'; +import { getPopupPosition, getPosition, Position } from '@/utils/sel'; +import { FoliateView } from './FoliateViewer'; +import { FootnoteHandler } from 'foliate-js/footnotes.js'; +import Popup from '@/components/Popup'; + +interface FootnotePopupProps { + bookKey: string; + bookDoc: BookDoc; +} + +const popupWidth = 360; +const popupHeight = 88; +const popupPadding = 10; + +const FootnotePopup: React.FC = ({ bookKey, bookDoc }) => { + const footnoteRef = useRef(null); + const footnoteViewRef = useRef(null); + const [trianglePosition, setTrianglePosition] = useState(); + const [popupPosition, setPopupPosition] = useState(); + const [showPopup, setShowPopup] = useState(false); + const { getView, getViewSettings } = useReaderStore(); + const { themeCode } = useTheme(); + const view = getView(bookKey); + const footnoteHandler = new FootnoteHandler(); + + useEffect(() => { + const handleBeforeRender = (e: Event) => { + const detail = (e as CustomEvent).detail; + const { view } = detail; + footnoteViewRef.current = view; + footnoteRef.current?.replaceChildren(view); + const { renderer } = view; + renderer.setAttribute('flow', 'scrolled'); + renderer.setAttribute('margin', '0px'); + renderer.setAttribute('gap', '5%'); + const viewSettings = getViewSettings(bookKey)!; + renderer.setStyles?.(getStyles(viewSettings, themeCode)); + }; + + const handleRender = (e: Event) => { + const detail = (e as CustomEvent).detail; + console.log('render footnote', detail); + setShowPopup(true); + }; + + footnoteHandler.addEventListener('before-render', handleBeforeRender); + footnoteHandler.addEventListener('render', handleRender); + return () => { + footnoteHandler.removeEventListener('before-render', handleBeforeRender); + footnoteHandler.removeEventListener('render', handleRender); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [view]); + + const docLinkHandler = async (event: Event) => { + const detail = (event as CustomEvent).detail; + console.log('doc link click', detail); + const gridFrame = document.querySelector(`#gridcell-${bookKey}`); + if (!gridFrame) return; + const rect = gridFrame.getBoundingClientRect(); + const triangPos = getPosition(detail.a, rect); + const popupPos = getPopupPosition(triangPos, rect, popupWidth, popupHeight, popupPadding); + setTrianglePosition(triangPos); + setPopupPosition(popupPos); + + footnoteHandler.handle(bookDoc, event)?.catch((err) => { + console.warn(err); + const detail = (event as CustomEvent).detail; + view?.goTo(detail.href); + }); + }; + + const closePopup = () => { + const view = footnoteRef.current?.querySelector('foliate-view') as FoliateView; + view?.close(); + view?.remove(); + }; + + const handleDismissPopup = () => { + closePopup(); + setPopupPosition(null); + setTrianglePosition(null); + setShowPopup(false); + }; + + useFoliateEvents(view, { + onLinkClick: docLinkHandler, + }); + + useEffect(() => { + if (footnoteViewRef.current) { + footnoteRef.current?.replaceChildren(footnoteViewRef.current); + } + }, [footnoteRef]); + + return ( +
+ {showPopup && ( +
+ )} + +
+
+
+ ); +}; + +export default FootnotePopup; diff --git a/apps/readest-app/src/app/reader/components/annotator/DeepLPopup.tsx b/apps/readest-app/src/app/reader/components/annotator/DeepLPopup.tsx index ee19d66b..04ea0769 100644 --- a/apps/readest-app/src/app/reader/components/annotator/DeepLPopup.tsx +++ b/apps/readest-app/src/app/reader/components/annotator/DeepLPopup.tsx @@ -120,8 +120,7 @@ const DeepLPopup: React.FC = ({ width={popupWidth} height={popupHeight} position={position} - className='bg-neutral select-text' - triangleClassName='text-neutral' + className='select-text' >
diff --git a/apps/readest-app/src/app/reader/components/annotator/WikipediaPopup.tsx b/apps/readest-app/src/app/reader/components/annotator/WikipediaPopup.tsx index e5d7bbf9..acce0475 100644 --- a/apps/readest-app/src/app/reader/components/annotator/WikipediaPopup.tsx +++ b/apps/readest-app/src/app/reader/components/annotator/WikipediaPopup.tsx @@ -107,8 +107,7 @@ const WikipediaPopup: React.FC = ({ height={popupHeight} position={position} trianglePosition={trianglePosition} - className='bg-neutral select-text overflow-y-auto' - triangleClassName='text-neutral' + className='select-text overflow-y-auto' >