feat: add border frame for vertical layout, also closes #563 (#612)

This commit is contained in:
Huang Xin
2025-03-16 01:55:12 +08:00
committed by GitHub
parent c66911b060
commit 58f1b5833f
29 changed files with 304 additions and 66 deletions
@@ -57,7 +57,6 @@
"Layout": "التخطيط",
"Light Mode": "الوضع الفاتح",
"Loading...": "جارٍ التحميل...",
"Loc. {{currentPage}} / {{totalPage}}": "الصفحة {{currentPage}} \\ {{totalPage}}",
"Logged in": "تم تسجيل الدخول",
"Logged in as {{userDisplayName}}": "تم تسجيل الدخول باسم {{userDisplayName}}",
"Match Case": "مطابقة حالة الأحرف",
@@ -243,5 +242,7 @@
"Background Color": "لون الخلفية",
"Preview": "معاينة",
"Contrast": "التباين",
"Sunset": "غروب الشمس"
"Sunset": "غروب الشمس",
"Double Border": "حدود مزدوجة",
"Border Color": "لون الحدود"
}
@@ -57,7 +57,6 @@
"Layout": "Layout",
"Light Mode": "Hellmodus",
"Loading...": "Wird geladen...",
"Loc. {{currentPage}} / {{totalPage}}": "Pos. {{currentPage}} / {{totalPage}}",
"Logged in": "Angemeldet",
"Logged in as {{userDisplayName}}": "Angemeldet als {{userDisplayName}}",
"Match Case": "Groß-/Kleinschreibung beachten",
@@ -243,5 +242,7 @@
"Background Color": "Hintergrundfarbe",
"Preview": "Vorschau",
"Contrast": "Kontrast",
"Sunset": "Sonnenuntergang"
"Sunset": "Sonnenuntergang",
"Double Border": "Doppelter Rand",
"Border Color": "Randfarbe"
}
@@ -57,7 +57,6 @@
"Layout": "Διάταξη",
"Light Mode": "Φωτεινή λειτουργία",
"Loading...": "Φόρτωση...",
"Loc. {{currentPage}} / {{totalPage}}": "Θέση {{currentPage}} / {{totalPage}}",
"Logged in": "Συνδεδεμένος",
"Logged in as {{userDisplayName}}": "Συνδεδεμένος ως {{userDisplayName}}",
"Match Case": "Ταίριασμα πεζών-κεφαλαίων",
@@ -243,5 +242,7 @@
"Background Color": "Χρώμα φόντου",
"Preview": "Προεπισκόπηση",
"Contrast": "Αντίθεση",
"Sunset": "Ηλιοβασίλεμα"
"Sunset": "Ηλιοβασίλεμα",
"Double Border": "Διπλό περιθώριο",
"Border Color": "Χρώμα περιγράμματος"
}
@@ -57,7 +57,6 @@
"Layout": "Diseño",
"Light Mode": "Modo claro",
"Loading...": "Cargando...",
"Loc. {{currentPage}} / {{totalPage}}": "Pos. {{currentPage}} / {{totalPage}}",
"Logged in": "Sesión iniciada",
"Logged in as {{userDisplayName}}": "Sesión iniciada como {{userDisplayName}}",
"Match Case": "Coincidir mayúsculas y minúsculas",
@@ -243,5 +242,7 @@
"Background Color": "Color de fondo",
"Preview": "Vista previa",
"Contrast": "Contraste",
"Sunset": "Atardecer"
"Sunset": "Atardecer",
"Double Border": "Doble borde",
"Border Color": "Color del borde"
}
@@ -57,7 +57,6 @@
"Layout": "Mise en page",
"Light Mode": "Mode clair",
"Loading...": "Chargement...",
"Loc. {{currentPage}} / {{totalPage}}": "Pos. {{currentPage}} / {{totalPage}}",
"Logged in": "Connecté",
"Logged in as {{userDisplayName}}": "Connecté en tant que {{userDisplayName}}",
"Match Case": "Respecter la casse",
@@ -243,5 +242,7 @@
"Background Color": "Couleur de fond",
"Preview": "Aperçu",
"Contrast": "Contraste",
"Sunset": "Coucher de soleil"
"Sunset": "Coucher de soleil",
"Double Border": "Double bordure",
"Border Color": "Couleur de la bordure"
}
@@ -57,7 +57,6 @@
"Layout": "लेआउट",
"Light Mode": "लाइट मोड",
"Loading...": "लोड हो रहा है...",
"Loc. {{currentPage}} / {{totalPage}}": "स्थान {{currentPage}} / {{totalPage}}",
"Logged in": "लॉग इन किया",
"Logged in as {{userDisplayName}}": "{{userDisplayName}} के रूप में लॉग इन किया",
"Match Case": "केस मैच करें",
@@ -243,5 +242,7 @@
"Background Color": "पृष्ठभूमि रंग",
"Preview": "पूर्वावलोकन",
"Contrast": "विरोधाभास",
"Sunset": "सनसेट"
"Sunset": "सनसेट",
"Double Border": "डबल बॉर्डर",
"Border Color": "बॉर्डर रंग"
}
@@ -57,7 +57,6 @@
"Layout": "Tata Letak",
"Light Mode": "Mode Terang",
"Loading...": "Memuat...",
"Loc. {{currentPage}} / {{totalPage}}": "Lok. {{currentPage}} / {{totalPage}}",
"Logged in": "Sudah Masuk",
"Logged in as {{userDisplayName}}": "Masuk sebagai {{userDisplayName}}",
"Match Case": "Cocokkan Huruf Besar/Kecil",
@@ -243,5 +242,7 @@
"Background Color": "Warna Latar Belakang",
"Preview": "Pratinjau",
"Contrast": "Kontras",
"Sunset": "Senja"
"Sunset": "Senja",
"Double Border": "Garis Ganda",
"Border Color": "Warna Garis"
}
@@ -57,7 +57,6 @@
"Layout": "Layout",
"Light Mode": "Modalità chiara",
"Loading...": "Caricamento...",
"Loc. {{currentPage}} / {{totalPage}}": "Pos. {{currentPage}} / {{totalPage}}",
"Logged in": "Accesso effettuato",
"Logged in as {{userDisplayName}}": "Accesso effettuato come {{userDisplayName}}",
"Match Case": "Maiuscole/minuscole",
@@ -243,5 +242,7 @@
"Background Color": "Colore sfondo",
"Preview": "Anteprima",
"Contrast": "Contrasto",
"Sunset": "Tramonto"
"Sunset": "Tramonto",
"Double Border": "Doppio bordo",
"Border Color": "Colore bordo"
}
@@ -57,7 +57,6 @@
"Layout": "レイアウト",
"Light Mode": "ライトモード",
"Loading...": "読み込み中...",
"Loc. {{currentPage}} / {{totalPage}}": "位置 {{currentPage}} / {{totalPage}}",
"Logged in": "ログイン済み",
"Logged in as {{userDisplayName}}": "{{userDisplayName}}としてログイン中",
"Match Case": "大文字・小文字を区別",
@@ -243,5 +242,7 @@
"Background Color": "背景色",
"Preview": "プレビュー",
"Contrast": "コントラスト",
"Sunset": "サンセット"
"Sunset": "サンセット",
"Double Border": "二重枠",
"Border Color": "ボーダーカラー"
}
@@ -57,7 +57,6 @@
"Layout": "레이아웃",
"Light Mode": "라이트 모드",
"Loading...": "로딩 중...",
"Loc. {{currentPage}} / {{totalPage}}": "위치 {{currentPage}} / {{totalPage}}",
"Logged in": "로그인됨",
"Logged in as {{userDisplayName}}": "{{userDisplayName}}(으)로 로그인됨",
"Match Case": "대소문자 구분",
@@ -243,5 +242,7 @@
"Background Color": "배경 색상",
"Preview": "미리보기",
"Contrast": "대비",
"Sunset": "일몰"
"Sunset": "일몰",
"Double Border": "이중 테두리",
"Border Color": "테두리 색상"
}
@@ -57,7 +57,6 @@
"Layout": "Układ",
"Light Mode": "Tryb jasny",
"Loading...": "Ładowanie...",
"Loc. {{currentPage}} / {{totalPage}}": "Poz. {{currentPage}} / {{totalPage}}",
"Logged in": "Zalogowano",
"Logged in as {{userDisplayName}}": "Zalogowano jako {{userDisplayName}}",
"Match Case": "Uwzględnij wielkość liter",
@@ -243,5 +242,7 @@
"Background Color": "Kolor tła",
"Preview": "Podgląd",
"Contrast": "Kontrast",
"Sunset": "Zachód słońca"
"Sunset": "Zachód słońca",
"Double Border": "Podwójna ramka",
"Border Color": "Kolor ramki"
}
@@ -57,7 +57,6 @@
"Layout": "Layout",
"Light Mode": "Modo Claro",
"Loading...": "Carregando...",
"Loc. {{currentPage}} / {{totalPage}}": "Loc. {{currentPage}} / {{totalPage}}",
"Logged in": "Conectado",
"Logged in as {{userDisplayName}}": "Conectado como {{userDisplayName}}",
"Match Case": "Diferenciar Maiúsculas e Minúsculas",
@@ -243,5 +242,7 @@
"Background Color": "Cor de Fundo",
"Preview": "Visualizar",
"Contrast": "Contraste",
"Sunset": "Pôr do Sol"
"Sunset": "Pôr do Sol",
"Double Border": "Borda Dupla",
"Border Color": "Cor da Borda"
}
@@ -57,7 +57,6 @@
"Layout": "Макет",
"Light Mode": "Светлая тема",
"Loading...": "Загрузка...",
"Loc. {{currentPage}} / {{totalPage}}": "Поз. {{currentPage}} / {{totalPage}}",
"Logged in": "Выполнен вход",
"Logged in as {{userDisplayName}}": "Вход выполнен как {{userDisplayName}}",
"Match Case": "Учитывать регистр",
@@ -243,5 +242,7 @@
"Background Color": "Цвет фона",
"Preview": "Предпросмотр",
"Contrast": "Контраст",
"Sunset": "Закат"
"Sunset": "Закат",
"Double Border": "Двойная граница",
"Border Color": "Цвет границы"
}
@@ -57,7 +57,6 @@
"Layout": "Düzen",
"Light Mode": "Aydınlık Mod",
"Loading...": "Yükleniyor...",
"Loc. {{currentPage}} / {{totalPage}}": "Konum {{currentPage}} / {{totalPage}}",
"Logged in": "Giriş Yapıldı",
"Logged in as {{userDisplayName}}": "{{userDisplayName}} olarak giriş yapıldı",
"Match Case": "Büyük/Küçük Harf Eşleştir",
@@ -243,5 +242,7 @@
"Background Color": "Arka Plan Rengi",
"Preview": "Önizleme",
"Contrast": "Kontrast",
"Sunset": "Gün Batımı"
"Sunset": "Gün Batımı",
"Double Border": "Çift Sınır",
"Border Color": "Sınır Rengi"
}
@@ -57,7 +57,6 @@
"Layout": "Макет",
"Light Mode": "Світлий режим",
"Loading...": "Завантаження...",
"Loc. {{currentPage}} / {{totalPage}}": "Поз. {{currentPage}} / {{totalPage}}",
"Logged in": "Увійшли",
"Logged in as {{userDisplayName}}": "Увійшли як {{userDisplayName}}",
"Match Case": "Враховувати регістр",
@@ -243,5 +242,7 @@
"Background Color": "Колір фону",
"Preview": "Попередній перегляд",
"Contrast": "Контраст",
"Sunset": "Захід сонця"
"Sunset": "Захід сонця",
"Double Border": "Подвійна рамка",
"Border Color": "Колір рамки"
}
@@ -57,7 +57,6 @@
"Layout": "Bố cục",
"Light Mode": "Chế độ sáng",
"Loading...": "Đang tải...",
"Loc. {{currentPage}} / {{totalPage}}": "Vị trí {{currentPage}} / {{totalPage}}",
"Logged in": "Đã đăng nhập",
"Logged in as {{userDisplayName}}": "Đăng nhập với tên {{userDisplayName}}",
"Match Case": "Phân biệt chữ hoa/thường",
@@ -243,5 +242,7 @@
"Background Color": "Màu nền",
"Preview": "Xem trước",
"Contrast": "Tương phản",
"Sunset": "Hoàng hôn"
"Sunset": "Hoàng hôn",
"Double Border": "Đôi viền",
"Border Color": "Màu viền"
}
@@ -57,7 +57,6 @@
"Layout": "布局",
"Light Mode": "浅色主题",
"Loading...": "加载中...",
"Loc. {{currentPage}} / {{totalPage}}": "位置 {{currentPage}} / {{totalPage}}",
"Logged in": "已登录",
"Logged in as {{userDisplayName}}": "{{userDisplayName}} 已登录",
"Match Case": "匹配大小写",
@@ -243,5 +242,7 @@
"Background Color": "背景颜色",
"Preview": "预览",
"Contrast": "对比",
"Sunset": "日落"
"Sunset": "日落",
"Double Border": "页面版框",
"Border Color": "版框颜色"
}
@@ -57,7 +57,6 @@
"Layout": "版面",
"Light Mode": "淺色主題",
"Loading...": "載入中...",
"Loc. {{currentPage}} / {{totalPage}}": "位置 {{currentPage}} / {{totalPage}}",
"Logged in": "已登入",
"Logged in as {{userDisplayName}}": "{{userDisplayName}} 已登入",
"Match Case": "匹配大小寫",
@@ -243,5 +242,7 @@
"Background Color": "背景顏色",
"Preview": "預覽",
"Contrast": "對比",
"Sunset": "日落"
"Sunset": "日落",
"Double Border": "頁面邊框",
"Border Color": "邊框顏色"
}
@@ -17,6 +17,7 @@ import SettingsDialog from './settings/SettingsDialog';
import Annotator from './annotator/Annotator';
import FootnotePopup from './FootnotePopup';
import HintInfo from './HintInfo';
import DoubleBorder from './DoubleBorder';
interface BooksGridProps {
bookKeys: string[];
@@ -60,7 +61,8 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
const { section, pageinfo, sectionLabel } = progress || {};
const isBookmarked = getViewState(bookKey)?.ribbonVisible;
const marginGap = `${viewSettings.gapPercent}%`;
const horizontalGapPercent = viewSettings.gapPercent;
const verticalMarginPixels = viewSettings.marginPx;
return (
<div
@@ -68,7 +70,7 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
key={bookKey}
className={`${appService?.hasRoundedWindow ? 'rounded-window' : ''} relative h-full w-full overflow-hidden`}
>
{isBookmarked && <Ribbon width={marginGap} />}
{isBookmarked && <Ribbon width={`${horizontalGapPercent}%`} />}
<HeaderBar
bookKey={bookKey}
bookTitle={book.title}
@@ -81,13 +83,32 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
<FootnotePopup bookKey={bookKey} bookDoc={bookDoc} />
{viewSettings.scrolled ? null : (
<>
<SectionInfo section={sectionLabel} gapLeft={marginGap} />
<HintInfo bookKey={bookKey} gapRight={marginGap} />
{viewSettings.vertical && viewSettings.doubleBorder && (
<DoubleBorder
borderColor={viewSettings.borderColor}
horizontalGap={horizontalGapPercent}
verticalMargin={verticalMarginPixels}
/>
)}
<SectionInfo
section={sectionLabel}
isVertical={viewSettings.vertical}
horizontalGap={horizontalGapPercent}
verticalMargin={verticalMarginPixels}
/>
<HintInfo
bookKey={bookKey}
isVertical={viewSettings.vertical}
horizontalGap={horizontalGapPercent}
verticalMargin={verticalMarginPixels}
/>
<PageInfoView
bookFormat={book.format}
section={section}
pageinfo={pageinfo}
gapRight={marginGap}
isVertical={viewSettings.vertical}
horizontalGap={horizontalGapPercent}
verticalMargin={verticalMarginPixels}
/>
</>
)}
@@ -0,0 +1,62 @@
interface DoubleBorderProps {
borderColor: string;
horizontalGap: number;
verticalMargin: number;
}
const paddingPx = 10;
const DoubleBorder: React.FC<DoubleBorderProps> = ({
borderColor,
horizontalGap,
verticalMargin,
}) => {
return (
<>
<div
className={'borderframe pointer-events-none absolute'}
style={{
border: `4px solid ${borderColor}`,
height: `calc(100% - ${verticalMargin * 2}px + ${paddingPx * 2}px)`,
top: `calc(${verticalMargin}px - ${paddingPx}px)`,
left: `calc(${horizontalGap * 2}% - 32px - ${paddingPx}px)`,
right: `calc(${horizontalGap * 2}% - 32px - ${paddingPx}px)`,
}}
></div>
<div
className={'borderframe pointer-events-none absolute'}
style={{
border: `1px solid ${borderColor}`,
height: `calc(100% - ${verticalMargin * 2}px)`,
top: `${verticalMargin}px`,
left: `${horizontalGap * 2}%`,
right: `${horizontalGap * 2}%`,
}}
/>
<div
className={'borderframe pointer-events-none absolute'}
style={{
border: `1px solid ${borderColor}`,
width: '32px',
height: `calc(100% - ${verticalMargin * 2}px)`,
top: `${verticalMargin}px`,
left: `calc(${horizontalGap * 2}% - 32px)`,
borderRight: 'none',
}}
/>
<div
className={'borderframe pointer-events-none absolute'}
style={{
border: `1px solid ${borderColor}`,
width: '32px',
height: `calc(100% - ${verticalMargin * 2}px)`,
top: `${verticalMargin}px`,
left: `calc(100% - ${horizontalGap * 2}%)`,
borderLeft: 'none',
}}
/>
</>
);
};
export default DoubleBorder;
@@ -6,10 +6,17 @@ import { eventDispatcher } from '@/utils/event';
interface SectionInfoProps {
bookKey: string;
gapRight: string;
isVertical: boolean;
horizontalGap: number;
verticalMargin: number;
}
const HintInfo: React.FC<SectionInfoProps> = ({ bookKey, gapRight }) => {
const HintInfo: React.FC<SectionInfoProps> = ({
bookKey,
isVertical,
horizontalGap,
verticalMargin,
}) => {
const { isSideBarVisible } = useSidebarStore();
const { isTrafficLightVisible } = useTrafficLightStore();
const [hintMessage, setHintMessage] = React.useState<string | null>(null);
@@ -42,13 +49,26 @@ const HintInfo: React.FC<SectionInfoProps> = ({ bookKey, gapRight }) => {
return (
<div
className={clsx(
'pageinfo absolute right-0 top-0 flex max-w-[50%] items-end',
isTrafficLightVisible && !isSideBarVisible ? 'h-[44px]' : 'h-[30px]',
hintMessage ? '' : '',
'sectioninfo absolute flex justify-end overflow-hidden',
!isVertical && (isTrafficLightVisible && !isSideBarVisible ? 'h-[44px]' : 'h-[30px]'),
isVertical ? 'writing-vertical-rl w-[32px] items-center' : 'top-0 items-end',
isVertical ? 'max-h-[50%]' : 'max-w-[50%]',
)}
style={{ right: gapRight }}
style={
isVertical
? {
bottom: `calc(${horizontalGap / 2}% + ${verticalMargin}px)`,
left: `calc(100% - ${horizontalGap * 2}%)`,
height: `calc(100% - ${verticalMargin * 2}px)`,
}
: { right: `${horizontalGap}%` }
}
>
<h2 className='text-neutral-content line-clamp-1 text-center font-sans text-xs font-light'>
<h2
className={clsx(
'text-neutral-content line-clamp-1 text-center font-sans text-xs font-light',
)}
>
{hintMessage || ''}
</h2>
</div>
@@ -1,3 +1,4 @@
import clsx from 'clsx';
import React from 'react';
import { useTranslation } from '@/hooks/useTranslation';
import { PageInfo } from '@/types/book';
@@ -6,27 +7,53 @@ interface PageInfoProps {
bookFormat: string;
section?: PageInfo;
pageinfo?: PageInfo;
gapRight: string;
isVertical: boolean;
horizontalGap: number;
verticalMargin: number;
}
const PageInfoView: React.FC<PageInfoProps> = ({ bookFormat, section, pageinfo, gapRight }) => {
const PageInfoView: React.FC<PageInfoProps> = ({
bookFormat,
section,
pageinfo,
isVertical,
horizontalGap,
verticalMargin,
}) => {
const _ = useTranslation();
const pageInfo =
bookFormat === 'PDF'
? section
? `${section.current + 1} / ${section.total}`
? isVertical
? `${section.current + 1} · ${section.total}`
: `${section.current + 1} / ${section.total}`
: ''
: pageinfo
? _('Loc. {{currentPage}} / {{totalPage}}', {
currentPage: pageinfo.current + 1,
totalPage: pageinfo.total,
})
? _(
isVertical ? '{{currentPage}} · {{totalPage}}' : 'Loc. {{currentPage}} / {{totalPage}}',
{
currentPage: pageinfo.current + 1,
totalPage: pageinfo.total,
},
)
: '';
return (
<div
className='pageinfo absolute bottom-0 left-0 right-0 flex h-12 items-center justify-end'
style={{ paddingRight: gapRight }}
className={clsx(
'pageinfo absolute bottom-0 flex items-center justify-end',
isVertical ? 'left-0' : 'right-0',
isVertical ? 'writing-vertical-rl w-[32px]' : 'h-12',
)}
style={
isVertical
? {
bottom: `calc(${horizontalGap / 2}% + ${verticalMargin}px)`,
left: `calc(${horizontalGap * 2}% - 32px)`,
height: `calc(100% - ${verticalMargin * 2}px)`,
}
: { paddingRight: `${horizontalGap}%` }
}
>
<h2 className='text-neutral-content text-right font-sans text-xs font-extralight'>
{pageInfo}
@@ -6,21 +6,42 @@ import { useTrafficLightStore } from '@/store/trafficLightStore';
interface SectionInfoProps {
section?: string;
gapLeft: string;
isVertical: boolean;
horizontalGap: number;
verticalMargin: number;
}
const SectionInfo: React.FC<SectionInfoProps> = ({ section, gapLeft }) => {
const SectionInfo: React.FC<SectionInfoProps> = ({
section,
isVertical,
horizontalGap,
verticalMargin,
}) => {
const { isSideBarVisible } = useSidebarStore();
const { isTrafficLightVisible } = useTrafficLightStore();
return (
<div
className={clsx(
'pageinfo absolute right-0 top-0 flex max-w-[50%] items-end',
isTrafficLightVisible && !isSideBarVisible ? 'h-[44px]' : 'h-[30px]',
'sectioninfo absolute flex overflow-hidden',
!isVertical && (isTrafficLightVisible && !isSideBarVisible ? 'h-[44px]' : 'h-[30px]'),
isVertical ? 'writing-vertical-rl w-[32px] items-center' : 'top-0 items-end',
isVertical ? 'max-h-[50%]' : 'max-w-[50%]',
)}
style={{ left: gapLeft }}
style={
isVertical
? {
top: `calc(${horizontalGap / 2}% + ${verticalMargin}px)`,
left: `calc(100% - ${horizontalGap * 2}%)`,
height: `calc(100% - ${verticalMargin * 2}px)`,
}
: { left: `${horizontalGap}%` }
}
>
<h2 className='text-neutral-content line-clamp-1 text-center font-sans text-xs font-light'>
<h2
className={clsx(
'text-neutral-content line-clamp-1 text-center font-sans text-xs font-light',
)}
>
{section || ''}
</h2>
</div>
@@ -36,6 +36,8 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
const [writingMode, setWritingMode] = useState(viewSettings.writingMode!);
const [overrideLayout, setOverrideLayout] = useState(viewSettings.overrideLayout!);
const [isScrolledMode, setScrolledMode] = useState(viewSettings.scrolled!);
const [doubleBorder, setDoubleBorder] = useState(viewSettings.doubleBorder!);
const [borderColor, setBorderColor] = useState(viewSettings.borderColor!);
useEffect(() => {
viewSettings.paragraphMargin = paragraphMargin;
@@ -177,6 +179,9 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
// global settings are not supported for writing mode
const prevWritingMode = viewSettings.writingMode;
viewSettings.writingMode = writingMode;
if (writingMode.includes('vertical')) {
viewSettings.vertical = true;
}
setViewSettings(bookKey, viewSettings);
if (view) {
view.renderer.setStyles?.(getStyles(viewSettings));
@@ -218,6 +223,26 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isScrolledMode]);
useEffect(() => {
viewSettings.doubleBorder = doubleBorder;
setViewSettings(bookKey, viewSettings);
if (isFontLayoutSettingsGlobal) {
settings.globalViewSettings.doubleBorder = doubleBorder;
setSettings(settings);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [doubleBorder]);
useEffect(() => {
viewSettings.borderColor = borderColor;
setViewSettings(bookKey, viewSettings);
if (isFontLayoutSettingsGlobal) {
settings.globalViewSettings.borderColor = borderColor;
setSettings(settings);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [borderColor]);
const langCode = getBookLangCode(bookData.bookDoc?.metadata?.language);
const mightBeRTLBook =
langCode === 'zh' || langCode === 'ja' || langCode === 'ko' || langCode === '';
@@ -281,6 +306,39 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
</div>
)}
{viewSettings.vertical && (
<>
<div className='w-full'>
<div className='flex items-center justify-between'>
<h2 className='font-medium'>{_('Double Border')}</h2>
<input
type='checkbox'
className='toggle'
checked={doubleBorder}
onChange={() => setDoubleBorder(!doubleBorder)}
/>
</div>
</div>
<div className='w-full'>
<div className='flex items-center justify-between'>
<h2 className='font-medium'>{_('Border Color')}</h2>
<div className='flex gap-4'>
<button
className={`btn btn-circle btn-sm bg-red-300 hover:bg-red-500 ${borderColor === 'red' ? 'btn-active !bg-red-500' : ''}`}
onClick={() => setBorderColor('red')}
></button>
<button
className={`btn btn-circle btn-sm bg-black/50 hover:bg-black ${borderColor === 'black' ? 'btn-active !bg-black' : ''}`}
onClick={() => setBorderColor('black')}
></button>
</div>
</div>
</div>
</>
)}
<div className='w-full'>
<h2 className='mb-2 font-medium'>{_('Paragraph')}</h2>
<div className='card bg-base-100 border-base-200 border shadow'>
@@ -374,7 +432,7 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
label={_('Horizontal Margins (%)')}
value={gapPercent}
onChange={setGapPercent}
min={0}
min={viewSettings.vertical ? 2 : 0}
max={30}
/>
<NumberInput
@@ -71,6 +71,8 @@ export const DEFAULT_BOOK_LAYOUT: BookLayout = {
writingMode: 'auto',
vertical: false,
rtl: false,
doubleBorder: true,
borderColor: 'red',
};
export const DEFAULT_BOOK_STYLE: BookStyle = {
+4
View File
@@ -278,6 +278,10 @@ foliate-view {
line-height: 1em !important;
}
.writing-vertical-rl {
writing-mode: vertical-rl;
}
.direction-rtl {
direction: rtl;
}
+2
View File
@@ -68,6 +68,8 @@ export interface BookLayout {
writingMode: WritingMode;
vertical: boolean;
rtl: boolean;
doubleBorder: boolean;
borderColor: string;
}
export interface BookStyle {
+4 -1
View File
@@ -133,6 +133,7 @@ const getLayoutStyles = (
hyphenate: boolean,
zoomLevel: number,
writingMode: string,
vertical: boolean,
bg: string,
fg: string,
primary: string,
@@ -181,6 +182,7 @@ const getLayoutStyles = (
color: ${fg};
${writingMode === 'auto' ? '' : `writing-mode: ${writingMode};`}
text-align: var(--default-text-align);
max-height: unset;
background-color: var(--theme-bg-color, transparent);
background: var(--background-set, none);
}
@@ -195,7 +197,7 @@ const getLayoutStyles = (
background-color: transparent !important;
}
p, li, blockquote, dd {
margin: ${paragraphMargin}em 0;
margin: ${vertical ? `0 ${paragraphMargin}em` : `${paragraphMargin}em 0`};
line-height: ${lineSpacing} ${overrideLayout ? '!important' : ''};
word-spacing: ${wordSpacing}px ${overrideLayout ? '!important' : ''};
letter-spacing: ${letterSpacing}px ${overrideLayout ? '!important' : ''};
@@ -314,6 +316,7 @@ export const getStyles = (viewSettings: ViewSettings, themeCode?: ThemeCode) =>
viewSettings.hyphenation!,
viewSettings.zoomLevel! / 100.0,
viewSettings.writingMode!,
viewSettings.vertical!,
themeCode.bg,
themeCode.fg,
themeCode.primary,