forked from akai/readest
Fix keyboard shortcuts for pagination
This commit is contained in:
@@ -84,9 +84,18 @@ const FoliateViewer: React.FC<{
|
||||
);
|
||||
};
|
||||
|
||||
const handleKeyboardPagination = (e: KeyboardEvent) => {
|
||||
if (e.key === 'ArrowLeft' || e.key === 'PageUp' || e.key === 'h') {
|
||||
view?.goLeft();
|
||||
} else if (e.key === 'ArrowRight' || e.key === 'PageDown' || e.key === 'l') {
|
||||
view?.goRight();
|
||||
}
|
||||
};
|
||||
|
||||
const docLoadHandler = (event: Event) => {
|
||||
const detail = (event as CustomEvent).detail;
|
||||
if (handleKeyDown) detail.doc?.addEventListener('keydown', handleKeyDown);
|
||||
detail.doc?.addEventListener('keydown', handleKeyDown);
|
||||
detail.doc?.addEventListener('keydown', handleKeyboardPagination);
|
||||
};
|
||||
|
||||
useFoliateEvents(view, { onLoad: docLoadHandler, onRelocate: progressRelocateHandler });
|
||||
|
||||
@@ -49,10 +49,15 @@ const ReaderContent = () => {
|
||||
return getKey(ids[nextIndex]!, nextIndex);
|
||||
});
|
||||
e.preventDefault();
|
||||
} else if (e.key === 't') {
|
||||
setSideBarVisibility((prev) => !prev);
|
||||
}
|
||||
if (e.key === 'ArrowLeft') {
|
||||
};
|
||||
|
||||
const handleKeyboardPagination = (e: KeyboardEvent) => {
|
||||
if (e.key === 'ArrowLeft' || e.key === 'PageUp' || e.key === 'h') {
|
||||
getFoliateView(sideBarBookKey)?.goLeft();
|
||||
} else if (e.key === 'ArrowRight') {
|
||||
} else if (e.key === 'ArrowRight' || e.key === 'PageDown' || e.key === 'l') {
|
||||
getFoliateView(sideBarBookKey)?.goRight();
|
||||
}
|
||||
};
|
||||
@@ -72,8 +77,10 @@ const ReaderContent = () => {
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
window.addEventListener('keydown', handleKeyboardPagination);
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
window.removeEventListener('keydown', handleKeyboardPagination);
|
||||
};
|
||||
}, [sideBarBookKey]);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import {
|
||||
MdSearch,
|
||||
MdOutlinePushPin,
|
||||
@@ -79,86 +80,78 @@ const SideBar: React.FC<{
|
||||
|
||||
const { book, bookDoc } = bookState;
|
||||
|
||||
return (
|
||||
isVisible && (
|
||||
<div
|
||||
className='sidebar-container bg-base-200 rounded-window-left z-20 h-full select-none'
|
||||
style={{
|
||||
width: `${width}`,
|
||||
minWidth: `${MIN_SIDEBAR_WIDTH * 100}%`,
|
||||
maxWidth: `${MAX_SIDEBAR_WIDTH * 100}%`,
|
||||
position: isPinned ? 'relative' : 'absolute',
|
||||
}}
|
||||
>
|
||||
<div className={'sidebar h-full'}>
|
||||
<div className='flex h-11 items-center justify-between pl-1.5 pr-3'>
|
||||
<div className='flex items-center'>
|
||||
<button className='btn btn-ghost h-8 min-h-8 w-8 p-0' onClick={onGoToLibrary}>
|
||||
<GiBookshelf size={20} className='fill-base-content' />
|
||||
</button>
|
||||
</div>
|
||||
<div className='flex size-[30%] min-w-20 items-center justify-between'>
|
||||
<button className='btn btn-ghost left-0 h-8 min-h-8 w-8 p-0'>
|
||||
<MdSearch size={20} className='fill-base-content' />
|
||||
</button>
|
||||
<button
|
||||
onClick={onTogglePin}
|
||||
className={`${isPinned ? 'bg-gray-300' : 'bg-base-300'} btn btn-ghost btn-circle right-0 h-6 min-h-6 w-6`}
|
||||
>
|
||||
{isPinned ? <MdPushPin size={14} /> : <MdOutlinePushPin size={14} />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className='border-b px-3'>
|
||||
<BookCard cover={book.coverImageUrl!} title={book.title} author={book.author} />
|
||||
</div>
|
||||
<div className='sidebar-content overflow-y-auto shadow-inner'>
|
||||
{activeTab === 'toc' && bookDoc!.toc && (
|
||||
<TOCView toc={bookDoc!.toc} bookKey={bookKey} currentHref={currentHref} />
|
||||
)}
|
||||
{activeTab === 'bookmarks' && <div>Bookmarks</div>}
|
||||
{activeTab === 'annotations' && <div>Annotations</div>}
|
||||
</div>
|
||||
<div className='bottom-tab absolute bottom-0 flex w-full border-t'>
|
||||
<button
|
||||
className={`m-1.5 flex-1 rounded-md p-2 ${activeTab === 'toc' ? 'bg-base-300' : ''}`}
|
||||
onClick={() => setActiveTab('toc')}
|
||||
>
|
||||
<MdToc size={20} className='mx-auto' />
|
||||
</button>
|
||||
<button
|
||||
className={`m-1.5 flex-1 rounded-md p-2 ${activeTab === 'annotations' ? 'bg-base-300' : ''}`}
|
||||
onClick={() => setActiveTab('annotations')}
|
||||
>
|
||||
<MdEditNote size={20} className='mx-auto' />
|
||||
</button>
|
||||
<button
|
||||
className={`m-1.5 flex-1 rounded-md p-2 ${activeTab === 'bookmarks' ? 'bg-base-300' : ''}`}
|
||||
onClick={() => setActiveTab('bookmarks')}
|
||||
>
|
||||
<MdBookmarkBorder size={20} className='mx-auto' />
|
||||
return isVisible ? (
|
||||
<div
|
||||
className={clsx(
|
||||
'sidebar-container bg-base-200 rounded-window-left z-20 h-full select-none',
|
||||
!isPinned && 'shadow-',
|
||||
)}
|
||||
style={{
|
||||
width: `${width}`,
|
||||
minWidth: `${MIN_SIDEBAR_WIDTH * 100}%`,
|
||||
maxWidth: `${MAX_SIDEBAR_WIDTH * 100}%`,
|
||||
position: isPinned ? 'relative' : 'absolute',
|
||||
}}
|
||||
>
|
||||
<div className={'sidebar h-full'}>
|
||||
<div className='flex h-11 items-center justify-between pl-1.5 pr-3'>
|
||||
<div className='flex items-center'>
|
||||
<button className='btn btn-ghost h-8 min-h-8 w-8 p-0' onClick={onGoToLibrary}>
|
||||
<GiBookshelf size={20} className='fill-base-content' />
|
||||
</button>
|
||||
</div>
|
||||
<div className='flex size-[30%] min-w-20 items-center justify-between'>
|
||||
<button className='btn btn-ghost left-0 h-8 min-h-8 w-8 p-0'>
|
||||
<MdSearch size={20} className='fill-base-content' />
|
||||
</button>
|
||||
<button
|
||||
onClick={onTogglePin}
|
||||
className={`${isPinned ? 'bg-gray-300' : 'bg-base-300'} btn btn-ghost btn-circle right-0 h-6 min-h-6 w-6`}
|
||||
>
|
||||
{isPinned ? <MdPushPin size={14} /> : <MdOutlinePushPin size={14} />}
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
className='drag-bar absolute right-0 top-0 h-full w-0.5 cursor-col-resize'
|
||||
onMouseDown={handleMouseDown}
|
||||
></div>
|
||||
</div>
|
||||
{!isPinned && (
|
||||
<div
|
||||
className='overlay fixed top-0'
|
||||
style={{
|
||||
left: width,
|
||||
width: `calc(100% - ${width})`,
|
||||
height: '100%',
|
||||
background: 'rgba(0, 0, 0, 0.2)',
|
||||
}}
|
||||
onClick={() => handleClickOverlay()}
|
||||
/>
|
||||
)}
|
||||
<div className='border-b px-3'>
|
||||
<BookCard cover={book.coverImageUrl!} title={book.title} author={book.author} />
|
||||
</div>
|
||||
<div className='sidebar-content overflow-y-auto shadow-inner'>
|
||||
{activeTab === 'toc' && bookDoc!.toc && (
|
||||
<TOCView toc={bookDoc!.toc} bookKey={bookKey} currentHref={currentHref} />
|
||||
)}
|
||||
{activeTab === 'annotations' && <div>Annotations</div>}
|
||||
{activeTab === 'bookmarks' && <div>Bookmarks</div>}
|
||||
</div>
|
||||
<div className='bottom-tab absolute bottom-0 flex w-full border-t'>
|
||||
{['toc', 'annotations', 'bookmarks'].map((tab) => (
|
||||
<button
|
||||
key={tab}
|
||||
className={`m-1.5 flex-1 rounded-md p-2 ${activeTab === tab ? 'bg-base-300' : ''}`}
|
||||
onClick={() => setActiveTab(tab)}
|
||||
>
|
||||
{tab === 'toc' && <MdToc size={20} className='mx-auto' />}
|
||||
{tab === 'annotations' && <MdEditNote size={20} className='mx-auto' />}
|
||||
{tab === 'bookmarks' && <MdBookmarkBorder size={20} className='mx-auto' />}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div
|
||||
className='drag-bar absolute right-0 top-0 h-full w-0.5 cursor-col-resize'
|
||||
onMouseDown={handleMouseDown}
|
||||
></div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
{!isPinned && (
|
||||
<div
|
||||
className='overlay fixed top-0 h-full bg-black/20'
|
||||
style={{
|
||||
left: width,
|
||||
width: `calc(100% - ${width})`,
|
||||
}}
|
||||
onClick={() => handleClickOverlay()}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : null;
|
||||
};
|
||||
|
||||
export default SideBar;
|
||||
|
||||
Reference in New Issue
Block a user