base flip
This commit is contained in:
Generated
+18
-17
@@ -15,11 +15,13 @@
|
||||
"i18next": "^25.6.2",
|
||||
"iconsax-react": "^0.0.8",
|
||||
"konva": "^10.0.9",
|
||||
"page-flip": "^2.0.7",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"react-dropzone": "^14.3.8",
|
||||
"react-i18next": "^16.3.0",
|
||||
"react-konva": "^19.2.0",
|
||||
"react-pageflip": "^2.0.3",
|
||||
"react-router-dom": "^7.9.5",
|
||||
"tailwind-merge": "^3.4.0",
|
||||
"tailwindcss": "^4.1.17",
|
||||
@@ -73,7 +75,6 @@
|
||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.5",
|
||||
@@ -1847,7 +1848,6 @@
|
||||
"integrity": "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~7.16.0"
|
||||
}
|
||||
@@ -1857,7 +1857,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz",
|
||||
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
@@ -1868,7 +1867,6 @@
|
||||
"integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.2.0"
|
||||
}
|
||||
@@ -1928,7 +1926,6 @@
|
||||
"integrity": "sha512-tK3GPFWbirvNgsNKto+UmB/cRtn6TZfyw0D6IKrW55n6Vbs7KJoZtI//kpTKzE/DUmmnAFD8/Ca46s7Obs92/w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.46.4",
|
||||
"@typescript-eslint/types": "8.46.4",
|
||||
@@ -2181,7 +2178,6 @@
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -2319,7 +2315,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.8.25",
|
||||
"caniuse-lite": "^1.0.30001754",
|
||||
@@ -2591,7 +2586,6 @@
|
||||
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
@@ -3001,7 +2995,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.6"
|
||||
},
|
||||
@@ -3227,8 +3220,7 @@
|
||||
"url": "https://github.com/sponsors/lavrton"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/levn": {
|
||||
"version": "0.4.1",
|
||||
@@ -3682,6 +3674,12 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/page-flip": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/page-flip/-/page-flip-2.0.7.tgz",
|
||||
"integrity": "sha512-96lQFUUz7r/LZzEUZJ3yBIMEKU9+m8HMFDzTvTdD6P7Ag/wXINjp9n0W7b4wanwnDbQETo4uNUoL3zMqpFxwGA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/parent-module": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
@@ -3819,7 +3817,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
|
||||
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -3829,7 +3826,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
|
||||
"integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.27.0"
|
||||
},
|
||||
@@ -3918,6 +3914,15 @@
|
||||
"react-dom": "^19.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-pageflip": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/react-pageflip/-/react-pageflip-2.0.3.tgz",
|
||||
"integrity": "sha512-k81mHhRvUM52y8jyzTCh5t4O0lepkLhp+XGSUzq2C3uD+iW99Cv0jfRlqFCjZbD5N3jKkIFr7/3giucoXKDP3Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"page-flip": "latest"
|
||||
}
|
||||
},
|
||||
"node_modules/react-reconciler": {
|
||||
"version": "0.33.0",
|
||||
"resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.33.0.tgz",
|
||||
@@ -4214,7 +4219,6 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -4273,7 +4277,6 @@
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -4378,7 +4381,6 @@
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz",
|
||||
"integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.5.0",
|
||||
@@ -4470,7 +4472,6 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
|
||||
@@ -17,11 +17,13 @@
|
||||
"i18next": "^25.6.2",
|
||||
"iconsax-react": "^0.0.8",
|
||||
"konva": "^10.0.9",
|
||||
"page-flip": "^2.0.7",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"react-dropzone": "^14.3.8",
|
||||
"react-i18next": "^16.3.0",
|
||||
"react-konva": "^19.2.0",
|
||||
"react-pageflip": "^2.0.3",
|
||||
"react-router-dom": "^7.9.5",
|
||||
"tailwind-merge": "^3.4.0",
|
||||
"tailwindcss": "^4.1.17",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export const Paths = {
|
||||
home: "/",
|
||||
editor: "/editor",
|
||||
viewer: "/viewer",
|
||||
};
|
||||
|
||||
@@ -89,3 +89,34 @@ html {
|
||||
.filterBlack {
|
||||
filter: brightness(0) invert(0);
|
||||
}
|
||||
|
||||
/* استایل برای صفحات کتاب - بکگراند سفید */
|
||||
.stf__item.page,
|
||||
.stf__item[class*="page"],
|
||||
.page.stf__item,
|
||||
div.stf__item.page,
|
||||
.stf__block .stf__item,
|
||||
.stf__parent .stf__item,
|
||||
.stf__wrapper .stf__item {
|
||||
background-color: #ffffff !important;
|
||||
background: #ffffff !important;
|
||||
}
|
||||
|
||||
/* برای اطمینان بیشتر - تمام صفحات با کلاس page */
|
||||
.page {
|
||||
background-color: #ffffff !important;
|
||||
background: #ffffff !important;
|
||||
}
|
||||
|
||||
/* استایل برای ظاهر کتاب */
|
||||
.flipbook-container {
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* استایل برای صفحات داخل flipbook */
|
||||
.flipbook-container .stf__item,
|
||||
.flipbook-container .page {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { type FC } from 'react';
|
||||
import BookViewer from './components/BookViewer';
|
||||
import { mockPages } from './components/mockData';
|
||||
|
||||
const Viewer: FC = () => {
|
||||
return (
|
||||
<div className="w-full h-full">
|
||||
<BookViewer pages={mockPages} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Viewer;
|
||||
@@ -0,0 +1,78 @@
|
||||
import { forwardRef } from 'react';
|
||||
import { type PageData } from '../types';
|
||||
|
||||
type BookPageProps = {
|
||||
page: PageData;
|
||||
};
|
||||
|
||||
/**
|
||||
* کامپوننت خالص برای نمایش محتوای یک صفحه کتاب
|
||||
* تمام المنتها با absolute positioning رندر میشوند
|
||||
* از HTML استفاده میشود (نه Canvas) برای سازگاری با Konva در آینده
|
||||
* این کامپوننت به عنوان child برای HTMLFlipBook استفاده میشود
|
||||
* نیاز به forwardRef دارد تا react-pageflip بتواند ref را مدیریت کند
|
||||
*/
|
||||
const BookPage = forwardRef<HTMLDivElement, BookPageProps>(({ page }, ref) => {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className="page"
|
||||
data-density="soft"
|
||||
style={{
|
||||
position: 'relative',
|
||||
backgroundColor: '#ffffff',
|
||||
overflow: 'hidden',
|
||||
padding: '40px',
|
||||
boxSizing: 'border-box',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
}}
|
||||
>
|
||||
{page.elements.map((element, index) => {
|
||||
if (element.type === 'text') {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: `${element.x}px`,
|
||||
top: `${element.y}px`,
|
||||
fontSize: `${element.fontSize}px`,
|
||||
color: element.color,
|
||||
fontFamily: 'inherit',
|
||||
whiteSpace: 'pre-wrap',
|
||||
}}
|
||||
>
|
||||
{element.text}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (element.type === 'image') {
|
||||
return (
|
||||
<img
|
||||
key={index}
|
||||
src={element.src}
|
||||
alt=""
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: `${element.x}px`,
|
||||
top: `${element.y}px`,
|
||||
width: `${element.width}px`,
|
||||
height: `${element.height}px`,
|
||||
objectFit: 'contain',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
BookPage.displayName = 'BookPage';
|
||||
|
||||
export default BookPage;
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
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;
|
||||
@@ -0,0 +1,179 @@
|
||||
import { type PageData } from '../types';
|
||||
|
||||
/**
|
||||
* دادههای mock برای 6 صفحه کتاب
|
||||
* این ساختار داده برای سازگاری با Konva در آینده طراحی شده است
|
||||
*/
|
||||
export const mockPages: PageData[] = [
|
||||
{
|
||||
id: 1,
|
||||
width: 794,
|
||||
height: 1123,
|
||||
elements: [
|
||||
{
|
||||
type: 'text',
|
||||
x: 100,
|
||||
y: 150,
|
||||
text: 'صفحه اول\nخوش آمدید',
|
||||
fontSize: 48,
|
||||
color: '#1a1a1a',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
x: 100,
|
||||
y: 250,
|
||||
text: 'این یک کتاب نمایشی است\nبرای تست کتابخوان',
|
||||
fontSize: 24,
|
||||
color: '#333333',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
width: 794,
|
||||
height: 1123,
|
||||
elements: [
|
||||
{
|
||||
type: 'text',
|
||||
x: 100,
|
||||
y: 100,
|
||||
text: 'فصل اول: مقدمه',
|
||||
fontSize: 36,
|
||||
color: '#000000',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
x: 100,
|
||||
y: 180,
|
||||
text: 'این یک متن نمونه است که در صفحه دوم نمایش داده میشود.\nمتنهای بیشتر برای نمایش بهتر صفحه.',
|
||||
fontSize: 20,
|
||||
color: '#2c2c2c',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
x: 100,
|
||||
y: 320,
|
||||
text: 'محتوا در اینجا ادامه دارد...',
|
||||
fontSize: 18,
|
||||
color: '#444444',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
width: 794,
|
||||
height: 1123,
|
||||
elements: [
|
||||
{
|
||||
type: 'text',
|
||||
x: 100,
|
||||
y: 100,
|
||||
text: 'صفحه سوم',
|
||||
fontSize: 32,
|
||||
color: '#1a1a1a',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
x: 100,
|
||||
y: 200,
|
||||
text: 'این صفحه شامل محتوای بیشتری است.\n\nمیتوانید متنهای مختلفی در اینجا اضافه کنید.',
|
||||
fontSize: 22,
|
||||
color: '#333333',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
x: 100,
|
||||
y: 400,
|
||||
text: 'نکته: این یک نمایش نمونه است',
|
||||
fontSize: 18,
|
||||
color: '#666666',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
width: 794,
|
||||
height: 1123,
|
||||
elements: [
|
||||
{
|
||||
type: 'text',
|
||||
x: 100,
|
||||
y: 150,
|
||||
text: 'فصل دوم: ادامه',
|
||||
fontSize: 36,
|
||||
color: '#000000',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
x: 100,
|
||||
y: 250,
|
||||
text: 'در این بخش محتوای بیشتری اضافه میشود.\n\nاین کتاب نمونه برای نمایش قابلیتهای کتابخوان طراحی شده است.',
|
||||
fontSize: 20,
|
||||
color: '#2c2c2c',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
width: 794,
|
||||
height: 1123,
|
||||
elements: [
|
||||
{
|
||||
type: 'text',
|
||||
x: 100,
|
||||
y: 120,
|
||||
text: 'صفحه پنجم',
|
||||
fontSize: 40,
|
||||
color: '#1a1a1a',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
x: 100,
|
||||
y: 220,
|
||||
text: 'محتوا در صفحات مختلف توزیع شده است.\n\nهر صفحه میتواند شامل متن و تصویر باشد.',
|
||||
fontSize: 22,
|
||||
color: '#333333',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
x: 100,
|
||||
y: 400,
|
||||
text: 'ادامه در صفحه بعد...',
|
||||
fontSize: 18,
|
||||
color: '#666666',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
width: 794,
|
||||
height: 1123,
|
||||
elements: [
|
||||
{
|
||||
type: 'text',
|
||||
x: 100,
|
||||
y: 200,
|
||||
text: 'صفحه آخر',
|
||||
fontSize: 36,
|
||||
color: '#1a1a1a',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
x: 100,
|
||||
y: 300,
|
||||
text: 'این آخرین صفحه کتاب است.\n\nاز کتابخوان استفاده کرده و صفحات را ورق بزنید!',
|
||||
fontSize: 24,
|
||||
color: '#333333',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
x: 100,
|
||||
y: 500,
|
||||
text: 'پایان',
|
||||
fontSize: 32,
|
||||
color: '#000000',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
export type PageElement =
|
||||
| { type: "text"; x: number; y: number; text: string; fontSize: number; color: string }
|
||||
| { type: "image"; x: number; y: number; src: string; width: number; height: number };
|
||||
|
||||
export type PageData = {
|
||||
id: number;
|
||||
width: number;
|
||||
height: number;
|
||||
elements: PageElement[];
|
||||
};
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
declare module 'page-flip' {
|
||||
export interface PageFlipSettings {
|
||||
width: number;
|
||||
height: number;
|
||||
size?: 'fixed' | 'stretch';
|
||||
showCover?: boolean;
|
||||
maxShadowOpacity?: number;
|
||||
mobileScrollSupport?: boolean;
|
||||
}
|
||||
|
||||
export class PageFlip {
|
||||
constructor(block: HTMLElement, setting: PageFlipSettings);
|
||||
loadFromHTML(pages: NodeListOf<Element> | Element[]): void;
|
||||
flipNext(corner?: 'top' | 'bottom'): void;
|
||||
flipPrev(corner?: 'top' | 'bottom'): void;
|
||||
destroy(): void;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { clx } from '@/helpers/utils'
|
||||
import { useSharedStore } from '@/shared/store/sharedStore'
|
||||
import Home from '@/pages/home/Home'
|
||||
import Editor from '@/pages/editor/Editor'
|
||||
import Viewer from '@/pages/viewer/Viewer'
|
||||
|
||||
const MainRouter = () => {
|
||||
const { hasSubMenu } = useSharedStore()
|
||||
@@ -31,6 +32,7 @@ const MainRouter = () => {
|
||||
<Routes>
|
||||
<Route path={Paths.home} element={<Home />} />
|
||||
<Route path={Paths.editor} element={<Editor />} />
|
||||
<Route path={Paths.viewer} element={<Viewer />} />
|
||||
</Routes>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user