Add theme and color config
This commit is contained in:
@@ -76,7 +76,11 @@ const BookmarkToggler: React.FC<BookmarkTogglerProps> = ({ bookKey }) => {
|
||||
|
||||
return (
|
||||
<button onClick={toggleBookmark} className='p-2'>
|
||||
{isBookmarked ? <MdOutlineBookmark size={20} /> : <MdOutlineBookmarkAdd size={20} />}
|
||||
{isBookmarked ? (
|
||||
<MdOutlineBookmark size={20} className='text-base-content' />
|
||||
) : (
|
||||
<MdOutlineBookmarkAdd size={20} className='text-base-content' />
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ import { BookDoc } from '@/libs/document';
|
||||
import { BookConfig, BookNote, BookSearchConfig, BookSearchResult } from '@/types/book';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
import { getStyles } from '@/utils/style';
|
||||
import { useTheme } from '@/hooks/useTheme';
|
||||
import { ONE_COLUMN_MAX_INLINE_SIZE } from '@/services/constants';
|
||||
import {
|
||||
handleKeydown,
|
||||
@@ -61,6 +62,7 @@ const FoliateViewer: React.FC<{
|
||||
const [viewInited, setViewInited] = useState(false);
|
||||
const isViewCreated = useRef(false);
|
||||
const { setView: setFoliateView, setProgress, getViewSettings } = useReaderStore();
|
||||
const { themeCode } = useTheme();
|
||||
|
||||
const progressRelocateHandler = (event: Event) => {
|
||||
const detail = (event as CustomEvent).detail;
|
||||
@@ -118,7 +120,6 @@ const FoliateViewer: React.FC<{
|
||||
|
||||
await view.open(bookDoc);
|
||||
const viewSettings = getViewSettings(bookKey)!;
|
||||
view.renderer.setStyles?.(getStyles(viewSettings));
|
||||
const isScrolled = viewSettings.scrolled!;
|
||||
const marginPx = viewSettings.marginPx!;
|
||||
const gapPercent = viewSettings.gapPercent!;
|
||||
@@ -146,6 +147,7 @@ const FoliateViewer: React.FC<{
|
||||
await view.goToFraction(0);
|
||||
}
|
||||
setViewInited(true);
|
||||
view.renderer.setStyles?.(getStyles(viewSettings, themeCode));
|
||||
|
||||
window.addEventListener('message', handleClickTurnPage);
|
||||
};
|
||||
|
||||
@@ -42,12 +42,12 @@ const FooterBar: React.FC<FooterBarProps> = ({ bookKey, pageinfo, isHoveredAnim
|
||||
<button className='btn btn-ghost mx-2 h-8 min-h-8 w-8 p-0' onClick={handleGoPrev}>
|
||||
<RiArrowLeftWideLine size={20} />
|
||||
</button>
|
||||
<span className='mx-2 text-center text-sm text-black'>
|
||||
<span className='mx-2 text-center text-sm'>
|
||||
{pageinfoValid ? `${Math.round(progressFraction * 100)}%` : ''}
|
||||
</span>
|
||||
<input
|
||||
type='range'
|
||||
className='mx-2 w-full'
|
||||
className='text-base-content mx-2 w-full'
|
||||
min={0}
|
||||
max={100}
|
||||
value={pageinfoValid ? progressFraction * 100 : 0}
|
||||
|
||||
@@ -20,9 +20,9 @@ const NotebookToggler: React.FC<NotebookTogglerProps> = ({ bookKey }) => {
|
||||
return (
|
||||
<button onClick={handleToggleSidebar} className='p-2'>
|
||||
{sideBarBookKey == bookKey && isNotebookVisible ? (
|
||||
<TbLayoutSidebarRightFilled size={20} />
|
||||
<TbLayoutSidebarRightFilled size={20} className='text-base-content' />
|
||||
) : (
|
||||
<TbLayoutSidebarRight size={20} />
|
||||
<TbLayoutSidebarRight size={20} className='text-base-content' />
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -23,7 +23,9 @@ const PageInfoView: React.FC<PageInfoProps> = ({ bookFormat, section, pageinfo,
|
||||
className='pageinfo absolute bottom-0 left-0 right-0 flex h-12 items-center justify-end'
|
||||
style={{ paddingRight: gapRight }}
|
||||
>
|
||||
<h2 className='text-right font-sans text-xs font-extralight text-slate-500'>{pageInfo}</h2>
|
||||
<h2 className='text-neutral-content text-right font-sans text-xs font-extralight'>
|
||||
{pageInfo}
|
||||
</h2>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -90,7 +90,7 @@ const ReaderContent: React.FC<{ settings: SystemSettings }> = ({ settings }) =>
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='flex h-screen'>
|
||||
<div className='text-base-content flex h-screen'>
|
||||
<SideBar onGoToLibrary={handleCloseBooks} />
|
||||
<BooksGrid bookKeys={bookKeys} onCloseBook={handleCloseBook} />
|
||||
<Notebook />
|
||||
|
||||
@@ -20,7 +20,9 @@ const SectionInfo: React.FC<SectionInfoProps> = ({ section, gapLeft }) => {
|
||||
)}
|
||||
style={{ left: gapLeft }}
|
||||
>
|
||||
<h2 className='text-center font-sans text-xs font-light text-slate-500'>{section || ''}</h2>
|
||||
<h2 className='text-neutral-content text-center font-sans text-xs font-light'>
|
||||
{section || ''}
|
||||
</h2>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -20,9 +20,9 @@ const SidebarToggler: React.FC<SidebarTogglerProps> = ({ bookKey }) => {
|
||||
return (
|
||||
<button onClick={handleToggleSidebar} className='p-2'>
|
||||
{sideBarBookKey == bookKey && isSideBarVisible ? (
|
||||
<TbLayoutSidebarFilled size={20} />
|
||||
<TbLayoutSidebarFilled size={20} className='text-base-content' />
|
||||
) : (
|
||||
<TbLayoutSidebar size={20} />
|
||||
<TbLayoutSidebar size={20} className='text-base-content' />
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -2,9 +2,12 @@ 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 MenuItem from '@/components/MenuItem';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
import { useTheme, ThemeMode } from '@/hooks/useTheme';
|
||||
import { getStyles } from '@/utils/style';
|
||||
|
||||
interface ViewMenuProps {
|
||||
@@ -21,7 +24,7 @@ const ViewMenu: React.FC<ViewMenuProps> = ({
|
||||
const { getView, getViewSettings, setViewSettings } = useReaderStore();
|
||||
const viewSettings = getViewSettings(bookKey);
|
||||
|
||||
const [isDarkMode, setDarkMode] = useState(viewSettings!.theme === 'dark');
|
||||
const { themeMode, isDarkMode, themeCode, updateThemeMode } = useTheme();
|
||||
const [isScrolledMode, setScrolledMode] = useState(viewSettings!.scrolled);
|
||||
const [isInvertedColors, setInvertedColors] = useState(viewSettings!.invert);
|
||||
const [zoomLevel, setZoomLevel] = useState(viewSettings!.zoomLevel!);
|
||||
@@ -30,7 +33,6 @@ const ViewMenu: React.FC<ViewMenuProps> = ({
|
||||
const zoomOut = () => setZoomLevel((prev) => Math.max(prev - 10, 50));
|
||||
const resetZoom = () => setZoomLevel(100);
|
||||
const toggleScrolledMode = () => setScrolledMode(!isScrolledMode);
|
||||
const toggleDarkMode = () => setDarkMode(!isDarkMode);
|
||||
const toggleInvertedColors = () => setInvertedColors(!isInvertedColors);
|
||||
|
||||
const openFontLayoutMenu = () => {
|
||||
@@ -38,6 +40,16 @@ const ViewMenu: React.FC<ViewMenuProps> = ({
|
||||
onSetSettingsDialogOpen(true);
|
||||
};
|
||||
|
||||
const cycleThemeMode = () => {
|
||||
const nextMode: ThemeMode =
|
||||
themeMode === 'auto' ? 'light' : themeMode === 'light' ? 'dark' : 'auto';
|
||||
updateThemeMode(nextMode);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getView(bookKey)?.renderer.setStyles?.(getStyles(viewSettings!, themeCode));
|
||||
}, [themeCode]);
|
||||
|
||||
useEffect(() => {
|
||||
getView(bookKey)?.renderer.setAttribute('flow', isScrolledMode ? 'scrolled' : 'paginated');
|
||||
viewSettings!.scrolled = isScrolledMode;
|
||||
@@ -46,6 +58,7 @@ const ViewMenu: React.FC<ViewMenuProps> = ({
|
||||
|
||||
useEffect(() => {
|
||||
document.body.classList.toggle('invert', isInvertedColors);
|
||||
getView(bookKey)?.renderer.setStyles?.(getStyles(viewSettings!, themeCode));
|
||||
}, [isInvertedColors]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -53,7 +66,7 @@ const ViewMenu: React.FC<ViewMenuProps> = ({
|
||||
if (!view) return;
|
||||
// FIXME: zoom level is not working in paginated mode
|
||||
if (viewSettings?.scrolled) {
|
||||
view.renderer.setStyles?.(getStyles(viewSettings!));
|
||||
view.renderer.setStyles?.(getStyles(viewSettings!, themeCode));
|
||||
}
|
||||
viewSettings!.zoomLevel = zoomLevel;
|
||||
setViewSettings(bookKey, viewSettings!);
|
||||
@@ -63,7 +76,7 @@ const ViewMenu: React.FC<ViewMenuProps> = ({
|
||||
<div
|
||||
id='exclude-title-bar-mousedown'
|
||||
tabIndex={0}
|
||||
className='view-menu dropdown-content dropdown-right no-triangle z-20 mt-1 w-72 border bg-white shadow-2xl'
|
||||
className='view-menu dropdown-content dropdown-right no-triangle border-base-100 z-20 mt-1 w-72 border shadow-2xl'
|
||||
>
|
||||
<div
|
||||
className={clsx(
|
||||
@@ -73,14 +86,17 @@ const ViewMenu: React.FC<ViewMenuProps> = ({
|
||||
>
|
||||
<button
|
||||
onClick={zoomOut}
|
||||
className={clsx('rounded-full p-2 hover:bg-gray-100', !isScrolledMode && 'btn-disabled')}
|
||||
className={clsx(
|
||||
'hover:bg-base-200 text-base-content rounded-full p-2',
|
||||
!isScrolledMode && 'btn-disabled text-gray-400',
|
||||
)}
|
||||
>
|
||||
<MdZoomOut size={20} />
|
||||
</button>
|
||||
<button
|
||||
className={clsx(
|
||||
'h-8 min-h-8 w-[50%] rounded-md p-1 text-center hover:bg-gray-100',
|
||||
!isScrolledMode && 'btn-disabled',
|
||||
'hover:bg-base-200 text-base-content h-8 min-h-8 w-[50%] rounded-md p-1 text-center',
|
||||
!isScrolledMode && 'btn-disabled text-gray-400',
|
||||
)}
|
||||
onClick={resetZoom}
|
||||
>
|
||||
@@ -88,65 +104,49 @@ const ViewMenu: React.FC<ViewMenuProps> = ({
|
||||
</button>
|
||||
<button
|
||||
onClick={zoomIn}
|
||||
className={clsx('rounded-full p-2 hover:bg-gray-100', !isScrolledMode && 'btn-disabled')}
|
||||
className={clsx(
|
||||
'hover:bg-base-200 text-base-content rounded-full p-2',
|
||||
!isScrolledMode && 'btn-disabled text-gray-400',
|
||||
)}
|
||||
>
|
||||
<MdZoomIn size={20} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<hr className='my-1' />
|
||||
<hr className='border-base-200 my-1' />
|
||||
|
||||
<button
|
||||
className='flex w-full items-center justify-between rounded-md p-2 hover:bg-gray-100'
|
||||
onClick={openFontLayoutMenu}
|
||||
>
|
||||
<div className='flex items-center'>
|
||||
<span style={{ minWidth: '20px' }}></span>
|
||||
<span className='ml-2'>Font & Layout</span>
|
||||
</div>
|
||||
<span className='text-sm text-gray-400'>Shift+F</span>
|
||||
</button>
|
||||
<MenuItem label='Font & Layout' shortcut='Shift+F' onClick={openFontLayoutMenu} />
|
||||
|
||||
<button
|
||||
className='flex w-full items-center justify-between rounded-md p-2 hover:bg-gray-100'
|
||||
<MenuItem
|
||||
label='Scrolled Mode'
|
||||
shortcut='Shift+J'
|
||||
icon={isScrolledMode ? <MdCheck size={20} /> : undefined}
|
||||
onClick={toggleScrolledMode}
|
||||
>
|
||||
<div className='flex items-center'>
|
||||
<span style={{ minWidth: '20px' }}>
|
||||
{isScrolledMode && <MdCheck size={20} className='text-base-content' />}
|
||||
</span>
|
||||
<span className='ml-2'>Scrolled Mode</span>
|
||||
</div>
|
||||
<span className='text-sm text-gray-400'>Shift+J</span>
|
||||
</button>
|
||||
/>
|
||||
|
||||
<hr className='my-1' />
|
||||
<hr className='border-base-200 my-1' />
|
||||
|
||||
<button
|
||||
className='flex w-full items-center rounded-md p-2 hover:bg-gray-100'
|
||||
onClick={toggleDarkMode}
|
||||
>
|
||||
<div className='flex items-center'>
|
||||
<span style={{ minWidth: '20px' }}>
|
||||
{isDarkMode ? <BiMoon size={20} /> : <BiSun size={20} />}
|
||||
</span>
|
||||
</div>
|
||||
<span className='ml-2'>{isDarkMode ? 'Dark Mode' : 'Light Mode'}</span>
|
||||
</button>
|
||||
<button
|
||||
className={clsx(
|
||||
'flex w-full items-center justify-between rounded-md p-2 hover:bg-gray-100',
|
||||
!isDarkMode && 'btn-disabled text-gray-400',
|
||||
)}
|
||||
<MenuItem
|
||||
label={
|
||||
themeMode === 'dark' ? 'Dark Mode' : themeMode === 'light' ? 'Light Mode' : 'Auto Mode'
|
||||
}
|
||||
icon={
|
||||
themeMode === 'dark' ? (
|
||||
<BiMoon size={20} />
|
||||
) : themeMode === 'light' ? (
|
||||
<BiSun size={20} />
|
||||
) : (
|
||||
<TbSunMoon size={20} />
|
||||
)
|
||||
}
|
||||
onClick={cycleThemeMode}
|
||||
/>
|
||||
<MenuItem
|
||||
label='Invert Colors in Dark Mode'
|
||||
icon={isInvertedColors ? <MdCheck size={20} className='text-base-content' /> : undefined}
|
||||
onClick={toggleInvertedColors}
|
||||
>
|
||||
<div className='flex items-center'>
|
||||
<span style={{ minWidth: '20px' }}>
|
||||
{isInvertedColors && <MdCheck size={20} className='text-base-content' />}
|
||||
</span>
|
||||
<span className='ml-2'>Invert Colors in Dark Mode</span>
|
||||
</div>
|
||||
</button>
|
||||
disabled={!isDarkMode}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ const NotebookHeader: React.FC<{
|
||||
<div className='z-10 flex items-center space-x-3'>
|
||||
<button
|
||||
onClick={handleTogglePin}
|
||||
className={`${isPinned ? 'bg-gray-300' : 'bg-base-300'} btn btn-ghost btn-circle h-6 min-h-6 w-6`}
|
||||
className={`${isPinned ? 'bg-base-300' : 'bg-base-300/65'} btn btn-ghost btn-circle h-6 min-h-6 w-6`}
|
||||
>
|
||||
{isPinned ? <MdPushPin size={14} /> : <MdOutlinePushPin size={14} />}
|
||||
</button>
|
||||
|
||||
@@ -48,7 +48,7 @@ const NoteEditor: React.FC<NoteEditorProps> = ({ onSave, onEdit }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='note-editor-container mt-2 rounded-md bg-white p-2'>
|
||||
<div className='note-editor-container bg-base-100 mt-2 rounded-md p-2'>
|
||||
<div className='flex w-full justify-between space-x-2'>
|
||||
<div className='settings-content relative w-full'>
|
||||
<textarea
|
||||
|
||||
@@ -145,7 +145,7 @@ const Notebook: React.FC = ({}) => {
|
||||
<li key={`${index}-${item.id}`} className='my-2'>
|
||||
<div
|
||||
tabIndex={0}
|
||||
className='collapse-arrow border-base-300 collapse border bg-white'
|
||||
className='collapse-arrow border-base-300 bg-base-100 collapse border'
|
||||
>
|
||||
<div
|
||||
className='collapse-title h-9 min-h-9 p-2 pr-8 text-sm font-medium'
|
||||
|
||||
@@ -1,32 +1,87 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { MdOutlineLightMode, MdOutlineDarkMode } from 'react-icons/md';
|
||||
import { MdRadioButtonUnchecked, MdRadioButtonChecked } from 'react-icons/md';
|
||||
import { TbSunMoon } from 'react-icons/tb';
|
||||
|
||||
const ColorPanel: React.FC<{ bookKey: string }> = () => {
|
||||
const [selectedTheme, setSelectedTheme] = useState('Default');
|
||||
const themes = [
|
||||
'Default',
|
||||
'Gray',
|
||||
'Sepia',
|
||||
'Grass',
|
||||
'Cherry',
|
||||
'Sky',
|
||||
'Nord',
|
||||
'Solarized',
|
||||
'Gruvbox',
|
||||
];
|
||||
import { useTheme } from '@/hooks/useTheme';
|
||||
import { themes } from '@/styles/themes';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
import { getStyles } from '@/utils/style';
|
||||
|
||||
const ColorPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
const { themeMode, themeColor, themeCode, isDarkMode, updateThemeMode, updateThemeColor } =
|
||||
useTheme();
|
||||
const { getView, getViewSettings } = useReaderStore();
|
||||
const view = getView(bookKey);
|
||||
const viewSettings = getViewSettings(bookKey)!;
|
||||
|
||||
useEffect(() => {
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings, themeCode));
|
||||
}, [themeCode]);
|
||||
|
||||
return (
|
||||
<div className='my-4 w-full space-y-6'>
|
||||
<div className='w-full'>
|
||||
<h2 className='mb-2 font-medium'>Color Settings</h2>
|
||||
<div className='mt-4 grid grid-cols-3 gap-2'>
|
||||
{themes.map((theme) => (
|
||||
<div className='flex items-center justify-between'>
|
||||
<h2 className='font-medium'>Theme Mode</h2>
|
||||
<div className='flex gap-2'>
|
||||
<div className='tooltip tooltip-bottom' data-tip='Auto Mode'>
|
||||
<button
|
||||
key={theme}
|
||||
className={`btn ${selectedTheme === theme ? 'btn-active' : ''}`}
|
||||
onClick={() => setSelectedTheme(theme)}
|
||||
className={`btn btn-ghost btn-circle ${themeMode === 'auto' ? 'btn-active bg-base-300' : ''}`}
|
||||
onClick={() => updateThemeMode('auto')}
|
||||
>
|
||||
{theme}
|
||||
<TbSunMoon size={20} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className='tooltip tooltip-bottom' data-tip='Light Mode'>
|
||||
<button
|
||||
className={`btn btn-ghost btn-circle ${themeMode === 'light' ? 'btn-active bg-base-300' : ''}`}
|
||||
onClick={() => updateThemeMode('light')}
|
||||
>
|
||||
<MdOutlineLightMode size={20} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className='tooltip tooltip-bottom' data-tip='Dark Mode'>
|
||||
<button
|
||||
className={`btn btn-ghost btn-circle ${themeMode === 'dark' ? 'btn-active bg-base-300' : ''}`}
|
||||
onClick={() => updateThemeMode('dark')}
|
||||
>
|
||||
<MdOutlineDarkMode size={20} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 className='mb-2 font-medium'>Theme Color</h2>
|
||||
<div className='grid grid-cols-3 gap-4'>
|
||||
{themes.map(({ name, label, colors }) => (
|
||||
<label
|
||||
key={name}
|
||||
className={`relative flex cursor-pointer flex-col items-center justify-center rounded-lg p-4 shadow-md ${
|
||||
themeColor === name ? 'ring-2 ring-indigo-500 ring-offset-2' : ''
|
||||
}`}
|
||||
style={{
|
||||
backgroundColor: isDarkMode ? colors.dark['base-100'] : colors.light['base-100'],
|
||||
color: isDarkMode ? colors.dark['base-content'] : colors.light['base-content'],
|
||||
}}
|
||||
>
|
||||
<input
|
||||
type='radio'
|
||||
name='theme'
|
||||
value={name}
|
||||
checked={themeColor === name}
|
||||
onChange={() => updateThemeColor(name)}
|
||||
className='hidden'
|
||||
/>
|
||||
{themeColor === name ? (
|
||||
<MdRadioButtonChecked size={24} />
|
||||
) : (
|
||||
<MdRadioButtonUnchecked size={24} />
|
||||
)}
|
||||
<span>{label}</span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -17,10 +17,10 @@ const DialogMenu: React.FC<DialogMenuProps> = ({ toggleDropdown }) => {
|
||||
return (
|
||||
<div
|
||||
tabIndex={0}
|
||||
className='dropdown-content dropdown-right no-triangle z-20 mt-1 w-44 border bg-white shadow-2xl'
|
||||
className='dropdown-content dropdown-right no-triangle border-base-200 z-20 mt-1 w-44 border shadow-2xl'
|
||||
>
|
||||
<button
|
||||
className='flex w-full items-center justify-between rounded-md p-2 hover:bg-gray-100'
|
||||
className='hover:bg-base-200 text-base-content flex w-full items-center justify-between rounded-md p-2'
|
||||
onClick={handleToggleGlobal}
|
||||
>
|
||||
<div className='flex items-center'>
|
||||
|
||||
@@ -6,6 +6,7 @@ import FontDropdown from './FontDropDown';
|
||||
import { MONOSPACE_FONTS, SANS_SERIF_FONTS, SERIF_FONTS } from '@/services/constants';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
import { getStyles } from '@/utils/style';
|
||||
import { useTheme } from '@/hooks/useTheme';
|
||||
|
||||
interface FontFaceProps {
|
||||
className?: string;
|
||||
@@ -24,7 +25,7 @@ const handleFontFaceFont = (option: string, family: string) => {
|
||||
|
||||
const FontFace = ({ className, family, label, options, selected, onSelect }: FontFaceProps) => (
|
||||
<div className={clsx('config-item', className)}>
|
||||
<span className='text-gray-700'>{label}</span>
|
||||
<span className=''>{label}</span>
|
||||
<FontDropdown
|
||||
family={family}
|
||||
options={options}
|
||||
@@ -40,6 +41,7 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
const { setSettings, getView, getViewSettings, setViewSettings } = useReaderStore();
|
||||
const view = getView(bookKey);
|
||||
const viewSettings = getViewSettings(bookKey)!;
|
||||
const { themeCode } = useTheme();
|
||||
|
||||
const [defaultFontSize, setDefaultFontSize] = useState(viewSettings.defaultFontSize!);
|
||||
const [minFontSize, setMinFontSize] = useState(viewSettings.minimumFontSize!);
|
||||
@@ -56,7 +58,7 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
settings.globalViewSettings.defaultFont = defaultFont;
|
||||
setSettings(settings);
|
||||
}
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings));
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings, themeCode));
|
||||
}, [defaultFont]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -66,7 +68,7 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
settings.globalViewSettings.defaultFontSize = defaultFontSize;
|
||||
setSettings(settings);
|
||||
}
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings));
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings, themeCode));
|
||||
}, [defaultFontSize]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -76,7 +78,7 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
settings.globalViewSettings.minimumFontSize = minFontSize;
|
||||
setSettings(settings);
|
||||
}
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings));
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings, themeCode));
|
||||
}, [minFontSize]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -86,7 +88,7 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
settings.globalViewSettings.serifFont = serifFont;
|
||||
setSettings(settings);
|
||||
}
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings));
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings, themeCode));
|
||||
}, [serifFont]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -96,7 +98,7 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
settings.globalViewSettings.sansSerifFont = sansSerifFont;
|
||||
setSettings(settings);
|
||||
}
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings));
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings, themeCode));
|
||||
}, [sansSerifFont]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -106,7 +108,7 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
settings.globalViewSettings.monospaceFont = monospaceFont;
|
||||
setSettings(settings);
|
||||
}
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings));
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings, themeCode));
|
||||
}, [monospaceFont]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -116,7 +118,7 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
settings.globalViewSettings.overrideFont = overrideFont;
|
||||
setSettings(settings);
|
||||
}
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings));
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings, themeCode));
|
||||
}, [overrideFont]);
|
||||
|
||||
const handleFontFamilyFont = (option: string) => {
|
||||
@@ -136,8 +138,8 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
<div className='my-4 w-full space-y-6'>
|
||||
<div className='w-full'>
|
||||
<h2 className='mb-2 font-medium'>Font Size</h2>
|
||||
<div className='card bg-base-100 border shadow'>
|
||||
<div className='divide-y'>
|
||||
<div className='card border-base-200 border shadow'>
|
||||
<div className='divide-base-200 divide-y'>
|
||||
<NumberInput
|
||||
className='config-item-top'
|
||||
label='Default Font Size'
|
||||
@@ -160,10 +162,10 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
|
||||
<div className='w-full'>
|
||||
<h2 className='mb-2 font-medium'>Font Family</h2>
|
||||
<div className='card bg-base-100 border shadow'>
|
||||
<div className='divide-y'>
|
||||
<div className='card border-base-200 border shadow'>
|
||||
<div className='divide-base-200 divide-y'>
|
||||
<div className='config-item config-item-top'>
|
||||
<span className='text-gray-700'>Default Font</span>
|
||||
<span className=''>Default Font</span>
|
||||
<FontDropdown
|
||||
options={fontFamilyOptions}
|
||||
selected={defaultFont}
|
||||
@@ -173,7 +175,7 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
</div>
|
||||
|
||||
<div className='config-item config-item-bottom'>
|
||||
<span className='text-gray-700'>Override Publisher Font</span>
|
||||
<span className=''>Override Publisher Font</span>
|
||||
<input
|
||||
type='checkbox'
|
||||
className='toggle'
|
||||
@@ -187,8 +189,8 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
|
||||
<div className='w-full'>
|
||||
<h2 className='mb-2 font-medium'>Font Face</h2>
|
||||
<div className='card bg-base-100 border shadow'>
|
||||
<div className='divide-y'>
|
||||
<div className='card border-base-200 border shadow'>
|
||||
<div className='divide-base-200 divide-y'>
|
||||
<FontFace
|
||||
className='config-item-top'
|
||||
family='serif'
|
||||
|
||||
@@ -3,12 +3,14 @@ import { useReaderStore } from '@/store/readerStore';
|
||||
import { getStyles } from '@/utils/style';
|
||||
import { ONE_COLUMN_MAX_INLINE_SIZE } from '@/services/constants';
|
||||
import NumberInput from './NumberInput';
|
||||
import { useTheme } from '@/hooks/useTheme';
|
||||
|
||||
const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
const { settings, isFontLayoutSettingsGlobal } = useReaderStore();
|
||||
const { setSettings, getView, getViewSettings, setViewSettings } = useReaderStore();
|
||||
const view = getView(bookKey);
|
||||
const viewSettings = getViewSettings(bookKey)!;
|
||||
const { themeCode } = useTheme();
|
||||
|
||||
const [lineHeight, setLineHeight] = useState(viewSettings.lineHeight!);
|
||||
const [fullJustification, setFullJustification] = useState(viewSettings.fullJustification!);
|
||||
@@ -26,7 +28,7 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
settings.globalViewSettings.lineHeight = lineHeight;
|
||||
setSettings(settings);
|
||||
}
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings));
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings, themeCode));
|
||||
}, [lineHeight]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -36,7 +38,7 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
settings.globalViewSettings.fullJustification = fullJustification;
|
||||
setSettings(settings);
|
||||
}
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings));
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings, themeCode));
|
||||
}, [fullJustification]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -46,7 +48,7 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
settings.globalViewSettings.hyphenation = hyphenation;
|
||||
setSettings(settings);
|
||||
}
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings));
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings, themeCode));
|
||||
}, [hyphenation]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -100,8 +102,8 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
<div className='my-4 w-full space-y-6'>
|
||||
<div className='w-full'>
|
||||
<h2 className='mb-2 font-medium'>Paragraph</h2>
|
||||
<div className='card bg-base-100 border shadow'>
|
||||
<div className='divide-y'>
|
||||
<div className='card bg-base-100 border-base-200 border shadow'>
|
||||
<div className='divide-base-200 divide-y'>
|
||||
<NumberInput
|
||||
className='config-item-top'
|
||||
label='Line Height'
|
||||
@@ -112,7 +114,7 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
step={0.1}
|
||||
/>
|
||||
<div className='config-item config-item-bottom'>
|
||||
<span className='text-gray-700'>Full Justification</span>
|
||||
<span className=''>Full Justification</span>
|
||||
<input
|
||||
type='checkbox'
|
||||
className='toggle'
|
||||
@@ -121,7 +123,7 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
/>
|
||||
</div>
|
||||
<div className='config-item config-item-bottom'>
|
||||
<span className='text-gray-700'>Hyphenation</span>
|
||||
<span className=''>Hyphenation</span>
|
||||
<input
|
||||
type='checkbox'
|
||||
className='toggle'
|
||||
@@ -135,8 +137,8 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
|
||||
<div className='w-full'>
|
||||
<h2 className='mb-2 font-medium'>Page</h2>
|
||||
<div className='card bg-base-100 border shadow'>
|
||||
<div className='divide-y'>
|
||||
<div className='card bg-base-100 border-base-200 border shadow'>
|
||||
<div className='divide-base-200 divide-y'>
|
||||
<NumberInput
|
||||
className='config-item-top'
|
||||
label='Margins (px)'
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
import cssbeautify from 'cssbeautify';
|
||||
import { getStyles } from '@/utils/style';
|
||||
import { useTheme } from '@/hooks/useTheme';
|
||||
|
||||
const cssRegex =
|
||||
/((?:\s*)([\w#.@*,:\-.:>+~\[\]\"=(),*\s]+)\s*{(?:[\s]*)((?:[A-Za-z\- \s]+[:]\s*['"0-9\w .,\/\()\-!#%]+;?)*)*\s*}(?:\s*))/gim;
|
||||
@@ -10,6 +11,7 @@ const MiscPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
const { settings, isFontLayoutSettingsGlobal } = useReaderStore();
|
||||
const { setSettings, getView, getViewSettings, setViewSettings } = useReaderStore();
|
||||
const viewSettings = getViewSettings(bookKey)!;
|
||||
const { themeCode } = useTheme();
|
||||
|
||||
const [animated, setAnimated] = useState(viewSettings.animated!);
|
||||
const [userStylesheet, setUserStylesheet] = useState(viewSettings.userStylesheet!);
|
||||
@@ -43,7 +45,7 @@ const MiscPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
settings.globalViewSettings.userStylesheet = formattedCSS;
|
||||
setSettings(settings);
|
||||
}
|
||||
getView(bookKey)?.renderer.setStyles?.(getStyles(viewSettings));
|
||||
getView(bookKey)?.renderer.setStyles?.(getStyles(viewSettings, themeCode));
|
||||
} catch (err) {
|
||||
setError('Invalid CSS: Please check your input.');
|
||||
console.log('CSS Error:', err);
|
||||
|
||||
@@ -55,14 +55,14 @@ const NumberInput: React.FC<NumberInputProps> = ({
|
||||
|
||||
return (
|
||||
<div className={clsx('config-item', className)}>
|
||||
<span className='text-gray-700'>{label}</span>
|
||||
<div className='flex items-center gap-2'>
|
||||
<span className='text-base-content'>{label}</span>
|
||||
<div className='text-base-content flex items-center gap-2'>
|
||||
<input
|
||||
type='text'
|
||||
value={localValue}
|
||||
onChange={handleChange}
|
||||
onBlur={handleOnBlur}
|
||||
className='input input-ghost settings-content w-20 max-w-xs rounded border-0 bg-transparent px-3 py-1 text-right !outline-none'
|
||||
className='input input-ghost settings-content text-base-content w-20 max-w-xs rounded border-0 bg-transparent px-3 py-1 text-right !outline-none'
|
||||
onFocus={(e) => e.target.select()}
|
||||
/>
|
||||
<button
|
||||
|
||||
@@ -25,28 +25,28 @@ const SettingsDialog: React.FC<{ bookKey: string; config: BookConfig }> = ({ boo
|
||||
<div className='dialog-header bg-base-100 sticky top-0 z-10 flex items-center justify-center px-4 pt-2'>
|
||||
<div className='dialog-tabs flex h-10 max-w-[80%] flex-grow items-center justify-around'>
|
||||
<button
|
||||
className={`btn btn-ghost h-8 min-h-8 px-4 ${activePanel === 'Font' ? 'btn-active' : ''}`}
|
||||
className={`btn btn-ghost text-base-content h-8 min-h-8 px-4 ${activePanel === 'Font' ? 'btn-active' : ''}`}
|
||||
onClick={() => setActivePanel('Font')}
|
||||
>
|
||||
<RiFontSize size={20} className='mr-0' />
|
||||
Font
|
||||
</button>
|
||||
<button
|
||||
className={`btn btn-ghost h-8 min-h-8 px-4 ${activePanel === 'Layout' ? 'btn-active' : ''}`}
|
||||
className={`btn btn-ghost text-base-content h-8 min-h-8 px-4 ${activePanel === 'Layout' ? 'btn-active' : ''}`}
|
||||
onClick={() => setActivePanel('Layout')}
|
||||
>
|
||||
<RiDashboardLine size={20} className='mr-0' />
|
||||
Layout
|
||||
</button>
|
||||
<button
|
||||
className={`btn btn-ghost h-8 min-h-8 px-4 ${activePanel === 'Color' ? 'btn-active' : ''}`}
|
||||
className={`btn btn-ghost text-base-content h-8 min-h-8 px-4 ${activePanel === 'Color' ? 'btn-active' : ''}`}
|
||||
onClick={() => setActivePanel('Color')}
|
||||
>
|
||||
<VscSymbolColor size={20} className='mr-0' />
|
||||
Color
|
||||
</button>
|
||||
<button
|
||||
className={`btn btn-ghost h-8 min-h-8 px-4 ${activePanel === 'Misc' ? 'btn-active' : ''}`}
|
||||
className={`btn btn-ghost text-base-content h-8 min-h-8 px-4 ${activePanel === 'Misc' ? 'btn-active' : ''}`}
|
||||
onClick={() => setActivePanel('Misc')}
|
||||
>
|
||||
<IoAccessibilityOutline size={20} className='mr-0' />
|
||||
@@ -70,7 +70,7 @@ const SettingsDialog: React.FC<{ bookKey: string; config: BookConfig }> = ({ boo
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='mt-2 flex-grow overflow-y-auto px-[10%]'>
|
||||
<div className='text-base-content mt-2 flex-grow overflow-y-auto px-[10%]'>
|
||||
{activePanel === 'Font' && <FontPanel bookKey={bookKey} />}
|
||||
{activePanel === 'Layout' && <LayoutPanel bookKey={bookKey} />}
|
||||
{activePanel === 'Color' && <ColorPanel bookKey={bookKey} />}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import Image from 'next/image';
|
||||
import { MdInfoOutline } from 'react-icons/md';
|
||||
import { formatAuthors } from '@/utils/book';
|
||||
|
||||
@@ -11,20 +12,22 @@ interface BookCardProps {
|
||||
const BookCard: React.FC<BookCardProps> = ({ cover, title, author }) => {
|
||||
return (
|
||||
<div className='flex h-20 w-full items-center'>
|
||||
<img
|
||||
<Image
|
||||
src={cover}
|
||||
alt='Book cover'
|
||||
width={56}
|
||||
height={80}
|
||||
className='mr-4 aspect-auto max-h-20 w-[15%] max-w-14 rounded-sm object-cover shadow-md'
|
||||
/>
|
||||
<div className='min-w-0 flex-1'>
|
||||
<h4 className='line-clamp-2 w-[90%] text-sm font-semibold'>{title}</h4>
|
||||
<p className='truncate text-sm text-gray-600'>{formatAuthors(author)}</p>
|
||||
<p className='text-neutral-content truncate text-sm'>{formatAuthors(author)}</p>
|
||||
</div>
|
||||
<button
|
||||
className='btn btn-ghost h-6 min-h-6 w-6 rounded-full p-0 transition-colors hover:bg-gray-300'
|
||||
className='btn btn-ghost hover:bg-base-300 h-6 min-h-6 w-6 rounded-full p-0 transition-colors'
|
||||
aria-label='More info'
|
||||
>
|
||||
<MdInfoOutline size={18} className='text-gray-600' />
|
||||
<MdInfoOutline size={18} className='fill-base-content' />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
import MenuItem from '@/components/MenuItem';
|
||||
import useBooksManager from '../../hooks/useBooksManager';
|
||||
|
||||
interface BookMenuProps {
|
||||
@@ -22,29 +24,12 @@ const BookMenu: React.FC<BookMenuProps> = ({ setIsDropdownOpen }) => {
|
||||
return (
|
||||
<div
|
||||
tabIndex={0}
|
||||
className='book-menu dropdown-content dropdown-center z-20 mt-3 w-56 border bg-white shadow-2xl'
|
||||
className='book-menu dropdown-content dropdown-center border-base-100 z-20 mt-3 w-56 shadow-2xl'
|
||||
>
|
||||
<button
|
||||
className='flex w-full items-center justify-between rounded-md p-2 hover:bg-gray-100'
|
||||
onClick={handleOpenSplitView}
|
||||
>
|
||||
<span className='ml-2'>Parallel Read</span>
|
||||
<span className='text-sm text-gray-400'>Shift+P</span>
|
||||
</button>
|
||||
<button
|
||||
className='flex w-full items-center justify-between rounded-md p-2 hover:bg-gray-100'
|
||||
onClick={handleReloadPage}
|
||||
>
|
||||
<span className='ml-2'>Reload Page</span>
|
||||
<span className='text-sm text-gray-400'>Shift+R</span>
|
||||
</button>
|
||||
<hr className='my-1' />
|
||||
<button
|
||||
className='flex w-full items-center rounded-md p-2 hover:bg-gray-100'
|
||||
onClick={showAboutReadest}
|
||||
>
|
||||
<span className='ml-2'>About Readest</span>
|
||||
</button>
|
||||
<MenuItem label='Parallel Read' shortcut='Shift+P' onClick={handleOpenSplitView} />
|
||||
<MenuItem label='Reload Page' shortcut='Shift+R' onClick={handleReloadPage} />
|
||||
<hr className='border-base-200 my-1' />
|
||||
<MenuItem label='About Readest' onClick={showAboutReadest} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -51,7 +51,7 @@ const BooknoteItem: React.FC<BooknoteItemProps> = ({ bookKey, item, editable = f
|
||||
className={clsx(
|
||||
'border-base-300 my-2 cursor-pointer rounded-lg p-2 text-sm',
|
||||
editable && 'collapse-arrow collapse',
|
||||
isCurrent ? 'bg-base-300 hover:bg-gray-300/70' : 'hover:bg-base-300 bg-white',
|
||||
isCurrent ? 'bg-base-300/85 hover:bg-base-300' : 'hover:bg-base-200 bg-base-100',
|
||||
)}
|
||||
tabIndex={0}
|
||||
onClick={handleClickItem}
|
||||
|
||||
@@ -33,22 +33,22 @@ const SidebarHeader: React.FC<{
|
||||
onClick={onToggleSearchBar}
|
||||
className={clsx(
|
||||
'btn btn-ghost left-0 h-8 min-h-8 w-8 p-0',
|
||||
isSearchBarVisible ? 'bg-gray-300 hover:bg-gray-300' : 'bg-base-200 hover:bg-base-200',
|
||||
isSearchBarVisible ? 'bg-base-300' : '',
|
||||
)}
|
||||
>
|
||||
<FiSearch size={18} />
|
||||
<FiSearch size={18} className='text-base-content' />
|
||||
</button>
|
||||
<Dropdown
|
||||
className='dropdown-bottom flex justify-center'
|
||||
buttonClassName='btn btn-ghost h-8 min-h-8 w-8 p-0'
|
||||
toggleButton={<MdOutlineMenu size={20} />}
|
||||
toggleButton={<MdOutlineMenu size={20} className='fill-base-content' />}
|
||||
>
|
||||
<BookMenu />
|
||||
</Dropdown>
|
||||
<div className='right-0 flex h-8 w-8 items-center justify-center'>
|
||||
<button
|
||||
onClick={onTogglePin}
|
||||
className={`${isPinned ? 'bg-gray-300' : 'bg-base-300'} btn btn-ghost btn-circle h-6 min-h-6 w-6`}
|
||||
className={`${isPinned ? 'bg-base-300' : 'bg-base-300/65'} btn btn-ghost btn-circle h-6 min-h-6 w-6`}
|
||||
>
|
||||
{isPinned ? <MdPushPin size={14} /> : <MdOutlinePushPin size={14} />}
|
||||
</button>
|
||||
|
||||
@@ -38,7 +38,7 @@ const SideBar: React.FC<{
|
||||
settings.globalReadSettings.isSideBarPinned,
|
||||
);
|
||||
|
||||
const onSearchEvent = (event: CustomEvent) => {
|
||||
const onSearchEvent = async (event: CustomEvent) => {
|
||||
const { term } = event.detail;
|
||||
setSideBarVisible(true);
|
||||
setIsSearchBarVisible(true);
|
||||
@@ -123,7 +123,7 @@ const SideBar: React.FC<{
|
||||
onSearchResultChange={setSearchResults}
|
||||
/>
|
||||
</div>
|
||||
<div className='border-b px-3'>
|
||||
<div className='border-base-300/50 border-b px-3'>
|
||||
<BookCard cover={book.coverImageUrl!} title={book.title} author={book.author} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { TOCItem } from '@/libs/document';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
import { findParentPath } from '@/utils/toc';
|
||||
import { getContentMd5 } from '@/utils/misc';
|
||||
import clsx from 'clsx';
|
||||
|
||||
const createExpanderIcon = (isExpanded: boolean) => {
|
||||
return (
|
||||
@@ -11,8 +12,12 @@ const createExpanderIcon = (isExpanded: boolean) => {
|
||||
viewBox='0 0 8 10'
|
||||
width='8'
|
||||
height='10'
|
||||
className={`transform transition-transform ${isExpanded ? 'rotate-90' : 'rotate-0'}`}
|
||||
className={clsx(
|
||||
'text-base-content transform transition-transform',
|
||||
isExpanded ? 'rotate-90' : 'rotate-0',
|
||||
)}
|
||||
style={{ transformOrigin: 'center' }}
|
||||
fill='currentColor'
|
||||
>
|
||||
<polygon points='0 0, 8 5, 0 10' />
|
||||
</svg>
|
||||
@@ -59,7 +64,7 @@ const TOCItemView: React.FC<{
|
||||
aria-selected={isActive ? 'true' : 'false'}
|
||||
data-href={item.href ? getContentMd5(item.href) : undefined}
|
||||
className={`flex w-full cursor-pointer items-center rounded-md py-2 ${
|
||||
isActive ? 'bg-gray-300 hover:bg-gray-400' : 'hover:bg-gray-300'
|
||||
isActive ? 'bg-base-300/85 hover:bg-base-300' : 'hover:bg-base-300/85'
|
||||
}`}
|
||||
>
|
||||
{item.subitems && (
|
||||
|
||||
@@ -5,7 +5,7 @@ const TabNavigation: React.FC<{
|
||||
activeTab: string;
|
||||
onTabChange: (tab: string) => void;
|
||||
}> = ({ activeTab, onTabChange }) => (
|
||||
<div className='bottom-tab flex w-full border-t'>
|
||||
<div className='bottom-tab border-base-300/50 flex w-full border-t'>
|
||||
{['toc', 'annotations', 'bookmarks'].map((tab) => (
|
||||
<button
|
||||
key={tab}
|
||||
|
||||
Reference in New Issue
Block a user