Files
dpage-editor/src/pages/viewer/components/BookViewer.tsx
T
hamid zarghami 84f93463a3 base flip
2026-01-01 12:02:24 +03:30

126 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { type FC, useRef, useCallback, useState } from 'react';
import HTMLFlipBook from 'react-pageflip';
import { type PageData } from '../types';
import BookPage from './BookPage';
import { ArrowLeft2, ArrowRight2 } from 'iconsax-react';
type BookViewerProps = {
pages: PageData[];
};
interface PageFlipAPI {
flipNext: () => void;
flipPrev: () => void;
}
interface FlipBookInstance {
pageFlip: () => PageFlipAPI | undefined;
}
interface FlipEvent {
data: number;
}
/**
* کامپوننت اصلی کتابخوان با استفاده از react-pageflip
* این کامپوننت از HTMLFlipBook استفاده می‌کند که یک wrapper برای PageFlip است
* از HTML برای رندر صفحات استفاده می‌شود (نه Canvas) برای سازگاری با Konva در آینده
*/
const BookViewer: FC<BookViewerProps> = ({ pages }) => {
const bookRef = useRef<FlipBookInstance | null>(null);
const [currentPage, setCurrentPage] = useState(0);
const handlePageFlip = useCallback((e: FlipEvent) => {
const pageNum = e.data;
setCurrentPage(pageNum);
}, []);
const goToNextPage = useCallback(() => {
bookRef.current?.pageFlip()?.flipNext();
}, []);
const goToPrevPage = useCallback(() => {
bookRef.current?.pageFlip()?.flipPrev();
}, []);
return (
<div className="flex flex-col items-center gap-4 md:gap-6 w-full h-full" dir="rtl">
{/* نمایش کتاب */}
<div className="relative w-full flex justify-center flex-1 items-center">
{/* Border و Shadow عمودی در وسط کتاب */}
<div
className="absolute inset-y-0 left-1/2 -translate-x-1/2 w-px border-r border-gray-300 pointer-events-none z-50"
style={{
boxShadow: '-5px 0 15px rgba(0, 0, 0, 0.15), 5px 0 15px rgba(0, 0, 0, 0.15)'
}}
/>
<HTMLFlipBook
ref={bookRef}
width={794}
height={1123}
size="stretch"
minWidth={315}
minHeight={400}
maxWidth={1000}
maxHeight={1533}
drawShadow={true}
flippingTime={800}
usePortrait={true}
startPage={0}
autoSize={true}
maxShadowOpacity={0.5}
showCover={true}
mobileScrollSupport={true}
clickEventForward={true}
useMouseEvents={true}
swipeDistance={30}
startZIndex={1}
showPageCorners={true}
disableFlipByClick={false}
className="flipbook-container mx-auto"
style={{
direction: 'rtl',
}}
onFlip={handlePageFlip}
>
{pages.map((page) => (
<BookPage key={page.id} page={page} />
))}
</HTMLFlipBook>
</div>
{/* کنترل‌های ناوبری */}
<div className="flex items-center gap-3 md:gap-6 bg-white rounded-full px-4 md:px-6 py-2 md:py-3 shadow-lg">
{/* دکمه صفحه بعد (در RTL، صفحه سمت چپ) */}
<button
onClick={goToNextPage}
disabled={currentPage <= 0}
className="p-2 md:p-2 rounded-full hover:bg-gray-100 disabled:opacity-30 disabled:cursor-not-allowed transition-all"
aria-label="صفحه بعد"
>
<ArrowLeft2 size={20} className="text-gray-700 md:w-6 md:h-6" />
</button>
{/* شماره صفحه */}
<div className="flex items-center gap-2 min-w-[100px] md:min-w-[120px] justify-center">
<span className="text-xs md:text-sm text-gray-600">
صفحه {currentPage + 1} از {pages.length}
</span>
</div>
{/* دکمه صفحه قبل (در RTL، صفحه سمت راست) */}
<button
onClick={goToPrevPage}
disabled={currentPage >= pages.length - 1}
className="p-2 md:p-2 rounded-full hover:bg-gray-100 disabled:opacity-30 disabled:cursor-not-allowed transition-all"
aria-label="صفحه قبل"
>
<ArrowRight2 size={20} className="text-gray-700 md:w-6 md:h-6" />
</button>
</div>
</div>
);
};
export default BookViewer;