RTL Trick
deploy to danak / build_and_deploy (push) Has been cancelled

This commit is contained in:
hamid zarghami
2026-06-22 16:44:00 +03:30
parent ca02a621da
commit 39e18c3331
2 changed files with 637 additions and 609 deletions
+136 -120
View File
@@ -54,13 +54,13 @@
}
},
"node_modules/@babel/code-frame": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
"integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
"integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-validator-identifier": "^7.27.1",
"@babel/helper-validator-identifier": "^7.29.7",
"js-tokens": "^4.0.0",
"picocolors": "^1.1.1"
},
@@ -69,9 +69,9 @@
}
},
"node_modules/@babel/compat-data": {
"version": "7.28.5",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz",
"integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
"integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -79,21 +79,21 @@
}
},
"node_modules/@babel/core": {
"version": "7.28.5",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz",
"integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5",
"@babel/helper-compilation-targets": "^7.27.2",
"@babel/helper-module-transforms": "^7.28.3",
"@babel/helpers": "^7.28.4",
"@babel/parser": "^7.28.5",
"@babel/template": "^7.27.2",
"@babel/traverse": "^7.28.5",
"@babel/types": "^7.28.5",
"@babel/code-frame": "^7.29.7",
"@babel/generator": "^7.29.7",
"@babel/helper-compilation-targets": "^7.29.7",
"@babel/helper-module-transforms": "^7.29.7",
"@babel/helpers": "^7.29.7",
"@babel/parser": "^7.29.7",
"@babel/template": "^7.29.7",
"@babel/traverse": "^7.29.7",
"@babel/types": "^7.29.7",
"@jridgewell/remapping": "^2.3.5",
"convert-source-map": "^2.0.0",
"debug": "^4.1.0",
@@ -110,14 +110,14 @@
}
},
"node_modules/@babel/generator": {
"version": "7.28.5",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz",
"integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
"integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.28.5",
"@babel/types": "^7.28.5",
"@babel/parser": "^7.29.7",
"@babel/types": "^7.29.7",
"@jridgewell/gen-mapping": "^0.3.12",
"@jridgewell/trace-mapping": "^0.3.28",
"jsesc": "^3.0.2"
@@ -127,14 +127,14 @@
}
},
"node_modules/@babel/helper-compilation-targets": {
"version": "7.27.2",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
"integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
"integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/compat-data": "^7.27.2",
"@babel/helper-validator-option": "^7.27.1",
"@babel/compat-data": "^7.29.7",
"@babel/helper-validator-option": "^7.29.7",
"browserslist": "^4.24.0",
"lru-cache": "^5.1.1",
"semver": "^6.3.1"
@@ -144,9 +144,9 @@
}
},
"node_modules/@babel/helper-globals": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
"integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
"integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -154,29 +154,29 @@
}
},
"node_modules/@babel/helper-module-imports": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
"integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
"integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/traverse": "^7.27.1",
"@babel/types": "^7.27.1"
"@babel/traverse": "^7.29.7",
"@babel/types": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-transforms": {
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
"integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
"integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-module-imports": "^7.27.1",
"@babel/helper-validator-identifier": "^7.27.1",
"@babel/traverse": "^7.28.3"
"@babel/helper-module-imports": "^7.29.7",
"@babel/helper-validator-identifier": "^7.29.7",
"@babel/traverse": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
@@ -196,9 +196,9 @@
}
},
"node_modules/@babel/helper-string-parser": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
"integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -206,9 +206,9 @@
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.28.5",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
"integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -216,9 +216,9 @@
}
},
"node_modules/@babel/helper-validator-option": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
"integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
"integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -226,27 +226,27 @@
}
},
"node_modules/@babel/helpers": {
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
"integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz",
"integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/template": "^7.27.2",
"@babel/types": "^7.28.4"
"@babel/template": "^7.29.7",
"@babel/types": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
"version": "7.28.5",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
"integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
"integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.28.5"
"@babel/types": "^7.29.7"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -297,33 +297,33 @@
}
},
"node_modules/@babel/template": {
"version": "7.27.2",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
"integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
"integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/parser": "^7.27.2",
"@babel/types": "^7.27.1"
"@babel/code-frame": "^7.29.7",
"@babel/parser": "^7.29.7",
"@babel/types": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
"version": "7.28.5",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz",
"integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz",
"integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5",
"@babel/helper-globals": "^7.28.0",
"@babel/parser": "^7.28.5",
"@babel/template": "^7.27.2",
"@babel/types": "^7.28.5",
"@babel/code-frame": "^7.29.7",
"@babel/generator": "^7.29.7",
"@babel/helper-globals": "^7.29.7",
"@babel/parser": "^7.29.7",
"@babel/template": "^7.29.7",
"@babel/types": "^7.29.7",
"debug": "^4.3.1"
},
"engines": {
@@ -331,14 +331,14 @@
}
},
"node_modules/@babel/types": {
"version": "7.28.5",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
"integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
"integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
"@babel/helper-validator-identifier": "^7.28.5"
"@babel/helper-string-parser": "^7.29.7",
"@babel/helper-validator-identifier": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
@@ -2570,13 +2570,16 @@
}
},
"node_modules/baseline-browser-mapping": {
"version": "2.8.25",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.25.tgz",
"integrity": "sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA==",
"version": "2.10.38",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.38.tgz",
"integrity": "sha512-31/02mVB4yuQU6adKk5SlY6m+mxDwUq5KZkyYgnLrrKl7TEm1+3PyDtDBz2kOv/wxZz41GHsvV1A/u6RmiyBvw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"baseline-browser-mapping": "dist/cli.js"
"baseline-browser-mapping": "dist/cli.cjs"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/brace-expansion": {
@@ -2604,9 +2607,9 @@
}
},
"node_modules/browserslist": {
"version": "4.28.0",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz",
"integrity": "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==",
"version": "4.28.4",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.4.tgz",
"integrity": "sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==",
"dev": true,
"funding": [
{
@@ -2624,11 +2627,11 @@
],
"license": "MIT",
"dependencies": {
"baseline-browser-mapping": "^2.8.25",
"caniuse-lite": "^1.0.30001754",
"electron-to-chromium": "^1.5.249",
"node-releases": "^2.0.27",
"update-browserslist-db": "^1.1.4"
"baseline-browser-mapping": "^2.10.38",
"caniuse-lite": "^1.0.30001799",
"electron-to-chromium": "^1.5.376",
"node-releases": "^2.0.48",
"update-browserslist-db": "^1.2.3"
},
"bin": {
"browserslist": "cli.js"
@@ -2661,9 +2664,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001754",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001754.tgz",
"integrity": "sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==",
"version": "1.0.30001799",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz",
"integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==",
"dev": true,
"funding": [
{
@@ -2875,9 +2878,9 @@
}
},
"node_modules/electron-to-chromium": {
"version": "1.5.250",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.250.tgz",
"integrity": "sha512-/5UMj9IiGDMOFBnN4i7/Ry5onJrAGSbOGo3s9FEKmwobGq6xw832ccET0CE3CkkMBZ8GJSlUIesZofpyurqDXw==",
"version": "1.5.376",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.376.tgz",
"integrity": "sha512-cUVA7/RvbFTEuw/i3obUwDTRIXojaxkResf+ibByPFxjc6XK3VNtcQXV0NSbAlJ0FMjcJGgftVVB4Qo184EXvA==",
"dev": true,
"license": "ISC"
},
@@ -3338,16 +3341,16 @@
}
},
"node_modules/form-data": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz",
"integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
"hasown": "^2.0.4",
"mime-types": "^2.1.35"
},
"engines": {
"node": ">= 6"
@@ -3537,9 +3540,9 @@
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
"integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
@@ -3742,10 +3745,20 @@
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
"integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/puzrin"
},
{
"type": "github",
"url": "https://github.com/sponsors/nodeca"
}
],
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
@@ -4292,11 +4305,14 @@
"license": "MIT"
},
"node_modules/node-releases": {
"version": "2.0.27",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
"integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
"version": "2.0.48",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.48.tgz",
"integrity": "sha512-1uz8041X6LoI6ZSdZacM9lVY28vuzDlSKitnpbSNK0RfKoIJkX29NBPVEFXhnuSuEOA9Ww0xnPJ+ILWbGAv8DA==",
"dev": true,
"license": "MIT"
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
@@ -5137,9 +5153,9 @@
"license": "MIT"
},
"node_modules/update-browserslist-db": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz",
"integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==",
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
"integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
"dev": true,
"funding": [
{
@@ -5206,9 +5222,9 @@
}
},
"node_modules/vite": {
"version": "7.3.3",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.3.tgz",
"integrity": "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==",
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.5.tgz",
"integrity": "sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==",
"license": "MIT",
"dependencies": {
"esbuild": "^0.27.0",
+129 -117
View File
@@ -1,16 +1,16 @@
import { type FC, useRef, useCallback, useState, useEffect, useMemo, useSyncExternalStore } from 'react';
import HTMLFlipBook from 'react-pageflip';
import { type PageData } from '../types';
import BookPage from './BookPage';
import { ArrowLeft2, ArrowRight2, Maximize4, Pause, Play, RowVertical } from 'iconsax-react';
import { getPaperDimensions } from '@/config/paperSizes';
import type { DocumentSettings } from '@/pages/editor/store/editorStore';
import { useBookEntranceController } from '@/pages/viewer/hooks/useBookEntranceController';
import { useViewerViewport } from '@/pages/viewer/hooks/useViewerViewport';
import { useIsBrowserFullscreen } from '@/pages/viewer/hooks/useIsBrowserFullscreen';
import { toggleViewerFullscreen } from '@/pages/viewer/utils/viewerFullscreen';
import { isLegacyIOSSafari } from '@/pages/viewer/utils/isLegacyIOSSafari';
import { clx } from '@/helpers/utils';
import { getPaperDimensions } from "@/config/paperSizes";
import { clx } from "@/helpers/utils";
import type { DocumentSettings } from "@/pages/editor/store/editorStore";
import { useBookEntranceController } from "@/pages/viewer/hooks/useBookEntranceController";
import { useIsBrowserFullscreen } from "@/pages/viewer/hooks/useIsBrowserFullscreen";
import { useViewerViewport } from "@/pages/viewer/hooks/useViewerViewport";
import { isLegacyIOSSafari } from "@/pages/viewer/utils/isLegacyIOSSafari";
import { toggleViewerFullscreen } from "@/pages/viewer/utils/viewerFullscreen";
import { ArrowLeft2, ArrowRight2, Maximize4, Pause, Play, RowVertical } from "iconsax-react";
import { type FC, useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
import HTMLFlipBook from "react-pageflip";
import { type PageData } from "../types";
import BookPage from "./BookPage";
/** فاصله پیش‌فرض بین هر ورق در پخش خودکار (میلی‌ثانیه) */
const AUTO_PLAY_INTERVAL_MS = 3000;
@@ -22,19 +22,19 @@ const BOOK_TOP_GAP_MOBILE = 32;
/** نسخهٔ فعلی `page-flip.mp3` دو ضربهٔ ورق پشت‌هم دارد؛ فقط تا این سهم از طول پخش می‌شود */
const PAGE_FLIP_SOUND_PLAY_FRACTION = 0.5;
/** هم‌راستا با `md` تیلویند (768px) — موبایل: زیر این عرض */
const MOBILE_QUERY = '(min-width: 768px)';
const MOBILE_QUERY = "(min-width: 768px)";
function subscribeDesktopMql(callback: () => void) {
const mq = window.matchMedia(MOBILE_QUERY);
mq.addEventListener('change', callback);
return () => mq.removeEventListener('change', callback);
mq.addEventListener("change", callback);
return () => mq.removeEventListener("change", callback);
}
function useIsDesktop() {
return useSyncExternalStore(
subscribeDesktopMql,
() => window.matchMedia(MOBILE_QUERY).matches,
() => true
() => true,
);
}
@@ -42,7 +42,7 @@ function useLegacyIOSSafari() {
return useSyncExternalStore(
() => () => {},
isLegacyIOSSafari,
() => false
() => false,
);
}
@@ -88,14 +88,10 @@ const AutoPlayToggleButton: FC<AutoPlayToggleButtonProps> = ({ isActive, onToggl
type="button"
onClick={onToggle}
className="px-3 py-1.5 text-xs md:text-sm font-medium inline-flex items-center justify-center gap-1.5 rounded-full border border-gray-300 hover:bg-gray-100 active:bg-gray-200 transition-all"
aria-label={isActive ? 'توقف پخش خودکار' : 'شروع پخش خودکار'}
aria-label={isActive ? "توقف پخش خودکار" : "شروع پخش خودکار"}
>
{isActive ? (
<Pause color='black' size={16} className="text-gray-700 md:w-[18px] md:h-[18px]" />
) : (
<Play color='black' size={16} className="text-gray-700 md:w-[18px] md:h-[18px]" />
)}
<span>{isActive ? 'توقف پخش' : 'پخش خودکار'}</span>
{isActive ? <Pause color="black" size={16} className="text-gray-700 md:w-[18px] md:h-[18px]" /> : <Play color="black" size={16} className="text-gray-700 md:w-[18px] md:h-[18px]" />}
<span>{isActive ? "توقف پخش" : "پخش خودکار"}</span>
</button>
);
@@ -133,24 +129,26 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
const autoPlay = documentSettings?.autoPlay ?? false;
const pageFlipSound = documentSettings?.pageFlipSound ?? true;
const leftToRightFlip = documentSettings?.leftToRightFlip ?? false;
const displayStyle = documentSettings?.displayStyle ?? 'double';
const backgroundType = documentSettings?.backgroundType ?? 'color';
const backgroundColor = documentSettings?.backgroundColor ?? '#ffffff';
/** react-pageflip از RTL پشتیبانی نمی‌کند؛ ترتیب صفحات برعکس + شروع از آخر (فقط دسکتاپ) */
const isRtlBook = !leftToRightFlip && isDesktop;
const displayPages = useMemo(() => (isRtlBook ? [...pages].reverse() : pages), [pages, isRtlBook]);
const toLogicalIndex = useCallback((flipIndex: number) => (isRtlBook ? pages.length - 1 - flipIndex : flipIndex), [isRtlBook, pages.length]);
const toFlipIndex = useCallback((logicalIndex: number) => (isRtlBook ? pages.length - 1 - logicalIndex : logicalIndex), [isRtlBook, pages.length]);
const displayStyle = documentSettings?.displayStyle ?? "double";
const backgroundType = documentSettings?.backgroundType ?? "color";
const backgroundColor = documentSettings?.backgroundColor ?? "#ffffff";
const backgroundGradient = documentSettings?.backgroundGradient;
const backgroundImageUrl = documentSettings?.backgroundImageUrl ?? '';
const backgroundImageUrl = documentSettings?.backgroundImageUrl ?? "";
useEffect(() => {
setIsAutoPlayActive(autoPlay);
}, [autoPlay]);
// سایز کتاب بر اساس catalogSize (A3/A4/A5)
const { width: bookWidth, height: bookHeight } = useMemo(
() => getPaperDimensions(catalogSize ?? 'a4'),
[catalogSize]
);
const { width: bookWidth, height: bookHeight } = useMemo(() => getPaperDimensions(catalogSize ?? "a4"), [catalogSize]);
const hasMultiplePages = pages.length > 1;
const viewport = useViewerViewport();
const usePortrait = !isDesktop || displayStyle === 'single';
const usePortrait = !isDesktop || displayStyle === "single";
const viewportFit = useMemo(() => {
const padX = isDesktop ? 48 : 20;
@@ -175,7 +173,7 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
const pagePixelHeight = viewportFit.h;
const contentScale = viewportFit.contentScale;
const flipKey = `${usePortrait ? 'portrait' : 'spread'}-${pagePixelWidth}-${pagePixelHeight}`;
const flipKey = `${usePortrait ? "portrait" : "spread"}-${pagePixelWidth}-${pagePixelHeight}`;
const {
scheduleEntranceForSpread,
@@ -190,10 +188,10 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
const commitEntranceAtCurrentSpread = useCallback(() => {
const api = bookRef.current?.pageFlip();
const idx = api?.getCurrentPageIndex?.();
if (typeof idx !== 'number' || pages.length <= 0) return;
const clamped = Math.max(0, Math.min(idx, pages.length - 1));
scheduleEntranceForSpread(clamped);
}, [pages.length, scheduleEntranceForSpread]);
if (typeof idx !== "number" || pages.length <= 0) return;
const flipIdx = Math.max(0, Math.min(idx, pages.length - 1));
scheduleEntranceForSpread(toLogicalIndex(flipIdx));
}, [pages.length, scheduleEntranceForSpread, toLogicalIndex]);
useEffect(() => {
resetEntrance();
@@ -218,14 +216,14 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
useEffect(() => {
const base = import.meta.env.BASE_URL;
const src = `${base}${base.endsWith('/') ? '' : '/'}sounds/page-flip.mp3`;
const src = `${base}${base.endsWith("/") ? "" : "/"}sounds/page-flip.mp3`;
const audio = new Audio(src);
audio.preload = 'auto';
audio.preload = "auto";
pageFlipSoundRef.current = audio;
return () => {
const trim = pageFlipSoundTrimListenerRef.current;
if (trim) {
audio.removeEventListener('timeupdate', trim);
audio.removeEventListener("timeupdate", trim);
pageFlipSoundTrimListenerRef.current = null;
}
audio.pause();
@@ -238,7 +236,7 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
if (!audio) return;
const prevTrim = pageFlipSoundTrimListenerRef.current;
if (prevTrim) {
audio.removeEventListener('timeupdate', prevTrim);
audio.removeEventListener("timeupdate", prevTrim);
pageFlipSoundTrimListenerRef.current = null;
}
const onTimeUpdate = () => {
@@ -248,44 +246,49 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
if (audio.currentTime >= end) {
audio.pause();
audio.currentTime = 0;
audio.removeEventListener('timeupdate', onTimeUpdate);
audio.removeEventListener("timeupdate", onTimeUpdate);
pageFlipSoundTrimListenerRef.current = null;
}
};
pageFlipSoundTrimListenerRef.current = onTimeUpdate;
try {
audio.currentTime = 0;
audio.addEventListener('timeupdate', onTimeUpdate);
audio.addEventListener("timeupdate", onTimeUpdate);
void audio.play().catch(() => {
audio.removeEventListener('timeupdate', onTimeUpdate);
audio.removeEventListener("timeupdate", onTimeUpdate);
pageFlipSoundTrimListenerRef.current = null;
/* autoplay policy — معمولاً بعد از تعامل کاربر ورق می‌خورد */
});
} catch {
audio.removeEventListener('timeupdate', onTimeUpdate);
audio.removeEventListener("timeupdate", onTimeUpdate);
pageFlipSoundTrimListenerRef.current = null;
}
}, []);
const handlePageFlip = useCallback((e: FlipEvent) => {
const handlePageFlip = useCallback(
(e: FlipEvent) => {
// react-pageflip شماره صفحه را به صورت 0-based برمی‌گرداند
// اما باید مطمئن شویم که در محدوده معتبر است
let pageNum = e.data;
let flipIndex = e.data;
// اگر pageNum منفی است یا بزرگتر از تعداد صفحات، آن را محدود می‌کنیم
if (pageNum < 0) {
pageNum = 0;
} else if (pageNum >= pages.length) {
pageNum = pages.length - 1;
// اگر flipIndex منفی است یا بزرگتر از تعداد صفحات، آن را محدود می‌کنیم
if (flipIndex < 0) {
flipIndex = 0;
} else if (flipIndex >= pages.length) {
flipIndex = pages.length - 1;
}
const pageNum = toLogicalIndex(flipIndex);
if (pageFlipSound && lastFlipPageRef.current !== null && lastFlipPageRef.current !== pageNum) {
playPageFlipSound();
}
lastFlipPageRef.current = pageNum;
setCurrentPage(pageNum);
}, [pages.length, pageFlipSound, playPageFlipSound]);
},
[pages.length, pageFlipSound, playPageFlipSound, toLogicalIndex],
);
pageFlipHandlerRef.current = handlePageFlip;
@@ -296,28 +299,29 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
const syncPageIndexFromBook = useCallback(() => {
const api = bookRef.current?.pageFlip();
const idx = api?.getCurrentPageIndex?.();
if (typeof idx !== 'number' || pages.length <= 0) return;
const clamped = Math.max(0, Math.min(idx, pages.length - 1));
setCurrentPage(clamped);
}, [pages.length]);
if (typeof idx !== "number" || pages.length <= 0) return;
const flipIdx = Math.max(0, Math.min(idx, pages.length - 1));
setCurrentPage(toLogicalIndex(flipIdx));
}, [pages.length, toLogicalIndex]);
const handleBookInit = useCallback(
(e: PageFlipInitEvent) => {
const p = e.data?.page;
if (typeof p !== 'number' || pages.length <= 0) return;
const clamped = Math.max(0, Math.min(p, pages.length - 1));
setCurrentPage(clamped);
lastFlipPageRef.current = clamped;
if (typeof p !== "number" || pages.length <= 0) return;
const flipIdx = Math.max(0, Math.min(p, pages.length - 1));
const logicalIdx = toLogicalIndex(flipIdx);
setCurrentPage(logicalIdx);
lastFlipPageRef.current = logicalIdx;
// اولین اسپرد قبل از onChangeState('read') هم باید انیمیشن ورود بگیرد
scheduleEntranceForSpread(clamped);
scheduleEntranceForSpread(logicalIdx);
},
[pages.length, scheduleEntranceForSpread],
[pages.length, scheduleEntranceForSpread, toLogicalIndex],
);
/** بعد از اتمام انیمیشن ورق، ایندکس واقعی کتاب را می‌گیریم و انیمیشن ورود را یک‌بار شروع می‌کنیم */
const handleChangeState = useCallback(
(e: PageFlipStateEvent) => {
if (e.data !== 'read') return;
if (e.data !== "read") return;
syncPageIndexFromBook();
commitEntranceAtCurrentSpread();
},
@@ -325,16 +329,24 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
);
const goToNextPage = useCallback(() => {
if (currentPage < pages.length - 1) {
bookRef.current?.pageFlip()?.flipNext();
if (currentPage >= pages.length - 1) return;
const api = bookRef.current?.pageFlip() as PageFlipWithFlipController | undefined;
if (isRtlBook) {
triggerFlipPrev(api);
} else {
api?.flipNext();
}
}, [currentPage, pages.length]);
}, [currentPage, pages.length, isRtlBook]);
const goToPrevPage = useCallback(() => {
if (currentPage <= 0) return;
const api = bookRef.current?.pageFlip() as PageFlipWithFlipController | undefined;
if (isRtlBook) {
api?.flipNext();
} else {
triggerFlipPrev(api);
}, [currentPage]);
}
}, [currentPage, isRtlBook]);
const stopAutoPlayByUser = useCallback(() => {
if (isAutoPlayActive) {
@@ -344,7 +356,7 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
const toggleAutoPlay = useCallback(() => {
if (pages.length <= 1) return;
setIsAutoPlayActive(prev => !prev);
setIsAutoPlayActive((prev) => !prev);
}, [pages.length]);
const navigateToPageIndex = useCallback(
@@ -352,12 +364,13 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
const pageFlipAPI = bookRef.current?.pageFlip();
if (!pageFlipAPI) return;
const clamped = Math.max(0, Math.min(targetIndex, pages.length - 1));
pageFlipAPI.turnToPage(clamped);
lastFlipPageRef.current = clamped;
scheduleEntranceForSpread(clamped);
const logicalIdx = Math.max(0, Math.min(targetIndex, pages.length - 1));
const flipIdx = toFlipIndex(logicalIdx);
pageFlipAPI.turnToPage(flipIdx);
lastFlipPageRef.current = logicalIdx;
scheduleEntranceForSpread(logicalIdx);
},
[pages.length, scheduleEntranceForSpread],
[pages.length, scheduleEntranceForSpread, toFlipIndex],
);
const handleLinkClick = useCallback(
@@ -365,17 +378,17 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
if (pages.length <= 1) return;
stopAutoPlayByUser();
if (!linkUrl.startsWith('page://')) return;
if (!linkUrl.startsWith("page://")) return;
const action = linkUrl.replace('page://', '');
const action = linkUrl.replace("page://", "");
if (action === 'next') {
if (action === "next") {
goToNextPage();
} else if (action === 'prev') {
} else if (action === "prev") {
goToPrevPage();
} else if (action === 'first') {
} else if (action === "first") {
navigateToPageIndex(0);
} else if (action === 'last') {
} else if (action === "last") {
navigateToPageIndex(pages.length - 1);
} else {
const pageNum = parseInt(action, 10);
@@ -392,40 +405,49 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
if (prevFlipKeyRef.current !== flipKey) {
prevFlipKeyRef.current = flipKey;
lastFlipPageRef.current = currentPage;
setStartPage(currentPage);
setStartPage(toFlipIndex(currentPage));
}
}, [flipKey, currentPage]);
}, [flipKey, currentPage, toFlipIndex]);
useEffect(() => {
if (pages.length <= 0) return;
const initialFlipPage = isRtlBook ? pages.length - 1 : 0;
setStartPage(initialFlipPage);
setCurrentPage(0);
lastFlipPageRef.current = 0;
}, [pages.length, pages[0]?.id, isRtlBook]);
// پخش خودکار
useEffect(() => {
if (!isAutoPlayActive || pages.length <= 1) return;
const timer = setInterval(() => {
if (currentPage >= pages.length - 1) {
bookRef.current?.pageFlip()?.turnToPage?.(0);
const firstFlipPage = isRtlBook ? pages.length - 1 : 0;
bookRef.current?.pageFlip()?.turnToPage?.(firstFlipPage);
setCurrentPage(0);
lastFlipPageRef.current = 0;
return;
}
bookRef.current?.pageFlip()?.flipNext();
const api = bookRef.current?.pageFlip() as PageFlipWithFlipController | undefined;
if (isRtlBook) {
triggerFlipPrev(api);
} else {
api?.flipNext();
}
}, AUTO_PLAY_INTERVAL_MS);
return () => clearInterval(timer);
}, [isAutoPlayActive, currentPage, pages.length]);
}, [isAutoPlayActive, currentPage, pages.length, isRtlBook]);
return (
<div
className="flex flex-col items-center gap-4 md:gap-6 w-full h-full max-md:min-h-0 max-md:max-w-full max-md:overflow-x-hidden max-md:pb-[env(safe-area-inset-bottom,0px)]"
dir="rtl"
>
<div
className="relative w-full flex flex-col flex-1 max-md:min-h-0 pt-8 md:pt-12 min-h-0 overflow-hidden"
>
<div className="flex flex-col items-center gap-4 md:gap-6 w-full h-full max-md:min-h-0 max-md:max-w-full max-md:overflow-x-hidden max-md:pb-[env(safe-area-inset-bottom,0px)]" dir="rtl">
<div className="relative w-full flex flex-col flex-1 max-md:min-h-0 pt-8 md:pt-12 min-h-0 overflow-hidden">
<div className="relative flex flex-1 min-h-0 w-full items-center justify-center">
{/* Border و Shadow عمودی در وسط (فقط حالت دوصفحه‌ای دسکتاپ) */}
{!usePortrait && (
<div
className="hidden md:block 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)',
boxShadow: "-5px 0 15px rgba(0, 0, 0, 0.15), 5px 0 15px rgba(0, 0, 0, 0.15)",
}}
/>
)}
@@ -437,12 +459,10 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
dir="ltr"
className="max-w-full flex justify-center shrink-0 rounded-sm"
style={{
overflow: 'hidden',
overflow: "hidden",
width: usePortrait ? pagePixelWidth : pagePixelWidth * 2,
height: pagePixelHeight,
boxShadow: isDesktop
? undefined
: '0 8px 32px rgba(0, 0, 0, 0.14), 0 2px 8px rgba(0, 0, 0, 0.08)',
boxShadow: isDesktop ? undefined : "0 8px 32px rgba(0, 0, 0, 0.14), 0 2px 8px rgba(0, 0, 0, 0.08)",
}}
>
<HTMLFlipBook
@@ -469,13 +489,13 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
startZIndex={1}
showPageCorners={hasMultiplePages}
disableFlipByClick={true}
className={clx('flipbook-container mx-auto max-w-full', legacyIOS && 'flipbook-legacy-ios')}
className={clx("flipbook-container mx-auto max-w-full", legacyIOS && "flipbook-legacy-ios")}
style={{}}
onFlip={onFlipForwarded}
onInit={handleBookInit}
onChangeState={handleChangeState}
>
{pages.map((page) => (
{displayPages.map((page) => (
<BookPage
key={page.id}
page={page}
@@ -512,9 +532,9 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
}}
disabled={leftToRightFlip ? currentPage <= 0 : currentPage >= pages.length - 1}
className="p-2 md:p-2 min-h-11 min-w-11 md:min-h-0 md:min-w-0 inline-flex items-center justify-center rounded-full hover:bg-gray-100 active:bg-gray-200 disabled:opacity-30 disabled:cursor-not-allowed transition-all"
aria-label={leftToRightFlip ? 'صفحه قبل' : 'صفحه بعد'}
aria-label={leftToRightFlip ? "صفحه قبل" : "صفحه بعد"}
>
<ArrowRight2 color='black' size={20} className="text-gray-700 md:w-6 md:h-6" />
<ArrowRight2 color="black" size={20} className="text-gray-700 md:w-6 md:h-6" />
</button>
</>
)}
@@ -526,8 +546,6 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
</span>
</div>
{hasMultiplePages && (
<>
{/* دکمه چپ: فارسی = صفحه قبل | لاتین = صفحه بعد */}
@@ -543,29 +561,23 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
}}
disabled={leftToRightFlip ? currentPage >= pages.length - 1 : currentPage <= 0}
className="p-2 md:p-2 min-h-11 min-w-11 md:min-h-0 md:min-w-0 inline-flex items-center justify-center rounded-full hover:bg-gray-100 active:bg-gray-200 disabled:opacity-30 disabled:cursor-not-allowed transition-all"
aria-label={leftToRightFlip ? 'صفحه بعد' : 'صفحه قبل'}
aria-label={leftToRightFlip ? "صفحه بعد" : "صفحه قبل"}
>
<ArrowLeft2 color='black' size={20} className="text-gray-700 md:w-6 md:h-6" />
<ArrowLeft2 color="black" size={20} className="text-gray-700 md:w-6 md:h-6" />
</button>
</>
)}
{hasMultiplePages && autoPlay && (
<AutoPlayToggleButton isActive={isAutoPlayActive} onToggle={toggleAutoPlay} />
)}
{hasMultiplePages && autoPlay && <AutoPlayToggleButton isActive={isAutoPlayActive} onToggle={toggleAutoPlay} />}
<button
type="button"
onClick={toggleViewerFullscreen}
className="p-2 md:p-2 min-h-11 min-w-11 md:min-h-0 md:min-w-0 inline-flex items-center justify-center rounded-full hover:bg-gray-100 active:bg-gray-200 transition-all"
aria-label={isBrowserFullscreen ? 'خروج از تمام‌صفحه' : 'تمام‌صفحه مرورگر'}
title={isBrowserFullscreen ? 'خروج از تمام‌صفحه' : 'تمام‌صفحه مرورگر'}
aria-label={isBrowserFullscreen ? "خروج از تمام‌صفحه" : "تمام‌صفحه مرورگر"}
title={isBrowserFullscreen ? "خروج از تمام‌صفحه" : "تمام‌صفحه مرورگر"}
>
{isBrowserFullscreen ? (
<RowVertical color="black" size={20} className="text-gray-700 md:w-6 md:h-6" />
) : (
<Maximize4 color="black" size={20} className="text-gray-700 md:w-6 md:h-6" />
)}
{isBrowserFullscreen ? <RowVertical color="black" size={20} className="text-gray-700 md:w-6 md:h-6" /> : <Maximize4 color="black" size={20} className="text-gray-700 md:w-6 md:h-6" />}
</button>
</div>
</div>