import React, { useEffect, useState } from 'react'; import { MdOutlineAutoMode } 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'; import { useBookDataStore } from '@/store/bookDataStore'; import { useTranslation } from '@/hooks/useTranslation'; import { getStyles } from '@/utils/style'; import { getMaxInlineSize } from '@/utils/config'; import { getBookDirFromWritingMode, getBookLangCode } from '@/utils/book'; import NumberInput from './NumberInput'; const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => { const _ = useTranslation(); const { settings, isFontLayoutSettingsGlobal, setSettings } = useSettingsStore(); const { getView, getViewSettings, setViewSettings } = useReaderStore(); const { getBookData } = useBookDataStore(); const view = getView(bookKey); const bookData = getBookData(bookKey)!; const viewSettings = getViewSettings(bookKey)!; const [paragraphMargin, setParagraphMargin] = useState(viewSettings.paragraphMargin!); const [lineHeight, setLineHeight] = useState(viewSettings.lineHeight!); const [wordSpacing, setWordSpacing] = useState(viewSettings.wordSpacing!); const [letterSpacing, setLetterSpacing] = useState(viewSettings.letterSpacing!); const [textIndent, setTextIndent] = useState(viewSettings.textIndent!); const [fullJustification, setFullJustification] = useState(viewSettings.fullJustification!); const [hyphenation, setHyphenation] = useState(viewSettings.hyphenation!); const [marginPx, setMarginPx] = useState(viewSettings.marginPx!); const [gapPercent, setGapPercent] = useState(viewSettings.gapPercent!); const [maxColumnCount, setMaxColumnCount] = useState(viewSettings.maxColumnCount!); const [maxInlineSize, setMaxInlineSize] = useState(viewSettings.maxInlineSize!); const [maxBlockSize, setMaxBlockSize] = useState(viewSettings.maxBlockSize!); const [writingMode, setWritingMode] = useState(viewSettings.writingMode!); const [overrideLayout, setOverrideLayout] = useState(viewSettings.overrideLayout!); useEffect(() => { viewSettings.paragraphMargin = paragraphMargin; setViewSettings(bookKey, viewSettings); if (isFontLayoutSettingsGlobal) { settings.globalViewSettings.paragraphMargin = paragraphMargin; setSettings(settings); } view?.renderer.setStyles?.(getStyles(viewSettings)); // eslint-disable-next-line react-hooks/exhaustive-deps }, [paragraphMargin]); useEffect(() => { viewSettings.lineHeight = lineHeight; setViewSettings(bookKey, viewSettings); if (isFontLayoutSettingsGlobal) { settings.globalViewSettings.lineHeight = lineHeight; setSettings(settings); } view?.renderer.setStyles?.(getStyles(viewSettings)); // eslint-disable-next-line react-hooks/exhaustive-deps }, [lineHeight]); useEffect(() => { viewSettings.wordSpacing = wordSpacing; setViewSettings(bookKey, viewSettings); if (isFontLayoutSettingsGlobal) { settings.globalViewSettings.wordSpacing = wordSpacing; setSettings(settings); } view?.renderer.setStyles?.(getStyles(viewSettings)); // eslint-disable-next-line react-hooks/exhaustive-deps }, [wordSpacing]); useEffect(() => { viewSettings.letterSpacing = letterSpacing; setViewSettings(bookKey, viewSettings); if (isFontLayoutSettingsGlobal) { settings.globalViewSettings.letterSpacing = letterSpacing; setSettings(settings); } view?.renderer.setStyles?.(getStyles(viewSettings)); // eslint-disable-next-line react-hooks/exhaustive-deps }, [letterSpacing]); useEffect(() => { viewSettings.textIndent = textIndent; setViewSettings(bookKey, viewSettings); if (isFontLayoutSettingsGlobal) { settings.globalViewSettings.textIndent = textIndent; setSettings(settings); } view?.renderer.setStyles?.(getStyles(viewSettings)); // eslint-disable-next-line react-hooks/exhaustive-deps }, [textIndent]); useEffect(() => { viewSettings.fullJustification = fullJustification; setViewSettings(bookKey, viewSettings); if (isFontLayoutSettingsGlobal) { settings.globalViewSettings.fullJustification = fullJustification; setSettings(settings); } view?.renderer.setStyles?.(getStyles(viewSettings)); // eslint-disable-next-line react-hooks/exhaustive-deps }, [fullJustification]); useEffect(() => { viewSettings.hyphenation = hyphenation; setViewSettings(bookKey, viewSettings); if (isFontLayoutSettingsGlobal) { settings.globalViewSettings.hyphenation = hyphenation; setSettings(settings); } view?.renderer.setStyles?.(getStyles(viewSettings)); // eslint-disable-next-line react-hooks/exhaustive-deps }, [hyphenation]); useEffect(() => { viewSettings.marginPx = marginPx; setViewSettings(bookKey, viewSettings); if (isFontLayoutSettingsGlobal) { settings.globalViewSettings.marginPx = marginPx; setSettings(settings); } view?.renderer.setAttribute('margin', `${marginPx}px`); // eslint-disable-next-line react-hooks/exhaustive-deps }, [marginPx]); useEffect(() => { viewSettings.gapPercent = gapPercent; setViewSettings(bookKey, viewSettings); if (isFontLayoutSettingsGlobal) { settings.globalViewSettings.gapPercent = gapPercent; setSettings(settings); } view?.renderer.setAttribute('gap', `${gapPercent}%`); if (viewSettings.scrolled) { view?.renderer.setAttribute('flow', 'scrolled'); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [gapPercent]); useEffect(() => { viewSettings.maxColumnCount = maxColumnCount; setViewSettings(bookKey, viewSettings); if (isFontLayoutSettingsGlobal) { settings.globalViewSettings.maxColumnCount = maxColumnCount; setSettings(settings); } view?.renderer.setAttribute('max-column-count', maxColumnCount); view?.renderer.setAttribute('max-inline-size', `${getMaxInlineSize(viewSettings)}px`); // eslint-disable-next-line react-hooks/exhaustive-deps }, [maxColumnCount]); useEffect(() => { viewSettings.maxInlineSize = maxInlineSize; setViewSettings(bookKey, viewSettings); if (isFontLayoutSettingsGlobal) { settings.globalViewSettings.maxInlineSize = maxInlineSize; setSettings(settings); } view?.renderer.setAttribute('max-inline-size', `${getMaxInlineSize(viewSettings)}px`); // eslint-disable-next-line react-hooks/exhaustive-deps }, [maxInlineSize]); useEffect(() => { viewSettings.maxBlockSize = maxBlockSize; setViewSettings(bookKey, viewSettings); if (isFontLayoutSettingsGlobal) { settings.globalViewSettings.maxBlockSize = maxBlockSize; setSettings(settings); } view?.renderer.setAttribute('max-block-size', `${maxBlockSize}px`); // eslint-disable-next-line react-hooks/exhaustive-deps }, [maxBlockSize]); 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)); 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]); useEffect(() => { viewSettings.overrideLayout = overrideLayout; setViewSettings(bookKey, viewSettings); if (isFontLayoutSettingsGlobal) { settings.globalViewSettings.overrideLayout = overrideLayout; setSettings(settings); } view?.renderer.setStyles?.(getStyles(viewSettings)); // eslint-disable-next-line react-hooks/exhaustive-deps }, [overrideLayout]); const langCode = getBookLangCode(bookData.bookDoc?.metadata?.language); const mightBeRTLBook = langCode === 'zh' || langCode === 'ja' || langCode === 'ko' || langCode === ''; return (