feat: add prev/next section buttons in footer bar, closes #1125 (#1195)

This commit is contained in:
Huang Xin
2025-05-19 22:40:11 +08:00
committed by GitHub
parent 90f3512ae2
commit 7c464b9a8d
22 changed files with 173 additions and 96 deletions
@@ -42,8 +42,6 @@
"Global Settings": "الإعدادات العامة",
"Go Back": "العودة",
"Go Forward": "التقدم",
"Go Left": "الذهاب لليسار",
"Go Right": "الذهاب لليمين",
"Grass": "عشبي",
"Gray": "رمادي",
"Gruvbox": "چروفبوكس",
@@ -310,5 +308,9 @@
"File Size:": "حجم الملف:",
"Auto Detect": "الكشف التلقائي",
"Touch": "اللمس",
"Scrolling Overlap (px)": "تداخل التمرير (بكسل)"
"Scrolling Overlap (px)": "تداخل التمرير (بكسل)",
"Next Section": "القسم التالي",
"Previous Section": "القسم السابق",
"Next Page": "الصفحة التالية",
"Previous Page": "الصفحة السابقة"
}
@@ -42,8 +42,6 @@
"Global Settings": "Globale Einstellungen",
"Go Back": "Zurück",
"Go Forward": "Vorwärts",
"Go Left": "Nach links",
"Go Right": "Nach rechts",
"Grass": "Grasgrün",
"Gray": "Grau",
"Gruvbox": "Gruvbox",
@@ -310,5 +308,9 @@
"File Size:": "Dateigröße:",
"Auto Detect": "Automatisch erkennen",
"Touch": "Berührung",
"Scrolling Overlap (px)": "Scroll-Überlappung (px)"
"Scrolling Overlap (px)": "Scroll-Überlappung (px)",
"Next Section": "Nächster Abschnitt",
"Previous Section": "Vorheriger Abschnitt",
"Next Page": "Nächste Seite",
"Previous Page": "Vorherige Seite"
}
@@ -42,8 +42,6 @@
"Global Settings": "Γενικές ρυθμίσεις",
"Go Back": "Πίσω",
"Go Forward": "Μπροστά",
"Go Left": "Αριστερά",
"Go Right": "Δεξιά",
"Grass": "Γρασίδι",
"Gray": "Γκρι",
"Gruvbox": "Gruvbox",
@@ -310,5 +308,9 @@
"File Size:": "Μέγεθος αρχείου:",
"Auto Detect": "Αυτόματη ανίχνευση",
"Touch": "Αφή",
"Scrolling Overlap (px)": "Επικάλυψη κύλισης (px)"
"Scrolling Overlap (px)": "Επικάλυψη κύλισης (px)",
"Next Section": "Επόμενη ενότητα",
"Previous Section": "Προηγούμενη ενότητα",
"Next Page": "Επόμενη σελίδα",
"Previous Page": "Προηγούμενη σελίδα"
}
@@ -42,8 +42,6 @@
"Global Settings": "Configuración global",
"Go Back": "Volver",
"Go Forward": "Avanzar",
"Go Left": "Ir a la izquierda",
"Go Right": "Ir a la derecha",
"Grass": "Hierba",
"Gray": "Gris",
"Gruvbox": "Gruvbox",
@@ -310,5 +308,9 @@
"File Size:": "Peso del archivo:",
"Auto Detect": "Detección automática",
"Touch": "Toque",
"Scrolling Overlap (px)": "Superposición de desplazamiento (px)"
"Scrolling Overlap (px)": "Superposición de desplazamiento (px)",
"Next Section": "Siguiente sección",
"Previous Section": "Sección anterior",
"Next Page": "Página siguiente",
"Previous Page": "Página anterior"
}
@@ -42,8 +42,6 @@
"Global Settings": "Paramètres globaux",
"Go Back": "Retour",
"Go Forward": "Avancer",
"Go Left": "Aller à gauche",
"Go Right": "Aller à droite",
"Grass": "Herbe",
"Gray": "Gris",
"Gruvbox": "Gruvbox",
@@ -310,5 +308,9 @@
"File Size:": "Taille du fichier :",
"Auto Detect": "Détection automatique",
"Touch": "Touch",
"Scrolling Overlap (px)": "Chevauchement de défilement (px)"
"Scrolling Overlap (px)": "Chevauchement de défilement (px)",
"Next Section": "Section suivante",
"Previous Section": "Section précédente",
"Next Page": "Page suivante",
"Previous Page": "Page précédente"
}
@@ -42,8 +42,6 @@
"Global Settings": "वैश्विक सेटिंग्स",
"Go Back": "वापस जाएं",
"Go Forward": "आगे जाएं",
"Go Left": "बाएं जाएं",
"Go Right": "दाएं जाएं",
"Grass": "घास",
"Gray": "ग्रे",
"Gruvbox": "ग्रूवबॉक्स",
@@ -310,5 +308,9 @@
"File Size:": "फ़ाइल आकार:",
"Auto Detect": "स्वचालित पहचान",
"Touch": "स्पर्श",
"Scrolling Overlap (px)": "स्क्रॉलिंग ओवरलैप (px)"
"Scrolling Overlap (px)": "स्क्रॉलिंग ओवरलैप (px)",
"Next Section": "अगला अनुभाग",
"Previous Section": "पिछला अनुभाग",
"Next Page": "अगला पृष्ठ",
"Previous Page": "पिछला पृष्ठ"
}
@@ -42,8 +42,6 @@
"Global Settings": "Pengaturan Global",
"Go Back": "Kembali",
"Go Forward": "Maju",
"Go Left": "Ke Kiri",
"Go Right": "Ke Kanan",
"Grass": "Rumput",
"Gray": "Abu-abu",
"Gruvbox": "Gruvbox",
@@ -310,5 +308,9 @@
"File Size:": "Ukuran File:",
"Auto Detect": "Deteksi Otomatis",
"Touch": "Sentuh",
"Scrolling Overlap (px)": "Overlapping Gulir (px)"
"Scrolling Overlap (px)": "Overlapping Gulir (px)",
"Next Section": "Bagian Selanjutnya",
"Previous Section": "Bagian Sebelumnya",
"Next Page": "Halaman Berikutnya",
"Previous Page": "Halaman Sebelumnya"
}
@@ -42,8 +42,6 @@
"Global Settings": "Impostazioni globali",
"Go Back": "Indietro",
"Go Forward": "Avanti",
"Go Left": "Vai a sinistra",
"Go Right": "Vai a destra",
"Grass": "Erba",
"Gray": "Grigio",
"Gruvbox": "Gruvbox",
@@ -310,5 +308,9 @@
"File Size:": "Dimensione file:",
"Auto Detect": "Rilevamento automatico",
"Touch": "Touch",
"Scrolling Overlap (px)": "Overlap scorrimento (px)"
"Scrolling Overlap (px)": "Overlap scorrimento (px)",
"Next Section": "Sezione successiva",
"Previous Section": "Sezione precedente",
"Next Page": "Pagina successiva",
"Previous Page": "Pagina precedente"
}
@@ -42,8 +42,6 @@
"Global Settings": "全体設定",
"Go Back": "戻る",
"Go Forward": "進む",
"Go Left": "左へ",
"Go Right": "右へ",
"Grass": "グリーン",
"Gray": "グレー",
"Gruvbox": "グルーブボックス",
@@ -310,5 +308,9 @@
"File Size:": "ファイルサイズ:",
"Auto Detect": "自動検出",
"Touch": "タッチ",
"Scrolling Overlap (px)": "スクロール重複(px"
"Scrolling Overlap (px)": "スクロール重複(px",
"Next Section": "次のセクション",
"Previous Section": "前のセクション",
"Next Page": "次のページ",
"Previous Page": "前のページ"
}
@@ -42,8 +42,6 @@
"Global Settings": "전역 설정",
"Go Back": "뒤로",
"Go Forward": "앞으로",
"Go Left": "왼쪽으로",
"Go Right": "오른쪽으로",
"Grass": "잔디색",
"Gray": "회색",
"Gruvbox": "그러브박스",
@@ -310,5 +308,9 @@
"File Size:": "파일 크기:",
"Auto Detect": "자동 감지",
"Touch": "터치",
"Scrolling Overlap (px)": "스크롤 겹침 (px)"
"Scrolling Overlap (px)": "스크롤 겹침 (px)",
"Next Section": "다음 섹션",
"Previous Section": "이전 섹션",
"Next Page": "다음 페이지",
"Previous Page": "이전 페이지"
}
@@ -123,8 +123,6 @@
"Translated Text": "Vertaalde tekst",
"Loading...": "Laden...",
"Bookmark": "Bladwijzer",
"Go Right": "Naar rechts",
"Go Left": "Naar links",
"Go Forward": "Vooruit",
"Small": "Klein",
"Large": "Groot",
@@ -310,5 +308,9 @@
"Dont have an account? Sign up": "Nog geen account? Registreer u",
"All the world's a stage,\nAnd all the men and women merely players;\nThey have their exits and their entrances,\nAnd one man in his time plays many parts,\nHis acts being seven ages.\n\n— William Shakespeare": "De hele wereld is een toneel,\nEn alle mannen en vrouwen slechts spelers;\nZij hebben hun uitgangen en hun entrees,\nEn één man speelt in zijn tijd vele rollen,\nZijn daden zijnde zeven leeftijden.\n\n— William Shakespeare",
"Touch": "Raak",
"Scrolling Overlap (px)": "Scroll-overlap (px)"
"Scrolling Overlap (px)": "Scroll-overlap (px)",
"Next Section": "Volgende sectie",
"Previous Section": "Vorige sectie",
"Next Page": "Volgende pagina",
"Previous Page": "Vorige pagina"
}
@@ -42,8 +42,6 @@
"Global Settings": "Ustawienia globalne",
"Go Back": "Wstecz",
"Go Forward": "Dalej",
"Go Left": "W lewo",
"Go Right": "W prawo",
"Grass": "Trawiasty",
"Gray": "Szary",
"Gruvbox": "Ciepły pomarańczowy",
@@ -310,5 +308,9 @@
"File Size:": "Rozmiar pliku:",
"Auto Detect": "Automatyczne wykrywanie",
"Touch": "Dotyk",
"Scrolling Overlap (px)": "Nakładanie przewijania (px)"
"Scrolling Overlap (px)": "Nakładanie przewijania (px)",
"Next Section": "Następna sekcja",
"Previous Section": "Poprzednia sekcja",
"Next Page": "Następna strona",
"Previous Page": "Poprzednia strona"
}
@@ -42,8 +42,6 @@
"Global Settings": "Configurações Globais",
"Go Back": "Voltar",
"Go Forward": "Avançar",
"Go Left": "Ir para Esquerda",
"Go Right": "Ir para Direita",
"Grass": "Grama",
"Gray": "Cinza",
"Gruvbox": "Gruvbox",
@@ -310,5 +308,9 @@
"File Size:": "Tamanho do arquivo:",
"Auto Detect": "Detectar Automaticamente",
"Touch": "Toque",
"Scrolling Overlap (px)": "Sobreposição de Rolagem (px)"
"Scrolling Overlap (px)": "Sobreposição de Rolagem (px)",
"Next Section": "Próxima seção",
"Previous Section": "Seção anterior",
"Next Page": "Próxima página",
"Previous Page": "Página anterior"
}
@@ -42,8 +42,6 @@
"Global Settings": "Общие настройки",
"Go Back": "Назад",
"Go Forward": "Вперёд",
"Go Left": "Влево",
"Go Right": "Вправо",
"Grass": "Трава",
"Gray": "Серый",
"Gruvbox": "Gruvbox",
@@ -310,5 +308,9 @@
"File Size:": "Размер файла:",
"Auto Detect": "Автоопределение",
"Touch": "Сенсорный экран",
"Scrolling Overlap (px)": "Перекрытие прокрутки (px)"
"Scrolling Overlap (px)": "Перекрытие прокрутки (px)",
"Next Section": "Следующий раздел",
"Previous Section": "Предыдущий раздел",
"Next Page": "Следующая страница",
"Previous Page": "Предыдущая страница"
}
@@ -42,8 +42,6 @@
"Global Settings": "Genel Ayarlar",
"Go Back": "Geri Git",
"Go Forward": "İleri Git",
"Go Left": "Sola Git",
"Go Right": "Sağa Git",
"Grass": "Çimen",
"Gray": "Gri",
"Gruvbox": "Gruvbox",
@@ -310,5 +308,9 @@
"File Size:": "Dosya Boyutu:",
"Auto Detect": "Algıla",
"Touch": "Dokunmatik",
"Scrolling Overlap (px)": "Kaydırma Üst Üste Binen (px)"
"Scrolling Overlap (px)": "Kaydırma Üst Üste Binen (px)",
"Next Section": "Sonraki Bölüm",
"Previous Section": "Önceki Bölüm",
"Next Page": "Sonraki Sayfa",
"Previous Page": "Önceki Sayfa"
}
@@ -42,8 +42,6 @@
"Global Settings": "Глобальні налаштування",
"Go Back": "Назад",
"Go Forward": "Вперед",
"Go Left": "Вліво",
"Go Right": "Вправо",
"Grass": "Трав'яний",
"Gray": "Сірий",
"Gruvbox": "Теплий помаранчевий",
@@ -310,5 +308,9 @@
"File Size:": "Розмір файлу:",
"Auto Detect": "Авто виявлення",
"Touch": "Дотик",
"Scrolling Overlap (px)": "Прокрутка перекриття (px)"
"Scrolling Overlap (px)": "Прокрутка перекриття (px)",
"Next Section": "Наступний розділ",
"Previous Section": "Попередній розділ",
"Next Page": "Наступна сторінка",
"Previous Page": "Попередня сторінка"
}
@@ -42,8 +42,6 @@
"Global Settings": "Cài đặt chung",
"Go Back": "Quay lại",
"Go Forward": "Tiến tới",
"Go Left": "Sang trái",
"Go Right": "Sang phải",
"Grass": "Xanh cỏ",
"Gray": "Xám",
"Gruvbox": "Gruvbox",
@@ -310,5 +308,9 @@
"File Size:": "Kích thước tệp:",
"Auto Detect": "Phát hiện tự động",
"Touch": "Chạm",
"Scrolling Overlap (px)": "Chồng chéo cuộn (px)"
"Scrolling Overlap (px)": "Chồng chéo cuộn (px)",
"Next Section": "Phần tiếp theo",
"Previous Section": "Phần trước",
"Next Page": "Trang tiếp theo",
"Previous Page": "Trang trước"
}
@@ -42,8 +42,6 @@
"Global Settings": "全局设置",
"Go Back": "返回",
"Go Forward": "前进",
"Go Left": "向左",
"Go Right": "向右",
"Grass": "青草",
"Gray": "素雅",
"Gruvbox": "暖橘",
@@ -310,5 +308,9 @@
"File Size:": "文件大小",
"Auto Detect": "自动检测",
"Touch": "触控",
"Scrolling Overlap (px)": "滚动重叠(px"
"Scrolling Overlap (px)": "滚动重叠(px",
"Next Section": "下一章",
"Previous Section": "上一章",
"Next Page": "下一页",
"Previous Page": "上一页"
}
@@ -42,8 +42,6 @@
"Global Settings": "全局設置",
"Go Back": "返回",
"Go Forward": "前進",
"Go Left": "向左",
"Go Right": "向右",
"Grass": "青草",
"Gray": "素雅",
"Gruvbox": "暖橘",
@@ -310,5 +308,9 @@
"File Size:": "檔案大小",
"Auto Detect": "自動檢測",
"Touch": "觸控",
"Scrolling Overlap (px)": "滾動重疊(px"
"Scrolling Overlap (px)": "滾動重疊(px",
"Next Section": "下一章",
"Previous Section": "上一章",
"Next Page": "下一頁",
"Previous Page": "上一頁"
}
@@ -1,7 +1,8 @@
import React, { useEffect } from 'react';
import clsx from 'clsx';
import { RiArrowLeftWideLine, RiArrowRightWideLine } from 'react-icons/ri';
import { RiArrowLeftSLine, RiArrowRightSLine } from 'react-icons/ri';
import { RiArrowGoBackLine, RiArrowGoForwardLine } from 'react-icons/ri';
import { RiArrowLeftDoubleLine, RiArrowRightDoubleLine } from 'react-icons/ri';
import { FaHeadphones } from 'react-icons/fa6';
import { IoIosList as TOCIcon } from 'react-icons/io';
import { PiNotePencil as NoteIcon } from 'react-icons/pi';
@@ -77,14 +78,26 @@ const FooterBar: React.FC<FooterBarProps> = ({
saveViewSettings(envConfig, bookKey, 'lineHeight', value / 10);
};
const handleGoPrev = () => {
const handleGoPrevPage = () => {
viewPagination(view, viewSettings, 'left');
};
const handleGoNext = () => {
const handleGoNextPage = () => {
viewPagination(view, viewSettings, 'right');
};
const handleGoPrevSection = () => {
if (view?.renderer.prevSection) {
view?.renderer.prevSection();
}
};
const handleGoNextSection = () => {
if (view?.renderer.nextSection) {
view?.renderer.nextSection();
}
};
const handleGoBack = () => {
view?.history.back();
};
@@ -174,7 +187,7 @@ const FooterBar: React.FC<FooterBarProps> = ({
{/* Mobile footer bar */}
<div
className={clsx(
'bg-base-200 absolute bottom-16 flex w-full items-center gap-x-2 px-4 transition-all sm:hidden',
'bg-base-200 absolute bottom-16 flex w-full flex-col items-center gap-y-8 px-4 transition-all sm:hidden',
actionTab === 'progress'
? 'pointer-events-auto translate-y-0 pb-4 pt-8 ease-out'
: 'pointer-events-none invisible translate-y-full overflow-hidden pb-0 pt-0 ease-in',
@@ -185,34 +198,48 @@ const FooterBar: React.FC<FooterBarProps> = ({
: '64px',
}}
>
<Button
icon={viewSettings?.rtl ? <RiArrowRightWideLine /> : <RiArrowLeftWideLine />}
onClick={viewSettings?.rtl ? handleGoNext : handleGoPrev}
tooltip={viewSettings?.rtl ? _('Go Right') : _('Go Left')}
/>
<Button
icon={viewSettings?.rtl ? <RiArrowGoForwardLine /> : <RiArrowGoBackLine />}
onClick={handleGoBack}
tooltip={_('Go Back')}
disabled={!view?.history.canGoBack}
/>
<Button
icon={viewSettings?.rtl ? <RiArrowGoBackLine /> : <RiArrowGoForwardLine />}
onClick={handleGoForward}
tooltip={_('Go Forward')}
disabled={!view?.history.canGoForward}
/>
<Slider
heightPx={sliderHeight}
bubbleLabel={`${Math.round(progressFraction * 100)}%`}
initialValue={progressValid ? progressFraction * 100 : 0}
onChange={(e) => handleProgressChange(e)}
/>
<Button
icon={viewSettings?.rtl ? <RiArrowLeftWideLine /> : <RiArrowRightWideLine />}
onClick={viewSettings?.rtl ? handleGoPrev : handleGoNext}
tooltip={viewSettings?.rtl ? _('Go Left') : _('Go Right')}
/>
<div className='flex w-full items-center justify-between gap-x-6'>
<Slider
heightPx={sliderHeight}
bubbleLabel={`${Math.round(progressFraction * 100)}%`}
initialValue={progressValid ? progressFraction * 100 : 0}
onChange={(e) => handleProgressChange(e)}
/>
</div>
<div className='flex w-full items-center justify-between gap-x-6'>
<Button
icon={viewSettings?.rtl ? <RiArrowRightDoubleLine /> : <RiArrowLeftDoubleLine />}
onClick={viewSettings?.rtl ? handleGoNextSection : handleGoPrevSection}
tooltip={viewSettings?.rtl ? _('Next Section') : _('Previous Section')}
/>
<Button
icon={viewSettings?.rtl ? <RiArrowRightSLine /> : <RiArrowLeftSLine />}
onClick={viewSettings?.rtl ? handleGoNextPage : handleGoPrevPage}
tooltip={viewSettings?.rtl ? _('Next Page') : _('Previous Page')}
/>
<Button
icon={viewSettings?.rtl ? <RiArrowGoForwardLine /> : <RiArrowGoBackLine />}
onClick={handleGoBack}
tooltip={_('Go Back')}
disabled={!view?.history.canGoBack}
/>
<Button
icon={viewSettings?.rtl ? <RiArrowGoBackLine /> : <RiArrowGoForwardLine />}
onClick={handleGoForward}
tooltip={_('Go Forward')}
disabled={!view?.history.canGoForward}
/>
<Button
icon={viewSettings?.rtl ? <RiArrowLeftSLine /> : <RiArrowRightSLine />}
onClick={viewSettings?.rtl ? handleGoPrevPage : handleGoNextPage}
tooltip={viewSettings?.rtl ? _('Previous Page') : _('Next Page')}
/>
<Button
icon={viewSettings?.rtl ? <RiArrowLeftDoubleLine /> : <RiArrowRightDoubleLine />}
onClick={viewSettings?.rtl ? handleGoPrevSection : handleGoNextSection}
tooltip={viewSettings?.rtl ? _('Previous Section') : _('Next Section')}
/>
</div>
</div>
<div
className={clsx(
@@ -290,9 +317,14 @@ const FooterBar: React.FC<FooterBarProps> = ({
{/* Desktop footer bar */}
<div className='hidden w-full items-center gap-x-4 px-4 sm:flex'>
<Button
icon={viewSettings?.rtl ? <RiArrowRightWideLine /> : <RiArrowLeftWideLine />}
onClick={viewSettings?.rtl ? handleGoNext : handleGoPrev}
tooltip={viewSettings?.rtl ? _('Go Right') : _('Go Left')}
icon={viewSettings?.rtl ? <RiArrowRightDoubleLine /> : <RiArrowLeftDoubleLine />}
onClick={viewSettings?.rtl ? handleGoNextSection : handleGoPrevSection}
tooltip={viewSettings?.rtl ? _('Next Section') : _('Previous Section')}
/>
<Button
icon={viewSettings?.rtl ? <RiArrowRightSLine /> : <RiArrowLeftSLine />}
onClick={viewSettings?.rtl ? handleGoNextPage : handleGoPrevPage}
tooltip={viewSettings?.rtl ? _('Next Page') : _('Previous Page')}
/>
<Button
icon={viewSettings?.rtl ? <RiArrowGoForwardLine /> : <RiArrowGoBackLine />}
@@ -321,9 +353,14 @@ const FooterBar: React.FC<FooterBarProps> = ({
/>
<Button icon={<FaHeadphones />} onClick={handleSpeakText} tooltip={_('Speak')} />
<Button
icon={viewSettings?.rtl ? <RiArrowLeftWideLine /> : <RiArrowRightWideLine />}
onClick={viewSettings?.rtl ? handleGoPrev : handleGoNext}
tooltip={viewSettings?.rtl ? _('Go Left') : _('Go Right')}
icon={viewSettings?.rtl ? <RiArrowLeftSLine /> : <RiArrowRightSLine />}
onClick={viewSettings?.rtl ? handleGoPrevPage : handleGoNextPage}
tooltip={viewSettings?.rtl ? _('Previous Page') : _('Next Page')}
/>
<Button
icon={viewSettings?.rtl ? <RiArrowLeftDoubleLine /> : <RiArrowRightDoubleLine />}
onClick={viewSettings?.rtl ? handleGoPrevSection : handleGoNextSection}
tooltip={viewSettings?.rtl ? _('Previous Section') : _('Next Section')}
/>
</div>
</div>
+1 -1
View File
@@ -65,7 +65,7 @@ const Slider: React.FC<SliderProps> = ({
return (
<div
ref={sliderRef}
className={`slider bg-base-200 mx-auto w-full max-w-md rounded-xl ${className}`}
className={`slider bg-base-200 mx-auto w-full rounded-xl ${className}`}
dir={isRtl ? 'rtl' : undefined}
>
<div className='relative' style={{ height: `${heightPx}px` }}>
+2
View File
@@ -45,6 +45,8 @@ export interface FoliateView extends HTMLElement {
removeAttribute: (name: string) => void;
next: () => Promise<void>;
prev: () => Promise<void>;
nextSection?: () => Promise<void>;
prevSection?: () => Promise<void>;
goTo?: (params: { index: number; anchor: number }) => void;
setStyles?: (css: string) => void;
getContents: () => { doc: Document; index?: number }[];