forked from akai/readest
693c79c9b7
This option is hard to understand and explain. Most hardcoded color in dark mode that affects readability has already been fixed in the reader so there is no necessity to have this option.
154 lines
5.0 KiB
TypeScript
154 lines
5.0 KiB
TypeScript
import clsx from 'clsx';
|
|
import React, { useEffect } from 'react';
|
|
import { useState } from 'react';
|
|
import { BiMoon, BiSun } from 'react-icons/bi';
|
|
import { TbSunMoon } from 'react-icons/tb';
|
|
import { MdZoomOut, MdZoomIn, MdCheck } from 'react-icons/md';
|
|
|
|
import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL, ZOOM_STEP } from '@/services/constants';
|
|
import { useEnv } from '@/context/EnvContext';
|
|
import { useThemeStore } from '@/store/themeStore';
|
|
import { useReaderStore } from '@/store/readerStore';
|
|
import { useTranslation } from '@/hooks/useTranslation';
|
|
import { ThemeMode } from '@/styles/themes';
|
|
import { getStyles } from '@/utils/style';
|
|
import { getMaxInlineSize } from '@/utils/config';
|
|
import { tauriHandleToggleFullScreen } from '@/utils/window';
|
|
import MenuItem from '@/components/MenuItem';
|
|
|
|
interface ViewMenuProps {
|
|
bookKey: string;
|
|
setIsDropdownOpen?: (open: boolean) => void;
|
|
onSetSettingsDialogOpen: (open: boolean) => void;
|
|
}
|
|
|
|
const ViewMenu: React.FC<ViewMenuProps> = ({
|
|
bookKey,
|
|
setIsDropdownOpen,
|
|
onSetSettingsDialogOpen,
|
|
}) => {
|
|
const _ = useTranslation();
|
|
const { appService } = useEnv();
|
|
const { getView, getViews, getViewSettings, setViewSettings } = useReaderStore();
|
|
const viewSettings = getViewSettings(bookKey)!;
|
|
|
|
const { themeMode, themeCode, setThemeMode } = useThemeStore();
|
|
const [isScrolledMode, setScrolledMode] = useState(viewSettings!.scrolled);
|
|
const [zoomLevel, setZoomLevel] = useState(viewSettings!.zoomLevel!);
|
|
|
|
const zoomIn = () => setZoomLevel((prev) => Math.min(prev + ZOOM_STEP, MAX_ZOOM_LEVEL));
|
|
const zoomOut = () => setZoomLevel((prev) => Math.max(prev - ZOOM_STEP, MIN_ZOOM_LEVEL));
|
|
const resetZoom = () => setZoomLevel(100);
|
|
const toggleScrolledMode = () => setScrolledMode(!isScrolledMode);
|
|
|
|
const openFontLayoutMenu = () => {
|
|
setIsDropdownOpen?.(false);
|
|
onSetSettingsDialogOpen(true);
|
|
};
|
|
|
|
const cycleThemeMode = () => {
|
|
const nextMode: ThemeMode =
|
|
themeMode === 'auto' ? 'light' : themeMode === 'light' ? 'dark' : 'auto';
|
|
setThemeMode(nextMode);
|
|
};
|
|
|
|
const handleFullScreen = () => {
|
|
tauriHandleToggleFullScreen();
|
|
setIsDropdownOpen?.(false);
|
|
};
|
|
|
|
useEffect(() => {
|
|
getViews().forEach((view) => {
|
|
view.renderer.setStyles?.(getStyles(viewSettings!));
|
|
});
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [themeCode]);
|
|
|
|
useEffect(() => {
|
|
viewSettings!.scrolled = isScrolledMode;
|
|
getView(bookKey)?.renderer.setAttribute('flow', isScrolledMode ? 'scrolled' : 'paginated');
|
|
getView(bookKey)?.renderer.setAttribute(
|
|
'max-inline-size',
|
|
`${getMaxInlineSize(viewSettings)}px`,
|
|
);
|
|
getView(bookKey)?.renderer.setStyles?.(getStyles(viewSettings!));
|
|
setViewSettings(bookKey, viewSettings!);
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [isScrolledMode]);
|
|
|
|
useEffect(() => {
|
|
const view = getView(bookKey);
|
|
if (!view) return;
|
|
viewSettings!.zoomLevel = zoomLevel;
|
|
setViewSettings(bookKey, viewSettings!);
|
|
view.renderer.setStyles?.(getStyles(viewSettings!));
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [zoomLevel]);
|
|
|
|
return (
|
|
<div
|
|
tabIndex={0}
|
|
className='view-menu dropdown-content bgcolor-base-200 dropdown-right no-triangle border-base-200 z-20 mt-1 w-72 border shadow-2xl'
|
|
>
|
|
<div className={clsx('flex items-center justify-between rounded-md')}>
|
|
<button
|
|
onClick={zoomOut}
|
|
className={clsx(
|
|
'hover:bg-base-300 text-base-content rounded-full p-2',
|
|
zoomLevel <= MIN_ZOOM_LEVEL && 'btn-disabled text-gray-400',
|
|
)}
|
|
>
|
|
<MdZoomOut />
|
|
</button>
|
|
<button
|
|
className={clsx(
|
|
'hover:bg-base-300 text-base-content h-8 min-h-8 w-[50%] rounded-md p-1 text-center',
|
|
)}
|
|
onClick={resetZoom}
|
|
>
|
|
{zoomLevel}%
|
|
</button>
|
|
<button
|
|
onClick={zoomIn}
|
|
className={clsx(
|
|
'hover:bg-base-300 text-base-content rounded-full p-2',
|
|
zoomLevel >= MAX_ZOOM_LEVEL && 'btn-disabled text-gray-400',
|
|
)}
|
|
>
|
|
<MdZoomIn />
|
|
</button>
|
|
</div>
|
|
|
|
<hr className='border-base-300 my-1' />
|
|
|
|
<MenuItem label={_('Font & Layout')} shortcut='Shift+F' onClick={openFontLayoutMenu} />
|
|
|
|
<MenuItem
|
|
label={_('Scrolled Mode')}
|
|
shortcut='Shift+J'
|
|
icon={isScrolledMode ? <MdCheck /> : undefined}
|
|
onClick={toggleScrolledMode}
|
|
/>
|
|
|
|
<hr className='border-base-300 my-1' />
|
|
|
|
{appService?.hasRoundedWindow && (
|
|
<MenuItem label={_('Fullscreen')} onClick={handleFullScreen} />
|
|
)}
|
|
<MenuItem
|
|
label={
|
|
themeMode === 'dark'
|
|
? _('Dark Mode')
|
|
: themeMode === 'light'
|
|
? _('Light Mode')
|
|
: _('Auto Mode')
|
|
}
|
|
icon={themeMode === 'dark' ? <BiMoon /> : themeMode === 'light' ? <BiSun /> : <TbSunMoon />}
|
|
onClick={cycleThemeMode}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ViewMenu;
|