cover
This commit is contained in:
Generated
+50
@@ -14,6 +14,7 @@
|
|||||||
"axios": "^1.13.6",
|
"axios": "^1.13.6",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
"html2canvas": "^1.4.1",
|
||||||
"i18next": "^25.6.2",
|
"i18next": "^25.6.2",
|
||||||
"iconsax-react": "^0.0.8",
|
"iconsax-react": "^0.0.8",
|
||||||
"konva": "^10.0.9",
|
"konva": "^10.0.9",
|
||||||
@@ -2357,6 +2358,15 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/baseline-browser-mapping": {
|
||||||
"version": "2.8.25",
|
"version": "2.8.25",
|
||||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.25.tgz",
|
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.25.tgz",
|
||||||
@@ -2581,6 +2591,15 @@
|
|||||||
"node": ">= 8"
|
"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": {
|
"node_modules/csstype": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||||
@@ -3302,6 +3321,19 @@
|
|||||||
"void-elements": "3.1.0"
|
"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": {
|
"node_modules/i18next": {
|
||||||
"version": "25.6.2",
|
"version": "25.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.6.2.tgz",
|
||||||
@@ -4584,6 +4616,15 @@
|
|||||||
"url": "https://opencollective.com/webpack"
|
"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": {
|
"node_modules/tinyglobby": {
|
||||||
"version": "0.2.15",
|
"version": "0.2.15",
|
||||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
"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"
|
"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": {
|
"node_modules/vite": {
|
||||||
"version": "7.2.2",
|
"version": "7.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz",
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
"axios": "^1.13.6",
|
"axios": "^1.13.6",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
"html2canvas": "^1.4.1",
|
||||||
"i18next": "^25.6.2",
|
"i18next": "^25.6.2",
|
||||||
"iconsax-react": "^0.0.8",
|
"iconsax-react": "^0.0.8",
|
||||||
"konva": "^10.0.9",
|
"konva": "^10.0.9",
|
||||||
|
|||||||
@@ -1,18 +1,12 @@
|
|||||||
import { type FC, useMemo } from 'react'
|
import { type FC, useMemo } from 'react'
|
||||||
import BookPage from '@/pages/viewer/components/BookPage'
|
import BookPage from '@/pages/viewer/components/BookPage'
|
||||||
import { transformViewerDataToPages } from '@/pages/viewer/utils/dataTransformer'
|
import { transformViewerDataToPages } from '@/pages/viewer/utils/dataTransformer'
|
||||||
|
import { getPaperDimensions } from '@/config/paperSizes'
|
||||||
import type { CatalogItemType } from '../types/Types'
|
import type { CatalogItemType } from '../types/Types'
|
||||||
import type { PageData } from '@/pages/viewer/types'
|
import type { PageData } from '@/pages/viewer/types'
|
||||||
|
|
||||||
const PREVIEW_WIDTH = 64
|
const PREVIEW_WIDTH = 64
|
||||||
const PREVIEW_HEIGHT = 89
|
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 {
|
function getFirstPageFromContent(content: string | undefined): PageData | null {
|
||||||
if (!content) return null
|
if (!content) return null
|
||||||
@@ -36,6 +30,16 @@ const CatalogPreview: FC<Props> = ({ item }) => {
|
|||||||
[item.content]
|
[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) {
|
if (!firstPage) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -58,17 +62,17 @@ const CatalogPreview: FC<Props> = ({ item }) => {
|
|||||||
style={{
|
style={{
|
||||||
top: 0,
|
top: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
transform: `scale(${PREVIEW_SCALE})`,
|
transform: `scale(${previewScale})`,
|
||||||
transformOrigin: 'top right',
|
transformOrigin: 'top right',
|
||||||
width: PAGE_BASE_WIDTH,
|
width: pageWidth,
|
||||||
height: PAGE_BASE_HEIGHT,
|
height: pageHeight,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<BookPage
|
<BookPage
|
||||||
page={firstPage}
|
page={firstPage}
|
||||||
scale={1}
|
scale={1}
|
||||||
pageWidth={PAGE_BASE_WIDTH}
|
pageWidth={pageWidth}
|
||||||
pageHeight={PAGE_BASE_HEIGHT}
|
pageHeight={pageHeight}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -31,10 +31,7 @@ export const useUpdateCatalog = () => {
|
|||||||
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||||
|
|
||||||
const scheduleUpdate = useCallback(
|
const scheduleUpdate = useCallback(
|
||||||
(
|
(params: Parameters<typeof api.updateCatalog>[0], delayMs = 3000) => {
|
||||||
params: Parameters<typeof api.updateCatalog>[0],
|
|
||||||
delayMs = 3000
|
|
||||||
) => {
|
|
||||||
if (timeoutRef.current) {
|
if (timeoutRef.current) {
|
||||||
clearTimeout(timeoutRef.current);
|
clearTimeout(timeoutRef.current);
|
||||||
}
|
}
|
||||||
@@ -43,7 +40,7 @@ export const useUpdateCatalog = () => {
|
|||||||
mutation.mutate(params);
|
mutation.mutate(params);
|
||||||
}, delayMs);
|
}, delayMs);
|
||||||
},
|
},
|
||||||
[mutation]
|
[mutation],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
@@ -52,7 +49,7 @@ export const useUpdateCatalog = () => {
|
|||||||
clearTimeout(timeoutRef.current);
|
clearTimeout(timeoutRef.current);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[]
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -7,9 +7,6 @@ import { getPaperDimensions } from '@/config/paperSizes';
|
|||||||
|
|
||||||
const VIEWER_SCALE = 0.5;
|
const VIEWER_SCALE = 0.5;
|
||||||
|
|
||||||
// سایز کتاب همیشه A4 است (ثابت)
|
|
||||||
const BOOK_PAPER_SIZE = 'a4';
|
|
||||||
|
|
||||||
export type BookViewerProps = {
|
export type BookViewerProps = {
|
||||||
pages: PageData[];
|
pages: PageData[];
|
||||||
catalogSize?: string;
|
catalogSize?: string;
|
||||||
@@ -38,19 +35,16 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize }) => {
|
|||||||
const bookRef = useRef<FlipBookInstance | null>(null);
|
const bookRef = useRef<FlipBookInstance | null>(null);
|
||||||
const [currentPage, setCurrentPage] = useState(0);
|
const [currentPage, setCurrentPage] = useState(0);
|
||||||
|
|
||||||
// سایز کتاب ثابت (همیشه A4)
|
// سایز کتاب بر اساس catalogSize (A3/A4/A5)
|
||||||
const { width: bookWidth, height: bookHeight } = useMemo(
|
const { width: bookWidth, height: bookHeight } = useMemo(
|
||||||
() => getPaperDimensions(BOOK_PAPER_SIZE),
|
() => getPaperDimensions(catalogSize ?? 'a4'),
|
||||||
[]
|
[catalogSize]
|
||||||
);
|
);
|
||||||
const displayWidth = Math.round(bookWidth * VIEWER_SCALE);
|
const displayWidth = Math.round(bookWidth * VIEWER_SCALE);
|
||||||
const displayHeight = Math.round(bookHeight * VIEWER_SCALE);
|
const displayHeight = Math.round(bookHeight * VIEWER_SCALE);
|
||||||
|
|
||||||
// نسبت محتوا بر اساس catalogSize (A3/A4/A5)
|
// scale محتوا همان VIEWER_SCALE است چون سایز کتاب با سایز محتوا یکی است
|
||||||
const contentScale = useMemo(() => {
|
const contentScale = VIEWER_SCALE;
|
||||||
const { width: contentWidth } = getPaperDimensions(catalogSize ?? 'a4');
|
|
||||||
return VIEWER_SCALE * (bookWidth / contentWidth);
|
|
||||||
}, [catalogSize]);
|
|
||||||
|
|
||||||
// اطمینان از اینکه currentPage همیشه در محدوده معتبر است
|
// اطمینان از اینکه currentPage همیشه در محدوده معتبر است
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { EditorObject } from '@/pages/editor/store/editorStore';
|
import type { EditorObject, TableData } from "@/pages/editor/store/editorStore";
|
||||||
import type { PageData } from '../types';
|
import type { PageData } from "../types";
|
||||||
|
|
||||||
type ViewerDataPage = {
|
type ViewerDataPage = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -25,7 +25,7 @@ type ViewerDataPage = {
|
|||||||
rotation?: number;
|
rotation?: number;
|
||||||
opacity?: number;
|
opacity?: number;
|
||||||
letterSpacing?: 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 objects: EditorObject[] = page.objects.map((obj) => {
|
||||||
const baseObject: EditorObject = {
|
const baseObject: EditorObject = {
|
||||||
id: obj.id,
|
id: obj.id,
|
||||||
type: obj.type as EditorObject['type'],
|
type: obj.type as EditorObject["type"],
|
||||||
x: obj.x,
|
x: obj.x,
|
||||||
y: obj.y,
|
y: obj.y,
|
||||||
// در JSON، opacity به صورت 0-100 است (درصد)
|
// در JSON، opacity به صورت 0-100 است (درصد)
|
||||||
@@ -53,7 +53,7 @@ export function transformViewerDataToPages(data: ViewerData): PageData[] {
|
|||||||
|
|
||||||
// برای line و arrow، width و height در viewer-data.json به عنوان مختصات انتهایی هستند
|
// برای line و arrow، width و height در viewer-data.json به عنوان مختصات انتهایی هستند
|
||||||
// در EditorObject هم به عنوان مختصات انتهایی ذخیره میشوند
|
// در EditorObject هم به عنوان مختصات انتهایی ذخیره میشوند
|
||||||
if (obj.type === 'line' || obj.type === 'arrow') {
|
if (obj.type === "line" || obj.type === "arrow") {
|
||||||
// width و height مستقیماً مختصات نقطه پایان هستند
|
// width و height مستقیماً مختصات نقطه پایان هستند
|
||||||
if (obj.width !== undefined) {
|
if (obj.width !== undefined) {
|
||||||
baseObject.width = obj.width;
|
baseObject.width = obj.width;
|
||||||
@@ -77,7 +77,8 @@ export function transformViewerDataToPages(data: ViewerData): PageData[] {
|
|||||||
// اما فعلاً JSON به صورت مرکز است، پس تبدیل نمیکنیم
|
// اما فعلاً JSON به صورت مرکز است، پس تبدیل نمیکنیم
|
||||||
if (obj.fill !== undefined) baseObject.fill = obj.fill;
|
if (obj.fill !== undefined) baseObject.fill = obj.fill;
|
||||||
if (obj.stroke !== undefined) baseObject.stroke = obj.stroke;
|
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;
|
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.fontSize !== undefined) baseObject.fontSize = obj.fontSize;
|
||||||
if (obj.fontFamily !== undefined) baseObject.fontFamily = obj.fontFamily;
|
if (obj.fontFamily !== undefined) baseObject.fontFamily = obj.fontFamily;
|
||||||
if (obj.fontWeight !== undefined) baseObject.fontWeight = obj.fontWeight;
|
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ها
|
// انواع خاص objectها
|
||||||
if (obj.type === 'rectangle' && obj.shapeType) {
|
if (obj.type === "rectangle" && obj.shapeType) {
|
||||||
baseObject.shapeType = obj.shapeType as EditorObject['shapeType'];
|
baseObject.shapeType = obj.shapeType as EditorObject["shapeType"];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj.type === 'image' && obj.imageUrl) {
|
if (obj.type === "image" && obj.imageUrl) {
|
||||||
baseObject.imageUrl = obj.imageUrl;
|
baseObject.imageUrl = obj.imageUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj.type === 'video' && obj.videoUrl) {
|
if (obj.type === "video" && obj.videoUrl) {
|
||||||
baseObject.videoUrl = obj.videoUrl;
|
baseObject.videoUrl = obj.videoUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj.type === 'link' && obj.linkUrl) {
|
if (obj.type === "link" && obj.linkUrl) {
|
||||||
baseObject.linkUrl = obj.linkUrl;
|
baseObject.linkUrl = obj.linkUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj.type === 'grid' && obj.tableData) {
|
if (obj.type === "grid" && obj.tableData) {
|
||||||
baseObject.tableData = obj.tableData;
|
baseObject.tableData = obj.tableData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,4 +121,3 @@ export function transformViewerDataToPages(data: ViewerData): PageData[] {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user