rtl: rtl page layout from settings and book language, closes #359 (#500)

This commit is contained in:
Huang Xin
2025-03-06 17:58:32 +08:00
committed by GitHub
parent 088c7d2c49
commit d30d1c17ae
3 changed files with 53 additions and 19 deletions
@@ -10,7 +10,7 @@ import { useProgressSync } from '../hooks/useProgressSync';
import { useProgressAutoSave } from '../hooks/useProgressAutoSave';
import { useAutoHideScrollbar } from '../hooks/useAutoHideScrollbar';
import { getStyles, mountAdditionalFonts } from '@/utils/style';
import { getBookDirFromWritingMode } from '@/utils/book';
import { getBookDirFromLanguage, getBookDirFromWritingMode } from '@/utils/book';
import { useTheme } from '@/hooks/useTheme';
import {
handleKeydown,
@@ -58,8 +58,9 @@ const FoliateViewer: React.FC<{
if (detail.doc) {
const writingDir = viewRef.current?.renderer.setStyles && getDirection(detail.doc);
const viewSettings = getViewSettings(bookKey)!;
viewSettings.vertical = writingDir?.vertical || false;
viewSettings.rtl = writingDir?.rtl || false;
viewSettings.vertical =
writingDir?.vertical || viewSettings.writingMode.includes('vertical') || false;
viewSettings.rtl = writingDir?.rtl || viewSettings.writingMode.includes('rl') || false;
setViewSettings(bookKey, viewSettings);
if (viewSettings.scrolled && shouldAutoHideScrollbar) {
handleScrollbarAutoHide(detail.doc);
@@ -137,22 +138,25 @@ const FoliateViewer: React.FC<{
document.body.append(view);
containerRef.current?.appendChild(view);
const viewSettings = getViewSettings(bookKey)!;
const writingMode = viewSettings.writingMode;
if (writingMode) {
const settingsDir = getBookDirFromWritingMode(writingMode);
const languageDir = getBookDirFromLanguage(bookDoc.metadata.language);
if (settingsDir !== 'auto') {
bookDoc.dir = settingsDir;
} else if (languageDir !== 'auto') {
bookDoc.dir = languageDir;
}
}
await view.open(bookDoc);
// make sure we can listen renderer events after opening book
viewRef.current = view;
setFoliateView(bookKey, view);
const viewSettings = getViewSettings(bookKey)!;
view.renderer.setStyles?.(getStyles(viewSettings, themeCode));
const writingMode = viewSettings.writingMode;
if (writingMode) {
const dir = getBookDirFromWritingMode(writingMode);
if (dir !== 'auto') {
view.book.dir = dir;
}
}
const isScrolled = viewSettings.scrolled!;
const marginPx = viewSettings.marginPx!;
const gapPercent = viewSettings.gapPercent!;
@@ -1,6 +1,7 @@
import React, { useEffect, useState } from 'react';
import { MdOutlineAutoMode } from 'react-icons/md';
import { MdOutlineTextRotationDown, MdOutlineTextRotationNone } from 'react-icons/md';
import { MdOutlineTextRotationNone, MdTextRotateVertical } from 'react-icons/md';
import { TbTextDirectionRtl } from 'react-icons/tb';
import { useSettingsStore } from '@/store/settingsStore';
import { useReaderStore } from '@/store/readerStore';
@@ -175,12 +176,19 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
useEffect(() => {
// global settings are not supported for writing mode
const prevWritingMode = viewSettings.writingMode;
viewSettings.writingMode = writingMode;
setViewSettings(bookKey, viewSettings);
if (view) {
view.renderer.setStyles?.(getStyles(viewSettings, themeCode));
view.book.dir = getBookDirFromWritingMode(writingMode);
}
if (
prevWritingMode !== writingMode &&
(writingMode === 'horizontal-rl' || prevWritingMode === 'horizontal-rl')
) {
setTimeout(() => window.location.reload(), 100);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [writingMode]);
@@ -196,11 +204,12 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
}, [overrideLayout]);
const langCode = getBookLangCode(bookData.bookDoc?.metadata?.language);
const isCJKBook = langCode === 'zh' || langCode === 'ja' || langCode === 'ko';
const mightBeRTLBook =
langCode === 'zh' || langCode === 'ja' || langCode === 'ko' || langCode === '';
return (
<div className='my-4 w-full space-y-6'>
{isCJKBook && (
{mightBeRTLBook && (
<div className='w-full'>
<div className='flex items-center justify-between'>
<h2 className='font-medium'>{_('Writing Mode')}</h2>
@@ -228,7 +237,16 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
className={`btn btn-ghost btn-circle ${writingMode === 'vertical-rl' ? 'btn-active bg-base-300' : ''}`}
onClick={() => setWritingMode('vertical-rl')}
>
<MdOutlineTextRotationDown />
<MdTextRotateVertical />
</button>
</div>
<div className='lg:tooltip lg:tooltip-bottom' data-tip={_('RTL Direction')}>
<button
className={`btn btn-ghost btn-circle ${writingMode === 'horizontal-rl' ? 'btn-active bg-base-300' : ''}`}
onClick={() => setWritingMode('horizontal-rl')}
>
<TbTextDirectionRtl />
</button>
</div>
</div>
+15 -3
View File
@@ -57,9 +57,9 @@ export const listFormater = (narrow = false, lang = userLang) => {
export const getBookLangCode = (lang: string | string[] | undefined) => {
try {
const bookLang = typeof lang === 'string' ? lang : lang?.[0];
return bookLang ? bookLang.split('-')[0]! : 'en';
return bookLang ? bookLang.split('-')[0]! : '';
} catch {
return 'en';
return '';
}
};
@@ -67,7 +67,7 @@ export const formatAuthors = (
contributors: string | Contributor | [string | Contributor],
bookLang?: string | string[],
) => {
const langCode = getBookLangCode(bookLang);
const langCode = getBookLangCode(bookLang) || 'en';
return Array.isArray(contributors)
? listFormater(langCode === 'zh', langCode).format(
contributors.map((contributor) =>
@@ -90,6 +90,10 @@ export const formatLanguage = (lang: string | string[] | undefined) => {
return Array.isArray(lang) ? lang.join(', ') : lang;
};
export const primaryLanguage = (lang: string | string[] | undefined) => {
return Array.isArray(lang) ? lang[0] : lang;
};
export const formatDate = (date: string | number | Date | undefined) => {
if (!date) return;
try {
@@ -131,3 +135,11 @@ export const getBookDirFromWritingMode = (writingMode: WritingMode) => {
return 'auto';
}
};
export const getBookDirFromLanguage = (language: string | string[] | undefined) => {
const lang = primaryLanguage(language);
if (!lang) return 'auto';
const rtlLanguages = new Set(['ar', 'he', 'fa', 'ur', 'dv', 'ps', 'sd', 'yi']);
const primaryLang = lang.split('-')[0]!.toLowerCase();
return rtlLanguages.has(primaryLang) ? 'rtl' : 'auto';
};