feat: responsive window size for popup footnotes, closes #538 (#632)

This commit is contained in:
Huang Xin
2025-03-19 00:00:07 +08:00
committed by GitHub
parent 359fa8df35
commit 82f303fcfe
4 changed files with 79 additions and 33 deletions
@@ -62,7 +62,7 @@ const FoliateViewer: React.FC<{
viewSettings.vertical =
writingDir?.vertical || viewSettings.writingMode.includes('vertical') || false;
viewSettings.rtl = writingDir?.rtl || viewSettings.writingMode.includes('rl') || false;
setViewSettings(bookKey, viewSettings);
setViewSettings(bookKey, { ...viewSettings });
applyColorScheme(detail.doc, getIsDarkMode());
mountAdditionalFonts(detail.doc);
@@ -25,11 +25,17 @@ const FootnotePopup: React.FC<FootnotePopupProps> = ({ bookKey, bookDoc }) => {
const [trianglePosition, setTrianglePosition] = useState<Position | null>();
const [popupPosition, setPopupPosition] = useState<Position | null>();
const [showPopup, setShowPopup] = useState(false);
const { getView, getViewSettings } = useReaderStore();
const { getView, getProgress, getViewSettings } = useReaderStore();
const view = getView(bookKey);
const progress = getProgress(bookKey)!;
const viewSettings = getViewSettings(bookKey)!;
const footnoteHandler = new FootnoteHandler();
const [gridRect, setGridRect] = useState<DOMRect | null>(null);
const [responsiveWidth, setResponsiveWidth] = useState(popupWidth);
const [responsiveHeight, setResponsiveHeight] = useState(popupHeight);
useEffect(() => {
const handleBeforeRender = (e: Event) => {
const detail = (e as CustomEvent).detail;
@@ -48,7 +54,7 @@ const FootnotePopup: React.FC<FootnotePopupProps> = ({ bookKey, bookDoc }) => {
const { renderer } = view;
renderer.setAttribute('flow', 'scrolled');
renderer.setAttribute('margin', '0px');
renderer.setAttribute('gap', '5%');
renderer.setAttribute('gap', '0%');
const viewSettings = getViewSettings(bookKey)!;
const themeCode = getThemeCode();
const popupTheme = { ...themeCode };
@@ -64,8 +70,17 @@ const FootnotePopup: React.FC<FootnotePopupProps> = ({ bookKey, bookDoc }) => {
const handleRender = (e: Event) => {
const detail = (e as CustomEvent).detail;
console.log('render footnote', detail);
setShowPopup(true);
// console.log('render footnote', detail);
const { view } = detail;
view.addEventListener('relocate', () => {
const { renderer } = view as FoliateView;
if (viewSettings.vertical) {
setResponsiveWidth(Math.min(renderer.viewSize, window.innerWidth / 2));
} else {
setResponsiveHeight(Math.min(renderer.viewSize, window.innerHeight / 2));
}
setShowPopup(true);
});
};
footnoteHandler.addEventListener('before-render', handleBeforeRender);
@@ -77,23 +92,44 @@ const FootnotePopup: React.FC<FootnotePopupProps> = ({ bookKey, bookDoc }) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [view]);
useEffect(() => {
handleDismissPopup();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [progress]);
useEffect(() => {
if (viewSettings.vertical) {
setResponsiveWidth(popupHeight);
setResponsiveHeight(Math.max(360, window.innerHeight / 4));
} else {
setResponsiveWidth(Math.max(360, window.innerWidth / 4));
setResponsiveHeight(popupHeight);
}
}, [viewSettings]);
useEffect(() => {
if (trianglePosition && gridRect) {
const popupPos = getPopupPosition(
trianglePosition,
gridRect,
responsiveWidth,
responsiveHeight,
popupPadding,
);
setPopupPosition(popupPos);
}
}, [trianglePosition, gridRect, responsiveWidth, responsiveHeight]);
const docLinkHandler = async (event: Event) => {
const detail = (event as CustomEvent).detail;
console.log('doc link click', detail);
// console.log('doc link click', detail);
const gridFrame = document.querySelector(`#gridcell-${bookKey}`);
if (!gridFrame) return;
const rect = gridFrame.getBoundingClientRect();
const viewSettings = getViewSettings(bookKey)!;
const triangPos = getPosition(detail.a, rect, popupPadding, viewSettings.vertical);
const popupPos = getPopupPosition(
triangPos,
rect,
viewSettings.vertical ? popupHeight : popupWidth,
viewSettings.vertical ? popupWidth : popupHeight,
popupPadding,
);
setGridRect(rect);
setTrianglePosition(triangPos);
setPopupPosition(popupPos);
footnoteHandler.handle(bookDoc, event)?.catch((err) => {
console.warn(err);
@@ -110,11 +146,13 @@ const FootnotePopup: React.FC<FootnotePopupProps> = ({ bookKey, bookDoc }) => {
const handleDismissPopup = () => {
closePopup();
setGridRect(null);
setPopupPosition(null);
setTrianglePosition(null);
setShowPopup(false);
};
// Handle custom footnote popup event from iframe event
const handleFootnotePopupEvent = (event: CustomEvent) => {
const { element, footnote } = event.detail;
const gridFrame = document.querySelector(`#gridcell-${bookKey}`);
@@ -122,21 +160,30 @@ const FootnotePopup: React.FC<FootnotePopupProps> = ({ bookKey, bookDoc }) => {
const rect = gridFrame.getBoundingClientRect();
const viewSettings = getViewSettings(bookKey)!;
const triangPos = getPosition(element, rect, popupPadding, viewSettings.vertical);
const popupPos = getPopupPosition(
triangPos,
rect,
viewSettings.vertical ? popupHeight : popupWidth,
viewSettings.vertical ? popupWidth : popupHeight,
popupPadding,
);
if (footnoteRef.current) {
const elem = document.createElement('p');
elem.textContent = footnote;
elem.setAttribute('style', `padding: 16px; hanging-punctuation: allow-end last;`);
elem.setAttribute('style', `padding: 1em; hanging-punctuation: allow-end last;`);
elem.style.visibility = 'hidden';
if (viewSettings.vertical) {
elem.style.height = `${responsiveHeight}px`;
} else {
elem.style.width = `${responsiveWidth}px`;
}
document.body.appendChild(elem);
const popupSize = elem.getBoundingClientRect();
if (viewSettings.vertical) {
setResponsiveWidth(Math.min(popupSize.width, window.innerWidth / 2));
} else {
setResponsiveHeight(Math.min(popupSize.height, window.innerHeight / 2));
}
document.body.removeChild(elem);
elem.style.visibility = 'visible';
footnoteRef.current.replaceChildren(elem);
setShowPopup(true);
setGridRect(rect);
setTrianglePosition(triangPos);
setPopupPosition(popupPos);
setShowPopup(true);
}
};
@@ -158,9 +205,6 @@ const FootnotePopup: React.FC<FootnotePopupProps> = ({ bookKey, bookDoc }) => {
}
}, [footnoteRef]);
const width = viewSettings.vertical ? popupHeight : popupWidth;
const height = viewSettings.vertical ? popupWidth : popupHeight;
return (
<div>
{showPopup && (
@@ -171,8 +215,8 @@ const FootnotePopup: React.FC<FootnotePopupProps> = ({ bookKey, bookDoc }) => {
/>
)}
<Popup
width={width}
height={height}
width={responsiveWidth}
height={responsiveHeight}
position={showPopup ? popupPosition! : undefined}
trianglePosition={showPopup ? trianglePosition! : undefined}
className='select-text overflow-y-auto'
@@ -181,8 +225,8 @@ const FootnotePopup: React.FC<FootnotePopupProps> = ({ bookKey, bookDoc }) => {
className=''
ref={footnoteRef}
style={{
width: `${width}px`,
height: `${height}px`,
width: `${responsiveWidth}px`,
height: `${responsiveHeight}px`,
}}
></div>
</Popup>
+3 -1
View File
@@ -254,7 +254,9 @@ export const getFootnoteStyles = () => `
}
p, li, blockquote, dd {
text-indent: 0;
padding: 1em !important;
margin: 0 !important;
text-indent: 0 !important;
}
`;