feat: support separate header/footer visibility for paginated and scrolled modes (#859)
This commit is contained in:
@@ -274,5 +274,7 @@
|
||||
"{{value}} hour": "{{value}} ساعة",
|
||||
"{{value}} hours": "{{value}} ساعات",
|
||||
"CJK Font": "خط CJK",
|
||||
"Clear Search": "مسح البحث"
|
||||
"Clear Search": "مسح البحث",
|
||||
"Header & Footer": "الرأس والتذييل",
|
||||
"Apply also in Scrolled Mode": "تطبيق أيضًا في وضع التمرير"
|
||||
}
|
||||
|
||||
@@ -274,5 +274,7 @@
|
||||
"{{value}} hour": "{{value}} Stunde",
|
||||
"{{value}} hours": "{{value}} Stunden",
|
||||
"CJK Font": "CJK-Schriftart",
|
||||
"Clear Search": "Suche löschen"
|
||||
"Clear Search": "Suche löschen",
|
||||
"Header & Footer": "Kopf- & Fußzeile",
|
||||
"Apply also in Scrolled Mode": "Auch im Scroll-Modus anwenden"
|
||||
}
|
||||
|
||||
@@ -274,5 +274,7 @@
|
||||
"{{value}} hour": "{{value}} ώρα",
|
||||
"{{value}} hours": "{{value}} ώρες",
|
||||
"CJK Font": "Γραμματοσειρά CJK",
|
||||
"Clear Search": "Καθαρισμός αναζήτησης"
|
||||
"Clear Search": "Καθαρισμός αναζήτησης",
|
||||
"Header & Footer": "Κεφαλίδα & Υποσέλιδο",
|
||||
"Apply also in Scrolled Mode": "Εφαρμογή και σε λειτουργία κύλισης"
|
||||
}
|
||||
|
||||
@@ -274,5 +274,7 @@
|
||||
"{{value}} hour": "{{value}} hora",
|
||||
"{{value}} hours": "{{value}} horas",
|
||||
"CJK Font": "Fuente CJK",
|
||||
"Clear Search": "Limpiar búsqueda"
|
||||
"Clear Search": "Limpiar búsqueda",
|
||||
"Header & Footer": "Encabezado y pie de página",
|
||||
"Apply also in Scrolled Mode": "Aplicar también en modo desplazamiento"
|
||||
}
|
||||
|
||||
@@ -274,5 +274,7 @@
|
||||
"{{value}} hour": "{{value}} heure",
|
||||
"{{value}} hours": "{{value}} heures",
|
||||
"CJK Font": "Police CJK",
|
||||
"Clear Search": "Effacer la recherche"
|
||||
"Clear Search": "Effacer la recherche",
|
||||
"Header & Footer": "En-tête et pied de page",
|
||||
"Apply also in Scrolled Mode": "Appliquer également en mode défilement"
|
||||
}
|
||||
|
||||
@@ -274,5 +274,7 @@
|
||||
"{{value}} hour": "{{value}} घंटा",
|
||||
"{{value}} hours": "{{value}} घंटे",
|
||||
"CJK Font": "CJK फ़ॉन्ट",
|
||||
"Clear Search": "खोज साफ करें"
|
||||
"Clear Search": "खोज साफ करें",
|
||||
"Header & Footer": "हेडर और फ़ुटर",
|
||||
"Apply also in Scrolled Mode": "स्क्रॉल मोड में भी लागू करें"
|
||||
}
|
||||
|
||||
@@ -274,5 +274,7 @@
|
||||
"{{value}} hour": "{{value}} jam",
|
||||
"{{value}} hours": "{{value}} jam",
|
||||
"CJK Font": "Font CJK",
|
||||
"Clear Search": "Hapus Pencarian"
|
||||
"Clear Search": "Hapus Pencarian",
|
||||
"Header & Footer": "Header & Footer",
|
||||
"Apply also in Scrolled Mode": "Terapkan juga di Mode Gulir"
|
||||
}
|
||||
|
||||
@@ -274,5 +274,7 @@
|
||||
"{{value}} hour": "{{value}} ora",
|
||||
"{{value}} hours": "{{value}} ore",
|
||||
"CJK Font": "Font CJK",
|
||||
"Clear Search": "Cancella ricerca"
|
||||
"Clear Search": "Cancella ricerca",
|
||||
"Header & Footer": "Intestazione e piè di pagina",
|
||||
"Apply also in Scrolled Mode": "Applica anche in modalità scorrimento"
|
||||
}
|
||||
|
||||
@@ -274,5 +274,7 @@
|
||||
"{{value}} hour": "{{value}}時間",
|
||||
"{{value}} hours": "{{value}}時間",
|
||||
"CJK Font": "CJKフォント",
|
||||
"Clear Search": "検索をクリア"
|
||||
"Clear Search": "検索をクリア",
|
||||
"Header & Footer": "ヘッダーとフッター",
|
||||
"Apply also in Scrolled Mode": "スクロールモードでも適用"
|
||||
}
|
||||
|
||||
@@ -274,5 +274,7 @@
|
||||
"{{value}} hour": "{{value}} 시간",
|
||||
"{{value}} hours": "{{value}} 시간",
|
||||
"CJK Font": "CJK 글꼴",
|
||||
"Clear Search": "검색 지우기"
|
||||
"Clear Search": "검색 지우기",
|
||||
"Header & Footer": "헤더 및 푸터",
|
||||
"Apply also in Scrolled Mode": "스크롤 모드에서도 적용"
|
||||
}
|
||||
|
||||
@@ -274,5 +274,7 @@
|
||||
"{{value}} hour": "{{value}} godzina",
|
||||
"{{value}} hours": "{{value}} godziny",
|
||||
"CJK Font": "Czcionka CJK",
|
||||
"Clear Search": "Wyczyść wyszukiwanie"
|
||||
"Clear Search": "Wyczyść wyszukiwanie",
|
||||
"Header & Footer": "Nagłówek i stopka",
|
||||
"Apply also in Scrolled Mode": "Zastosuj również w trybie przewijania"
|
||||
}
|
||||
|
||||
@@ -274,5 +274,7 @@
|
||||
"{{value}} hour": "{{value}} hora",
|
||||
"{{value}} hours": "{{value}} horas",
|
||||
"CJK Font": "Fonte CJK",
|
||||
"Clear Search": "Limpar Pesquisa"
|
||||
"Clear Search": "Limpar Pesquisa",
|
||||
"Header & Footer": "Cabeçalho e Rodapé",
|
||||
"Apply also in Scrolled Mode": "Aplicar também no Modo Rolado"
|
||||
}
|
||||
|
||||
@@ -274,5 +274,7 @@
|
||||
"{{value}} hour": "{{value}} час",
|
||||
"{{value}} hours": "{{value}} часа",
|
||||
"CJK Font": "CJK Шрифт",
|
||||
"Clear Search": "Очистить поиск"
|
||||
"Clear Search": "Очистить поиск",
|
||||
"Header & Footer": "Заголовок и нижний колонтитул",
|
||||
"Apply also in Scrolled Mode": "Применить также в прокручиваемом режиме"
|
||||
}
|
||||
|
||||
@@ -274,5 +274,7 @@
|
||||
"{{value}} hour": "{{value}} saat",
|
||||
"{{value}} hours": "{{value}} saat",
|
||||
"CJK Font": "CJK Yazı Tipi",
|
||||
"Clear Search": "Aramayı Temizle"
|
||||
"Clear Search": "Aramayı Temizle",
|
||||
"Header & Footer": "Başlık ve Altbilgi",
|
||||
"Apply also in Scrolled Mode": "Kaydırılmış Modda da Uygula"
|
||||
}
|
||||
|
||||
@@ -274,5 +274,7 @@
|
||||
"{{value}} hour": "{{value}} година",
|
||||
"{{value}} hours": "{{value}} години",
|
||||
"CJK Font": "CJK шрифт",
|
||||
"Clear Search": "Очистити пошук"
|
||||
"Clear Search": "Очистити пошук",
|
||||
"Header & Footer": "Заголовок і нижній колонтитул",
|
||||
"Apply also in Scrolled Mode": "Застосувати також у прокрученому режимі"
|
||||
}
|
||||
|
||||
@@ -274,5 +274,7 @@
|
||||
"{{value}} hour": "{{value}} giờ",
|
||||
"{{value}} hours": "{{value}} giờ",
|
||||
"CJK Font": "Phông chữ CJK",
|
||||
"Clear Search": "Xóa tìm kiếm"
|
||||
"Clear Search": "Xóa tìm kiếm",
|
||||
"Header & Footer": "Đầu trang & Chân trang",
|
||||
"Apply also in Scrolled Mode": "Áp dụng cũng trong chế độ cuộn"
|
||||
}
|
||||
|
||||
@@ -274,5 +274,7 @@
|
||||
"{{value}} hour": "{{value}}小时",
|
||||
"{{value}} hours": "{{value}}小时",
|
||||
"CJK Font": "中文字体",
|
||||
"Clear Search": "清除搜索"
|
||||
"Clear Search": "清除搜索",
|
||||
"Header & Footer": "页眉页脚",
|
||||
"Apply also in Scrolled Mode": "应用到滚动模式"
|
||||
}
|
||||
|
||||
@@ -274,5 +274,7 @@
|
||||
"{{value}} hour": "{{value}}小時",
|
||||
"{{value}} hours": "{{value}}小時",
|
||||
"CJK Font": "中文字體",
|
||||
"Clear Search": "清除搜索"
|
||||
"Clear Search": "清除搜索",
|
||||
"Header & Footer": "頁眉頁腳",
|
||||
"Apply also in Scrolled Mode": "滾動模式同樣適用"
|
||||
}
|
||||
|
||||
@@ -42,10 +42,7 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
'grid h-full flex-grow',
|
||||
appService?.hasSafeAreaInset && 'pt-[env(safe-area-inset-top)]',
|
||||
)}
|
||||
className={clsx('grid h-full flex-grow')}
|
||||
style={{
|
||||
gridTemplateColumns: gridTemplate.columns,
|
||||
gridTemplateRows: gridTemplate.rows,
|
||||
@@ -63,6 +60,10 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
|
||||
const isBookmarked = getViewState(bookKey)?.ribbonVisible;
|
||||
const horizontalGapPercent = viewSettings.gapPercent;
|
||||
const verticalMarginPixels = viewSettings.marginPx;
|
||||
const scrolled = viewSettings.scrolled;
|
||||
const showBarsOnScroll = viewSettings.showBarsOnScroll;
|
||||
const showHeader = viewSettings.showHeader && (scrolled ? showBarsOnScroll : true);
|
||||
const showFooter = viewSettings.showFooter && (scrolled ? showBarsOnScroll : true);
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -70,6 +71,10 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
|
||||
key={bookKey}
|
||||
className={clsx(
|
||||
'relative h-full w-full overflow-hidden',
|
||||
index === 0 &&
|
||||
!viewSettings?.scrolled &&
|
||||
appService?.hasSafeAreaInset &&
|
||||
'pt-[env(safe-area-inset-top)]',
|
||||
!isSideBarVisible && appService?.hasRoundedWindow && 'rounded-window',
|
||||
)}
|
||||
>
|
||||
@@ -85,32 +90,36 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
|
||||
<FoliateViewer bookKey={bookKey} bookDoc={bookDoc} config={config} />
|
||||
{viewSettings.vertical && viewSettings.scrolled && (
|
||||
<>
|
||||
<div
|
||||
className='bg-base-100 absolute left-0 top-0 h-full'
|
||||
style={{
|
||||
width: `calc(${horizontalGapPercent}%)`,
|
||||
height: `calc(100% - ${verticalMarginPixels}px)`,
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className='bg-base-100 absolute right-0 top-0 h-full'
|
||||
style={{
|
||||
width: `calc(${horizontalGapPercent}%)`,
|
||||
height: `calc(100% - ${verticalMarginPixels}px)`,
|
||||
}}
|
||||
/>
|
||||
{(showFooter || viewSettings.doubleBorder) && (
|
||||
<div
|
||||
className='bg-base-100 absolute left-0 top-0 h-full'
|
||||
style={{
|
||||
width: `calc(${horizontalGapPercent}%)`,
|
||||
height: `calc(100% - ${verticalMarginPixels}px)`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{(showHeader || viewSettings.doubleBorder) && (
|
||||
<div
|
||||
className='bg-base-100 absolute right-0 top-0 h-full'
|
||||
style={{
|
||||
width: `calc(${horizontalGapPercent}%)`,
|
||||
height: `calc(100% - ${verticalMarginPixels}px)`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{viewSettings.vertical && viewSettings.doubleBorder && (
|
||||
<DoubleBorder
|
||||
showHeader={viewSettings.showHeader}
|
||||
showFooter={viewSettings.showFooter}
|
||||
showHeader={showHeader}
|
||||
showFooter={showFooter}
|
||||
borderColor={viewSettings.borderColor}
|
||||
horizontalGap={horizontalGapPercent}
|
||||
verticalMargin={verticalMarginPixels}
|
||||
/>
|
||||
)}
|
||||
{viewSettings.showHeader && (
|
||||
{showHeader && (
|
||||
<SectionInfo
|
||||
section={sectionLabel}
|
||||
showDoubleBorder={viewSettings.vertical && viewSettings.doubleBorder}
|
||||
@@ -127,7 +136,7 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
|
||||
horizontalGap={horizontalGapPercent}
|
||||
verticalMargin={verticalMarginPixels}
|
||||
/>
|
||||
{viewSettings.showFooter && (
|
||||
{showFooter && (
|
||||
<PageInfoView
|
||||
bookFormat={book.format}
|
||||
section={section}
|
||||
|
||||
@@ -149,7 +149,7 @@ const FooterBar: React.FC<FooterBarProps> = ({
|
||||
className={clsx(
|
||||
'footer-bar shadow-xs absolute bottom-0 z-50 flex w-full flex-col',
|
||||
'sm:h-[52px] sm:justify-center',
|
||||
'sm:bg-base-100 border-base-300/50 border sm:border-none',
|
||||
'sm:bg-base-100 border-base-300/50 border-t sm:border-none',
|
||||
'transition-[opacity,transform] duration-300',
|
||||
appService?.hasRoundedWindow && 'rounded-window-bottom-right',
|
||||
!isSideBarVisible && appService?.hasRoundedWindow && 'rounded-window-bottom-left',
|
||||
|
||||
@@ -42,7 +42,7 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
|
||||
cleanupTrafficLightListeners,
|
||||
} = useTrafficLightStore();
|
||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||
const { hoveredBookKey, setHoveredBookKey, bookKeys } = useReaderStore();
|
||||
const { bookKeys, hoveredBookKey, setHoveredBookKey } = useReaderStore();
|
||||
const { isSideBarVisible } = useSidebarStore();
|
||||
const iconSize16 = useResponsiveSize(16);
|
||||
|
||||
@@ -70,56 +70,78 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isSideBarVisible]);
|
||||
|
||||
const isVisible = hoveredBookKey === bookKey || isDropdownOpen;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={headerRef}
|
||||
className={clsx(
|
||||
`header-bar absolute top-0 z-10 flex h-11 w-full items-center pr-4`,
|
||||
isTrafficLightVisible && isTopLeft && !isSideBarVisible ? 'pl-16' : 'pl-4',
|
||||
`shadow-xs bg-base-100 transition-opacity duration-300`,
|
||||
appService?.hasRoundedWindow && 'rounded-window-top-right',
|
||||
!isSideBarVisible && appService?.hasRoundedWindow && 'rounded-window-top-left',
|
||||
isHoveredAnim && 'hover-bar-anim',
|
||||
hoveredBookKey === bookKey || isDropdownOpen ? `visible` : `opacity-0`,
|
||||
isDropdownOpen && 'header-bar-pinned',
|
||||
'bg-base-100 absolute top-0 w-full',
|
||||
appService?.hasSafeAreaInset && 'pt-[env(safe-area-inset-top)]',
|
||||
)}
|
||||
onMouseEnter={() => setHoveredBookKey(bookKey)}
|
||||
onMouseLeave={() => setHoveredBookKey('')}
|
||||
>
|
||||
<div className='sidebar-bookmark-toggler bg-base-100 z-20 flex h-full items-center gap-x-4'>
|
||||
<div className='hidden sm:flex'>
|
||||
<SidebarToggler bookKey={bookKey} />
|
||||
<div
|
||||
className={clsx('absolute top-0 z-10 hidden h-11 w-full sm:flex')}
|
||||
onMouseEnter={() => !appService?.isMobile && setHoveredBookKey(bookKey)}
|
||||
onTouchStart={() => !appService?.isMobile && setHoveredBookKey(bookKey)}
|
||||
/>
|
||||
<div
|
||||
className={clsx(
|
||||
'bg-base-100 absolute left-0 right-0 top-0 z-10 h-[env(safe-area-inset-top)]',
|
||||
isVisible ? 'visible' : 'hidden',
|
||||
)}
|
||||
/>
|
||||
<div
|
||||
ref={headerRef}
|
||||
className={clsx(
|
||||
`header-bar bg-base-100 absolute top-0 z-10 flex h-11 w-full items-center pr-4`,
|
||||
`shadow-xs transition-opacity duration-300`,
|
||||
isTrafficLightVisible && isTopLeft && !isSideBarVisible ? 'pl-16' : 'pl-4',
|
||||
appService?.hasRoundedWindow && 'rounded-window-top-right',
|
||||
appService?.hasSafeAreaInset && 'mt-[env(safe-area-inset-top)]',
|
||||
!isSideBarVisible && appService?.hasRoundedWindow && 'rounded-window-top-left',
|
||||
isHoveredAnim && 'hover-bar-anim',
|
||||
isVisible ? 'pointer-events-auto visible' : 'pointer-events-none opacity-0',
|
||||
isDropdownOpen && 'header-bar-pinned',
|
||||
)}
|
||||
onMouseLeave={() => !appService?.isMobile && setHoveredBookKey('')}
|
||||
>
|
||||
<div className='sidebar-bookmark-toggler z-20 flex h-full items-center gap-x-4'>
|
||||
<div className='hidden sm:flex'>
|
||||
<SidebarToggler bookKey={bookKey} />
|
||||
</div>
|
||||
<BookmarkToggler bookKey={bookKey} />
|
||||
</div>
|
||||
<BookmarkToggler bookKey={bookKey} />
|
||||
</div>
|
||||
|
||||
<div className='header-title z-15 pointer-events-none absolute inset-0 hidden items-center justify-center sm:flex'>
|
||||
<h2 className='line-clamp-1 max-w-[50%] text-center text-xs font-semibold'>{bookTitle}</h2>
|
||||
</div>
|
||||
<div className='header-title z-15 bg-base-100 pointer-events-none absolute inset-0 hidden items-center justify-center sm:flex'>
|
||||
<h2 className='line-clamp-1 max-w-[50%] text-center text-xs font-semibold'>
|
||||
{bookTitle}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className='bg-base-100 z-20 ml-auto flex h-full items-center space-x-4'>
|
||||
<SettingsToggler />
|
||||
<NotebookToggler bookKey={bookKey} />
|
||||
<Dropdown
|
||||
className='exclude-title-bar-mousedown dropdown-bottom dropdown-end'
|
||||
buttonClassName='btn btn-ghost h-8 min-h-8 w-8 p-0'
|
||||
toggleButton={<PiDotsThreeVerticalBold size={iconSize16} />}
|
||||
onToggle={handleToggleDropdown}
|
||||
>
|
||||
<ViewMenu bookKey={bookKey} onSetSettingsDialogOpen={onSetSettingsDialogOpen} />
|
||||
</Dropdown>
|
||||
<div className='bg-base-100 z-20 ml-auto flex h-full items-center space-x-4'>
|
||||
<SettingsToggler />
|
||||
<NotebookToggler bookKey={bookKey} />
|
||||
<Dropdown
|
||||
className='exclude-title-bar-mousedown dropdown-bottom dropdown-end'
|
||||
buttonClassName='btn btn-ghost h-8 min-h-8 w-8 p-0'
|
||||
toggleButton={<PiDotsThreeVerticalBold size={iconSize16} />}
|
||||
onToggle={handleToggleDropdown}
|
||||
>
|
||||
<ViewMenu bookKey={bookKey} onSetSettingsDialogOpen={onSetSettingsDialogOpen} />
|
||||
</Dropdown>
|
||||
|
||||
<WindowButtons
|
||||
className='window-buttons flex h-full items-center'
|
||||
headerRef={headerRef}
|
||||
showMinimize={
|
||||
bookKeys.length == 1 && !isTrafficLightVisible && appService?.appPlatform !== 'web'
|
||||
}
|
||||
showMaximize={
|
||||
bookKeys.length == 1 && !isTrafficLightVisible && appService?.appPlatform !== 'web'
|
||||
}
|
||||
onClose={() => onCloseBook(bookKey)}
|
||||
/>
|
||||
<WindowButtons
|
||||
className='window-buttons flex h-full items-center'
|
||||
headerRef={headerRef}
|
||||
showMinimize={
|
||||
bookKeys.length == 1 && !isTrafficLightVisible && appService?.appPlatform !== 'web'
|
||||
}
|
||||
showMaximize={
|
||||
bookKeys.length == 1 && !isTrafficLightVisible && appService?.appPlatform !== 'web'
|
||||
}
|
||||
onClose={() => onCloseBook(bookKey)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -45,26 +45,35 @@ const HintInfo: React.FC<SectionInfoProps> = ({
|
||||
}, [hintMessage]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
'hintinfo absolute flex items-center justify-end overflow-hidden',
|
||||
hintMessage ? 'bg-base-100' : 'bg-transparent',
|
||||
isVertical ? 'writing-vertical-rl max-h-[50%]' : 'top-0 h-[44px] max-w-[50%]',
|
||||
)}
|
||||
style={
|
||||
isVertical
|
||||
? {
|
||||
bottom: `${verticalMargin * 1.5}px`,
|
||||
left: `calc(100% - ${horizontalGap}%)`,
|
||||
width: showDoubleBorder ? '30px' : `${horizontalGap}%`,
|
||||
}
|
||||
: { insetInlineEnd: `${horizontalGap}%` }
|
||||
}
|
||||
>
|
||||
<h2 className={clsx('text-neutral-content text-center font-sans text-xs font-light')}>
|
||||
{hintMessage || ''}
|
||||
</h2>
|
||||
</div>
|
||||
<>
|
||||
<div
|
||||
className={clsx(
|
||||
'absolute left-0 right-0 top-0 z-10 h-[env(safe-area-inset-top)]',
|
||||
hintMessage ? 'bg-base-100' : 'bg-transparent',
|
||||
)}
|
||||
/>
|
||||
<div
|
||||
className={clsx(
|
||||
'hintinfo absolute flex items-center justify-end overflow-hidden ps-2',
|
||||
'mt-[env(safe-area-inset-top)]',
|
||||
hintMessage ? 'bg-base-100' : 'bg-transparent',
|
||||
isVertical ? 'writing-vertical-rl max-h-[50%]' : 'top-0 h-[44px] max-w-[50%]',
|
||||
)}
|
||||
style={
|
||||
isVertical
|
||||
? {
|
||||
bottom: `${verticalMargin * 1.5}px`,
|
||||
left: `calc(100% - ${horizontalGap}%)`,
|
||||
width: showDoubleBorder ? '30px' : `${horizontalGap}%`,
|
||||
}
|
||||
: { insetInlineEnd: `${horizontalGap}%` }
|
||||
}
|
||||
>
|
||||
<h2 className={clsx('text-neutral-content text-center font-sans text-xs font-light')}>
|
||||
{hintMessage || ''}
|
||||
</h2>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -57,7 +57,9 @@ const PageInfoView: React.FC<PageInfoProps> = ({
|
||||
}
|
||||
: {
|
||||
insetInlineEnd: `${horizontalGap}%`,
|
||||
paddingBottom: appService?.hasSafeAreaInset ? 'env(safe-area-inset-bottom)' : 0,
|
||||
paddingBottom: appService?.hasSafeAreaInset
|
||||
? 'calc(env(safe-area-inset-bottom)*0.67)'
|
||||
: 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
@@ -19,32 +19,41 @@ const SectionInfo: React.FC<SectionInfoProps> = ({
|
||||
verticalMargin,
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
'sectioninfo absolute flex items-center overflow-hidden',
|
||||
isVertical ? 'writing-vertical-rl max-h-[85%]' : 'top-0 h-[44px]',
|
||||
isScrolled && !isVertical && 'bg-base-100',
|
||||
)}
|
||||
style={
|
||||
isVertical
|
||||
? {
|
||||
top: `${verticalMargin * 1.5}px`,
|
||||
left: `calc(100% - ${horizontalGap}%)`,
|
||||
width: showDoubleBorder ? '32px' : `${horizontalGap}%`,
|
||||
height: `calc(100% - ${verticalMargin * 2}px)`,
|
||||
}
|
||||
: { insetInlineStart: `${horizontalGap}%`, width: `calc(100% - ${horizontalGap * 2}%)` }
|
||||
}
|
||||
>
|
||||
<h2
|
||||
<>
|
||||
<div
|
||||
className={clsx(
|
||||
'text-neutral-content text-center font-sans text-xs font-light',
|
||||
isVertical ? '' : 'line-clamp-1',
|
||||
'absolute left-0 right-0 top-0 z-10 h-[env(safe-area-inset-top)]',
|
||||
isScrolled && !isVertical && 'bg-base-100',
|
||||
)}
|
||||
/>
|
||||
<div
|
||||
className={clsx(
|
||||
'sectioninfo absolute flex items-center overflow-hidden',
|
||||
'mt-[env(safe-area-inset-top)]',
|
||||
isVertical ? 'writing-vertical-rl max-h-[85%]' : 'top-0 h-[44px]',
|
||||
isScrolled && !isVertical && 'bg-base-100',
|
||||
)}
|
||||
style={
|
||||
isVertical
|
||||
? {
|
||||
top: `${verticalMargin * 1.5}px`,
|
||||
left: `calc(100% - ${horizontalGap}%)`,
|
||||
width: showDoubleBorder ? '32px' : `${horizontalGap}%`,
|
||||
height: `calc(100% - ${verticalMargin * 2}px)`,
|
||||
}
|
||||
: { insetInlineStart: `${horizontalGap}%`, width: `calc(100% - ${horizontalGap * 2}%)` }
|
||||
}
|
||||
>
|
||||
{section || ''}
|
||||
</h2>
|
||||
</div>
|
||||
<h2
|
||||
className={clsx(
|
||||
'text-neutral-content text-center font-sans text-xs font-light',
|
||||
isVertical ? '' : 'line-clamp-1',
|
||||
)}
|
||||
>
|
||||
{section || ''}
|
||||
</h2>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
const [borderColor, setBorderColor] = useState(viewSettings.borderColor!);
|
||||
const [showHeader, setShowHeader] = useState(viewSettings.showHeader!);
|
||||
const [showFooter, setShowFooter] = useState(viewSettings.showFooter!);
|
||||
const [showBarsOnScroll, setShowBarsOnScroll] = useState(viewSettings.showBarsOnScroll!);
|
||||
|
||||
useEffect(() => {
|
||||
saveViewSettings(envConfig, bookKey, 'paragraphMargin', paragraphMargin);
|
||||
@@ -196,6 +197,11 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [showHeader]);
|
||||
|
||||
useEffect(() => {
|
||||
saveViewSettings(envConfig, bookKey, 'showBarsOnScroll', showBarsOnScroll, false, false);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [showBarsOnScroll]);
|
||||
|
||||
useEffect(() => {
|
||||
if (showFooter === viewSettings.showFooter) return;
|
||||
if (showFooter && !viewSettings.vertical) {
|
||||
@@ -391,24 +397,6 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
<h2 className='mb-2 font-medium'>{_('Page')}</h2>
|
||||
<div className='card bg-base-100 border-base-200 border shadow'>
|
||||
<div className='divide-base-200 divide-y'>
|
||||
<div className='config-item'>
|
||||
<span className=''>{_('Show Header')}</span>
|
||||
<input
|
||||
type='checkbox'
|
||||
className='toggle'
|
||||
checked={showHeader}
|
||||
onChange={() => setShowHeader(!showHeader)}
|
||||
/>
|
||||
</div>
|
||||
<div className='config-item'>
|
||||
<span className=''>{_('Show Footer')}</span>
|
||||
<input
|
||||
type='checkbox'
|
||||
className='toggle'
|
||||
checked={showFooter}
|
||||
onChange={() => setShowFooter(!showFooter)}
|
||||
/>
|
||||
</div>
|
||||
<NumberInput
|
||||
label={_('Vertical Margins (px)')}
|
||||
value={marginPx}
|
||||
@@ -456,6 +444,41 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='w-full'>
|
||||
<h2 className='mb-2 font-medium'>{_('Header & Footer')}</h2>
|
||||
<div className='card bg-base-100 border-base-200 border shadow'>
|
||||
<div className='divide-base-200 divide-y'>
|
||||
<div className='config-item'>
|
||||
<span className=''>{_('Show Header')}</span>
|
||||
<input
|
||||
type='checkbox'
|
||||
className='toggle'
|
||||
checked={showHeader}
|
||||
onChange={() => setShowHeader(!showHeader)}
|
||||
/>
|
||||
</div>
|
||||
<div className='config-item'>
|
||||
<span className=''>{_('Show Footer')}</span>
|
||||
<input
|
||||
type='checkbox'
|
||||
className='toggle'
|
||||
checked={showFooter}
|
||||
onChange={() => setShowFooter(!showFooter)}
|
||||
/>
|
||||
</div>
|
||||
<div className='config-item'>
|
||||
<span className=''>{_('Apply also in Scrolled Mode')}</span>
|
||||
<input
|
||||
type='checkbox'
|
||||
className='toggle'
|
||||
checked={showBarsOnScroll}
|
||||
onChange={() => setShowBarsOnScroll(!showBarsOnScroll)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -31,7 +31,10 @@ export const useClickEvent = (
|
||||
if (appService?.isMobile) {
|
||||
windowStartX = 0;
|
||||
} else {
|
||||
const windowPosition = await tauriGetWindowLogicalPosition();
|
||||
const windowPosition = (await tauriGetWindowLogicalPosition()) as {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
windowStartX = windowPosition.x;
|
||||
}
|
||||
} else {
|
||||
@@ -52,6 +55,7 @@ export const useClickEvent = (
|
||||
} else {
|
||||
if (hoveredBookKey) {
|
||||
setHoveredBookKey(null);
|
||||
return;
|
||||
}
|
||||
if (!viewSettings.disableClick! && screenX >= viewCenterX) {
|
||||
if (viewSettings.swapClickArea) {
|
||||
|
||||
@@ -90,6 +90,7 @@ export const DEFAULT_BOOK_LAYOUT: BookLayout = {
|
||||
borderColor: 'red',
|
||||
showHeader: true,
|
||||
showFooter: true,
|
||||
showBarsOnScroll: false,
|
||||
};
|
||||
|
||||
export const DEFAULT_BOOK_STYLE: BookStyle = {
|
||||
|
||||
@@ -82,6 +82,7 @@ export interface BookLayout {
|
||||
borderColor: string;
|
||||
showHeader: boolean;
|
||||
showFooter: boolean;
|
||||
showBarsOnScroll: boolean;
|
||||
}
|
||||
|
||||
export interface BookStyle {
|
||||
|
||||
@@ -2,11 +2,12 @@ import { ViewSettings } from '@/types/book';
|
||||
|
||||
export const getMaxInlineSize = (viewSettings: ViewSettings) => {
|
||||
const isScrolled = viewSettings.scrolled!;
|
||||
const isVertical = viewSettings.vertical!;
|
||||
const maxColumnCount = viewSettings.maxColumnCount!;
|
||||
const screenWidth = window.innerWidth;
|
||||
const screenHeight = window.innerHeight;
|
||||
|
||||
return maxColumnCount === 1 || isScrolled
|
||||
return maxColumnCount === 1 || isScrolled || isVertical
|
||||
? Math.max(screenWidth, screenHeight, 720)
|
||||
: viewSettings.maxInlineSize!;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user