From 703ed374f41d2c167b4a3db254b89e7d09ba4e37 Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Sat, 14 Mar 2026 12:00:25 +0330 Subject: [PATCH] previewer --- .../catalogue/components/CatalogPreview.tsx | 78 ++ .../catalogue/components/CatalogueItem.tsx | 5 +- src/pages/viewer/components/BookPage.tsx | 834 +++++++++--------- 3 files changed, 498 insertions(+), 419 deletions(-) create mode 100644 src/pages/catalogue/components/CatalogPreview.tsx diff --git a/src/pages/catalogue/components/CatalogPreview.tsx b/src/pages/catalogue/components/CatalogPreview.tsx new file mode 100644 index 0000000..f6991d2 --- /dev/null +++ b/src/pages/catalogue/components/CatalogPreview.tsx @@ -0,0 +1,78 @@ +import { type FC, useMemo } from 'react' +import BookPage from '@/pages/viewer/components/BookPage' +import { transformViewerDataToPages } from '@/pages/viewer/utils/dataTransformer' +import type { CatalogItemType } from '../types/Types' +import type { PageData } from '@/pages/viewer/types' + +const PREVIEW_WIDTH = 64 +const PREVIEW_HEIGHT = 89 +const PAGE_BASE_WIDTH = 794 +const PAGE_BASE_HEIGHT = 1123 + +const PREVIEW_SCALE = Math.min( + PREVIEW_WIDTH / PAGE_BASE_WIDTH, + PREVIEW_HEIGHT / PAGE_BASE_HEIGHT +) + +function getFirstPageFromContent(content: string | undefined): PageData | null { + if (!content) return null + try { + const parsed = JSON.parse(content) + const pagesArray = Array.isArray(parsed) ? parsed : (parsed?.pages ?? []) + const transformed = transformViewerDataToPages({ pages: pagesArray }) + return transformed[0] ?? null + } catch { + return null + } +} + +type Props = { + item: CatalogItemType +} + +const CatalogPreview: FC = ({ item }) => { + const firstPage = useMemo( + () => getFirstPageFromContent(item.content), + [item.content] + ) + + if (!firstPage) { + return ( +
+ بدون پیش‌نمایش +
+ ) + } + + return ( +
+
+ +
+
+ ) +} + +export default CatalogPreview diff --git a/src/pages/catalogue/components/CatalogueItem.tsx b/src/pages/catalogue/components/CatalogueItem.tsx index db3cddf..45cd4ba 100644 --- a/src/pages/catalogue/components/CatalogueItem.tsx +++ b/src/pages/catalogue/components/CatalogueItem.tsx @@ -4,6 +4,7 @@ import type { CatalogItemType } from '../types/Types' import moment from 'moment-jalaali' import { Link } from 'react-router-dom' import { Paths } from '@/config/Paths' +import CatalogPreview from './CatalogPreview' type Props = { item: CatalogItemType @@ -13,7 +14,7 @@ const CatalogueItem: FC = ({ item }) => { return (
-
+
{item.name} @@ -23,7 +24,7 @@ const CatalogueItem: FC = ({ item }) => {
-
+
diff --git a/src/pages/viewer/components/BookPage.tsx b/src/pages/viewer/components/BookPage.tsx index e7d55d3..5646731 100644 --- a/src/pages/viewer/components/BookPage.tsx +++ b/src/pages/viewer/components/BookPage.tsx @@ -19,151 +19,104 @@ type BookPageProps = { */ const BookPage = forwardRef( ({ page, scale = 1, pageWidth, pageHeight, onLinkClick }, ref) => { - // تابع برای تبدیل opacity به رنگ (مثل editor) - // در editor، getColorWithOpacity انتظار opacity 0-100 دارد - // در dataTransformer، opacity از 0-1 به 0-100 تبدیل شده است - // پس opacity را مستقیماً استفاده می‌کنیم - const getColorWithOpacity = (fill?: string, opacity?: number): string => { - if (!fill) return '#000000'; + // تابع برای تبدیل opacity به رنگ (مثل editor) + // در editor، getColorWithOpacity انتظار opacity 0-100 دارد + // در dataTransformer، opacity از 0-1 به 0-100 تبدیل شده است + // پس opacity را مستقیماً استفاده می‌کنیم + const getColorWithOpacity = (fill?: string, opacity?: number): string => { + if (!fill) return '#000000'; - // opacity در EditorObject به صورت 0-100 است (بعد از تبدیل در dataTransformer) - // در editor getColorWithOpacity انتظار 0-100 دارد - const opacityValue = opacity !== undefined ? opacity : 100; + // opacity در EditorObject به صورت 0-100 است (بعد از تبدیل در dataTransformer) + // در editor getColorWithOpacity انتظار 0-100 دارد + const opacityValue = opacity !== undefined ? opacity : 100; - // اگر opacity 100% باشد، رنگ را بدون تغییر برگردان (مثل editor) - if (opacityValue >= 100) return fill; + // اگر opacity 100% باشد، رنگ را بدون تغییر برگردان (مثل editor) + if (opacityValue >= 100) return fill; - const hex = fill.replace('#', ''); - // اگر hex کوتاه‌تر از 6 کاراکتر باشد، padding اضافه کن - const paddedHex = hex.length === 3 - ? hex.split('').map(char => char + char).join('') - : hex; + const hex = fill.replace('#', ''); + // اگر hex کوتاه‌تر از 6 کاراکتر باشد، padding اضافه کن + const paddedHex = hex.length === 3 + ? hex.split('').map(char => char + char).join('') + : hex; - const r = parseInt(paddedHex.substring(0, 2), 16); - const g = parseInt(paddedHex.substring(2, 4), 16); - const b = parseInt(paddedHex.substring(4, 6), 16); - // opacityValue را به alpha تبدیل کن (0-100 -> 0-1) مثل editor - const alpha = opacityValue / 100; + const r = parseInt(paddedHex.substring(0, 2), 16); + const g = parseInt(paddedHex.substring(2, 4), 16); + const b = parseInt(paddedHex.substring(4, 6), 16); + // opacityValue را به alpha تبدیل کن (0-100 -> 0-1) مثل editor + const alpha = opacityValue / 100; - return `rgba(${r}, ${g}, ${b}, ${alpha})`; - }; - - const renderObject = (obj: EditorObject, index: number) => { - const baseStyle: React.CSSProperties = { - position: 'absolute', - left: `${(obj.x || 0) * scale}px`, - top: `${(obj.y || 0) * scale}px`, - opacity: obj.opacity ?? 1, - transform: obj.rotation ? `rotate(${obj.rotation}deg)` : undefined, - transformOrigin: 'center center', + return `rgba(${r}, ${g}, ${b}, ${alpha})`; }; - if (obj.visible === false) { - return null; - } + const renderObject = (obj: EditorObject, index: number) => { + const baseStyle: React.CSSProperties = { + position: 'absolute', + left: `${(obj.x || 0) * scale}px`, + top: `${(obj.y || 0) * scale}px`, + opacity: obj.opacity ?? 1, + transform: obj.rotation ? `rotate(${obj.rotation}deg)` : undefined, + transformOrigin: 'center center', + }; - switch (obj.type) { - case 'text': { - // برای text، opacity در رنگ اعمال می‌شود، پس باید opacity را از baseStyle حذف کنیم - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { opacity, ...textBaseStyle } = baseStyle; - return ( -
- {obj.text || ''} -
- ); + if (obj.visible === false) { + return null; } - case 'image': - return ( - - ); - - case 'video': - return ( -
+ ); + } + + case 'image': + return ( + + ); + + case 'video': + return ( +