Add dropdown menu for book settings in sidebar

This commit is contained in:
chrox
2024-10-29 18:48:43 +01:00
parent f341bd4b5e
commit 16857345b4
4 changed files with 187 additions and 7 deletions
+57 -1
View File
@@ -71,4 +71,60 @@ foliate-view {
.rounded-window-right {
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
}
}
.dropdown-content {
position: relative;
background-color: white;
padding: 10px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15);
border-radius: 14px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
font-size: 14px;
@apply text-gray-700;
}
.dropdown-content::before,
.dropdown-content::after {
content: '';
position: absolute;
top: -12px;
width: 0;
height: 0;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
}
.dropdown-content::before {
border-bottom: 12px solid white;
z-index: 1;
}
.dropdown-content::after {
top: -14px;
border-bottom: 13px solid rgba(0, 0, 0, 0.02);
z-index: 0;
}
.dropdown-left::before,
.dropdown-left::after {
left: 20px;
transform: translateX(0);
}
.dropdown-center::before,
.dropdown-center::after {
left: 50%;
transform: translateX(-50%);
}
.dropdown-right::before,
.dropdown-right::after {
right: 20px;
transform: translateX(0);
}
.dropdown-content.no-triangle::before,
.dropdown-content.no-triangle::after {
display: none;
}
@@ -0,0 +1,56 @@
import React from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
interface BookMenuProps {
bookId: string;
toggleDropdown?: () => void;
}
const BookMenu: React.FC<BookMenuProps> = ({ bookId, toggleDropdown }) => {
const router = useRouter();
const searchParams = useSearchParams();
const openSplitView = () => {
const params = new URLSearchParams(searchParams.toString());
const ids = (params.get('ids') || '').split(',');
const updatedIds = [...ids, bookId].join(',');
params.set('ids', updatedIds);
router.push(`?${params.toString()}`);
toggleDropdown?.();
};
const reloadPage = () => {
window.location.reload();
toggleDropdown?.();
};
const showAboutReadest = () => {
toggleDropdown?.();
};
return (
<div
tabIndex={0}
className='book-menu dropdown-content dropdown-center z-20 mt-3 w-56 border bg-white shadow-2xl'
>
<button
className='flex w-full items-center rounded-md p-2 hover:bg-gray-100'
onClick={openSplitView}
>
<span className='ml-2'>Split View</span>
</button>
<button
className='flex w-full items-center rounded-md p-2 hover:bg-gray-100'
onClick={reloadPage}
>
<span className='ml-2'>Reload Page</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>
</div>
);
};
export default BookMenu;
@@ -1,18 +1,21 @@
import React, { useState, useEffect } from 'react';
import clsx from 'clsx';
import {
MdSearch,
MdOutlinePushPin,
MdPushPin,
MdToc,
MdEditNote,
MdBookmarkBorder,
MdOutlineMenu,
} from 'react-icons/md';
import { GiBookshelf } from 'react-icons/gi';
import { CiSearch } from 'react-icons/ci';
import { BookState, DEFAULT_BOOK_STATE, useReaderStore } from '@/store/readerStore';
import Dropdown from '@/components/Dropdown';
import BookCard from './BookCard';
import TOCView from './TOCView';
import { BookState, DEFAULT_BOOK_STATE, useReaderStore } from '@/store/readerStore';
import BookMenu from './BookMenu';
const MIN_SIDEBAR_WIDTH = 0.15;
const MAX_SIDEBAR_WIDTH = 0.45;
@@ -84,7 +87,7 @@ const SideBar: React.FC<{
<div
className={clsx(
'sidebar-container bg-base-200 rounded-window-left z-20 h-full select-none',
!isPinned && 'shadow-',
!isPinned && 'shadow-2xl',
)}
style={{
width: `${width}`,
@@ -94,16 +97,23 @@ const SideBar: React.FC<{
}}
>
<div className={'sidebar h-full'}>
<div className='flex h-11 items-center justify-between pl-1.5 pr-3'>
<div className='sidebar-header 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'>
<div className='flex size-[50%] 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' />
<CiSearch size={20} className='fill-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} />}
>
<BookMenu bookId={book.hash} />
</Dropdown>
<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`}
@@ -0,0 +1,58 @@
import clsx from 'clsx';
import React, { useRef, useState, isValidElement, ReactElement } from 'react';
interface DropdownProps {
className?: string;
buttonClassName?: string;
toggleButton: React.ReactNode;
children: ReactElement<{ toggleDropdown: () => void }>;
onToggle?: (isOpen: boolean) => void;
}
const Dropdown: React.FC<DropdownProps> = ({
className,
buttonClassName,
toggleButton,
children,
onToggle,
}) => {
const dropdownRef = useRef<HTMLDivElement>(null);
const [isOpen, setIsOpen] = useState(false);
const toggleDropdown = () => {
const newIsOpen = !isOpen;
setIsOpen(newIsOpen);
onToggle?.(newIsOpen);
};
const handleBlur = (event: React.FocusEvent<HTMLDivElement>) => {
if (!dropdownRef.current?.contains(event.relatedTarget as Node)) {
setIsOpen(false);
onToggle?.(false);
}
};
const childrenWithToggle = isValidElement(children)
? React.cloneElement(children, { toggleDropdown })
: children;
return (
<div className={clsx('dropdown', className)} ref={dropdownRef}>
<div
tabIndex={-1}
onClick={toggleDropdown}
onBlur={handleBlur}
className={clsx(
'dropdown-toggle hover:bg-gray-300',
buttonClassName,
isOpen && 'bg-base-300',
)}
>
{toggleButton}
</div>
{isOpen && childrenWithToggle}
</div>
);
};
export default Dropdown;