import clsx from 'clsx'; import React, { useEffect, useRef, useState } from 'react'; import { BookConfig } from '@/types/book'; import { useEnv } from '@/context/EnvContext'; import { useSettingsStore } from '@/store/settingsStore'; import { useTranslation } from '@/hooks/useTranslation'; import { RiFontSize } from 'react-icons/ri'; import { RiDashboardLine, RiTranslate } from 'react-icons/ri'; import { VscSymbolColor } from 'react-icons/vsc'; import { PiDotsThreeVerticalBold } from 'react-icons/pi'; import { LiaHandPointerSolid } from 'react-icons/lia'; import { IoAccessibilityOutline } from 'react-icons/io5'; import { MdArrowBackIosNew, MdArrowForwardIos } from 'react-icons/md'; import { getDirFromUILanguage } from '@/utils/rtl'; import FontPanel from './FontPanel'; import LayoutPanel from './LayoutPanel'; import ColorPanel from './ColorPanel'; import Dropdown from '@/components/Dropdown'; import Dialog from '@/components/Dialog'; import DialogMenu from './DialogMenu'; import ControlPanel from './ControlPanel'; import LangPanel from './LangPanel'; import MiscPanel from './MiscPanel'; export type SettingsPanelType = 'Font' | 'Layout' | 'Color' | 'Control' | 'Language' | 'Custom'; export type SettingsPanelPanelProp = { bookKey: string; onRegisterReset: (resetFn: () => void) => void; }; type TabConfig = { tab: SettingsPanelType; icon: React.ElementType; label: string; }; const SettingsDialog: React.FC<{ bookKey: string; config: BookConfig }> = ({ bookKey }) => { const _ = useTranslation(); const { appService } = useEnv(); const [isRtl] = useState(() => getDirFromUILanguage() === 'rtl'); const tabsRef = useRef(null); const [showAllTabLabels, setShowAllTabLabels] = useState(false); const { setFontLayoutSettingsDialogOpen } = useSettingsStore(); const tabConfig = [ { tab: 'Font', icon: RiFontSize, label: _('Font'), }, { tab: 'Layout', icon: RiDashboardLine, label: _('Layout'), }, { tab: 'Color', icon: VscSymbolColor, label: _('Color'), }, { tab: 'Control', icon: LiaHandPointerSolid, label: _('Behavior'), }, { tab: 'Language', icon: RiTranslate, label: _('Language'), }, { tab: 'Custom', icon: IoAccessibilityOutline, label: _('Custom'), }, ] as TabConfig[]; const [activePanel, setActivePanel] = useState(() => { const lastPanel = localStorage.getItem('lastConfigPanel'); if (lastPanel && tabConfig.some((tab) => tab.tab === lastPanel)) { return lastPanel as SettingsPanelType; } return 'Font' as SettingsPanelType; }); const handleSetActivePanel = (tab: SettingsPanelType) => { setActivePanel(tab); localStorage.setItem('lastConfigPanel', tab); }; const [resetFunctions, setResetFunctions] = useState< Record void) | null> >({ Font: null, Layout: null, Color: null, Control: null, Language: null, Custom: null, }); const registerResetFunction = (panel: SettingsPanelType, resetFn: () => void) => { setResetFunctions((prev) => ({ ...prev, [panel]: resetFn })); }; const handleResetCurrentPanel = () => { const resetFn = resetFunctions[activePanel]; if (resetFn) { resetFn(); } }; const handleClose = () => { setFontLayoutSettingsDialogOpen(false); }; useEffect(() => { const container = tabsRef.current; if (!container) return; const checkButtonWidths = () => { const threshold = (container.clientWidth - 64) * 0.22; const hideLabel = Array.from(container.querySelectorAll('button')).some((button) => { const labelSpan = button.querySelector('span'); const labelText = labelSpan?.textContent || ''; const clone = button.cloneNode(true) as HTMLButtonElement; clone.style.position = 'absolute'; clone.style.visibility = 'hidden'; clone.style.width = 'auto'; const cloneSpan = clone.querySelector('span'); if (cloneSpan) { cloneSpan.classList.remove('hidden'); cloneSpan.textContent = labelText; } document.body.appendChild(clone); const fullWidth = clone.scrollWidth; document.body.removeChild(clone); return fullWidth > threshold; }); setShowAllTabLabels(!hideLabel); }; checkButtonWidths(); const resizeObserver = new ResizeObserver(checkButtonWidths); resizeObserver.observe(container); const mutationObserver = new MutationObserver(checkButtonWidths); mutationObserver.observe(container, { subtree: true, characterData: true, }); return () => { resizeObserver.disconnect(); mutationObserver.disconnect(); }; }, []); const currentPanel = tabConfig.find((tab) => tab.tab === activePanel); return (
{currentPanel?.label || ''}
{tabConfig.map(({ tab, icon: Icon, label }) => ( ))}
} >
} > {activePanel === 'Font' && ( registerResetFunction('Font', fn)} /> )} {activePanel === 'Layout' && ( registerResetFunction('Layout', fn)} /> )} {activePanel === 'Color' && ( registerResetFunction('Color', fn)} /> )} {activePanel === 'Control' && ( registerResetFunction('Control', fn)} /> )} {activePanel === 'Language' && ( registerResetFunction('Language', fn)} /> )} {activePanel === 'Custom' && ( registerResetFunction('Custom', fn)} /> )}
); }; export default SettingsDialog;