From 67b5944ddbc04a33fe02ddf68fa4300b7c104033 Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Sat, 14 Mar 2026 15:25:25 +0330 Subject: [PATCH] cover --- package-lock.json | 50 +++++++++++++++++++ package.json | 1 + .../catalogue/components/CatalogPreview.tsx | 28 ++++++----- src/pages/home/hooks/useHomeData.ts | 9 ++-- src/pages/viewer/components/BookViewer.tsx | 16 ++---- src/pages/viewer/utils/dataTransformer.ts | 29 +++++------ 6 files changed, 90 insertions(+), 43 deletions(-) diff --git a/package-lock.json b/package-lock.json index d9bbe1e..2265733 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "axios": "^1.13.6", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "html2canvas": "^1.4.1", "i18next": "^25.6.2", "iconsax-react": "^0.0.8", "konva": "^10.0.9", @@ -2357,6 +2358,15 @@ "dev": true, "license": "MIT" }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/baseline-browser-mapping": { "version": "2.8.25", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.25.tgz", @@ -2581,6 +2591,15 @@ "node": ">= 8" } }, + "node_modules/css-line-break": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "license": "MIT", + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -3302,6 +3321,19 @@ "void-elements": "3.1.0" } }, + "node_modules/html2canvas": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "license": "MIT", + "dependencies": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/i18next": { "version": "25.6.2", "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.6.2.tgz", @@ -4584,6 +4616,15 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/text-segmentation": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", + "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "license": "MIT", + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -4779,6 +4820,15 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/utrie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "license": "MIT", + "dependencies": { + "base64-arraybuffer": "^1.0.2" + } + }, "node_modules/vite": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz", diff --git a/package.json b/package.json index b472570..7bf8313 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "axios": "^1.13.6", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "html2canvas": "^1.4.1", "i18next": "^25.6.2", "iconsax-react": "^0.0.8", "konva": "^10.0.9", diff --git a/src/pages/catalogue/components/CatalogPreview.tsx b/src/pages/catalogue/components/CatalogPreview.tsx index f6991d2..6574323 100644 --- a/src/pages/catalogue/components/CatalogPreview.tsx +++ b/src/pages/catalogue/components/CatalogPreview.tsx @@ -1,18 +1,12 @@ import { type FC, useMemo } from 'react' import BookPage from '@/pages/viewer/components/BookPage' import { transformViewerDataToPages } from '@/pages/viewer/utils/dataTransformer' +import { getPaperDimensions } from '@/config/paperSizes' 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 @@ -36,6 +30,16 @@ const CatalogPreview: FC = ({ item }) => { [item.content] ) + const { pageWidth, pageHeight, previewScale } = useMemo(() => { + const { width, height } = getPaperDimensions(item.size ?? 'a4') + const scale = Math.min(PREVIEW_WIDTH / width, PREVIEW_HEIGHT / height) + return { + pageWidth: width, + pageHeight: height, + previewScale: scale, + } + }, [item.size]) + if (!firstPage) { return (
= ({ item }) => { style={{ top: 0, right: 0, - transform: `scale(${PREVIEW_SCALE})`, + transform: `scale(${previewScale})`, transformOrigin: 'top right', - width: PAGE_BASE_WIDTH, - height: PAGE_BASE_HEIGHT, + width: pageWidth, + height: pageHeight, }} >
diff --git a/src/pages/home/hooks/useHomeData.ts b/src/pages/home/hooks/useHomeData.ts index 3489b79..752b20c 100644 --- a/src/pages/home/hooks/useHomeData.ts +++ b/src/pages/home/hooks/useHomeData.ts @@ -31,10 +31,7 @@ export const useUpdateCatalog = () => { const timeoutRef = useRef | null>(null); const scheduleUpdate = useCallback( - ( - params: Parameters[0], - delayMs = 3000 - ) => { + (params: Parameters[0], delayMs = 3000) => { if (timeoutRef.current) { clearTimeout(timeoutRef.current); } @@ -43,7 +40,7 @@ export const useUpdateCatalog = () => { mutation.mutate(params); }, delayMs); }, - [mutation] + [mutation], ); useEffect( @@ -52,7 +49,7 @@ export const useUpdateCatalog = () => { clearTimeout(timeoutRef.current); } }, - [] + [], ); return { diff --git a/src/pages/viewer/components/BookViewer.tsx b/src/pages/viewer/components/BookViewer.tsx index d7d8f3b..723a562 100644 --- a/src/pages/viewer/components/BookViewer.tsx +++ b/src/pages/viewer/components/BookViewer.tsx @@ -7,9 +7,6 @@ import { getPaperDimensions } from '@/config/paperSizes'; const VIEWER_SCALE = 0.5; -// سایز کتاب همیشه A4 است (ثابت) -const BOOK_PAPER_SIZE = 'a4'; - export type BookViewerProps = { pages: PageData[]; catalogSize?: string; @@ -38,19 +35,16 @@ const BookViewer: FC = ({ pages, catalogSize }) => { const bookRef = useRef(null); const [currentPage, setCurrentPage] = useState(0); - // سایز کتاب ثابت (همیشه A4) + // سایز کتاب بر اساس catalogSize (A3/A4/A5) const { width: bookWidth, height: bookHeight } = useMemo( - () => getPaperDimensions(BOOK_PAPER_SIZE), - [] + () => getPaperDimensions(catalogSize ?? 'a4'), + [catalogSize] ); const displayWidth = Math.round(bookWidth * VIEWER_SCALE); const displayHeight = Math.round(bookHeight * VIEWER_SCALE); - // نسبت محتوا بر اساس catalogSize (A3/A4/A5) - const contentScale = useMemo(() => { - const { width: contentWidth } = getPaperDimensions(catalogSize ?? 'a4'); - return VIEWER_SCALE * (bookWidth / contentWidth); - }, [catalogSize]); + // scale محتوا همان VIEWER_SCALE است چون سایز کتاب با سایز محتوا یکی است + const contentScale = VIEWER_SCALE; // اطمینان از اینکه currentPage همیشه در محدوده معتبر است useEffect(() => { diff --git a/src/pages/viewer/utils/dataTransformer.ts b/src/pages/viewer/utils/dataTransformer.ts index 38580f4..fd8c0ca 100644 --- a/src/pages/viewer/utils/dataTransformer.ts +++ b/src/pages/viewer/utils/dataTransformer.ts @@ -1,5 +1,5 @@ -import type { EditorObject } from '@/pages/editor/store/editorStore'; -import type { PageData } from '../types'; +import type { EditorObject, TableData } from "@/pages/editor/store/editorStore"; +import type { PageData } from "../types"; type ViewerDataPage = { id: string; @@ -25,7 +25,7 @@ type ViewerDataPage = { rotation?: number; opacity?: number; letterSpacing?: number; - tableData?: any; + tableData?: TableData; }>; }; @@ -41,7 +41,7 @@ export function transformViewerDataToPages(data: ViewerData): PageData[] { const objects: EditorObject[] = page.objects.map((obj) => { const baseObject: EditorObject = { id: obj.id, - type: obj.type as EditorObject['type'], + type: obj.type as EditorObject["type"], x: obj.x, y: obj.y, // در JSON، opacity به صورت 0-100 است (درصد) @@ -53,7 +53,7 @@ export function transformViewerDataToPages(data: ViewerData): PageData[] { // برای line و arrow، width و height در viewer-data.json به عنوان مختصات انتهایی هستند // در EditorObject هم به عنوان مختصات انتهایی ذخیره می‌شوند - if (obj.type === 'line' || obj.type === 'arrow') { + if (obj.type === "line" || obj.type === "arrow") { // width و height مستقیماً مختصات نقطه پایان هستند if (obj.width !== undefined) { baseObject.width = obj.width; @@ -77,7 +77,8 @@ export function transformViewerDataToPages(data: ViewerData): PageData[] { // اما فعلاً JSON به صورت مرکز است، پس تبدیل نمی‌کنیم if (obj.fill !== undefined) baseObject.fill = obj.fill; if (obj.stroke !== undefined) baseObject.stroke = obj.stroke; - if (obj.strokeWidth !== undefined) baseObject.strokeWidth = obj.strokeWidth; + if (obj.strokeWidth !== undefined) + baseObject.strokeWidth = obj.strokeWidth; if (obj.rotation !== undefined) baseObject.rotation = obj.rotation; // ویژگی‌های متنی @@ -85,26 +86,27 @@ export function transformViewerDataToPages(data: ViewerData): PageData[] { if (obj.fontSize !== undefined) baseObject.fontSize = obj.fontSize; if (obj.fontFamily !== undefined) baseObject.fontFamily = obj.fontFamily; if (obj.fontWeight !== undefined) baseObject.fontWeight = obj.fontWeight; - if (obj.letterSpacing !== undefined) baseObject.letterSpacing = obj.letterSpacing; + if (obj.letterSpacing !== undefined) + baseObject.letterSpacing = obj.letterSpacing; // انواع خاص objectها - if (obj.type === 'rectangle' && obj.shapeType) { - baseObject.shapeType = obj.shapeType as EditorObject['shapeType']; + if (obj.type === "rectangle" && obj.shapeType) { + baseObject.shapeType = obj.shapeType as EditorObject["shapeType"]; } - if (obj.type === 'image' && obj.imageUrl) { + if (obj.type === "image" && obj.imageUrl) { baseObject.imageUrl = obj.imageUrl; } - if (obj.type === 'video' && obj.videoUrl) { + if (obj.type === "video" && obj.videoUrl) { baseObject.videoUrl = obj.videoUrl; } - if (obj.type === 'link' && obj.linkUrl) { + if (obj.type === "link" && obj.linkUrl) { baseObject.linkUrl = obj.linkUrl; } - if (obj.type === 'grid' && obj.tableData) { + if (obj.type === "grid" && obj.tableData) { baseObject.tableData = obj.tableData; } @@ -119,4 +121,3 @@ export function transformViewerDataToPages(data: ViewerData): PageData[] { }; }); } -