diff --git a/.gitignore b/.gitignore index a547bf3..6528a17 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ node_modules dist dist-ssr *.local +public/virtual-tours # Editor directories and files .vscode/* diff --git a/bun.lock b/bun.lock index 6ccadbd..121d939 100644 --- a/bun.lock +++ b/bun.lock @@ -24,6 +24,7 @@ "@types/react-dom": "^19.2.2", "@vitejs/plugin-react": "^5.1.0", "autoprefixer": "^10.4.21", + "baseline-browser-mapping": "^2.9.14", "eslint": "^9.39.1", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.24", @@ -236,7 +237,7 @@ "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - "baseline-browser-mapping": ["baseline-browser-mapping@2.8.25", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.9.14", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg=="], "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], @@ -644,6 +645,8 @@ "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "browserslist/baseline-browser-mapping": ["baseline-browser-mapping@2.8.25", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA=="], + "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], diff --git a/package.json b/package.json index a755c36..5c06b8e 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@types/react-dom": "^19.2.2", "@vitejs/plugin-react": "^5.1.0", "autoprefixer": "^10.4.21", + "baseline-browser-mapping": "^2.9.14", "eslint": "^9.39.1", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.24", diff --git a/src/components/UnitFilters.tsx b/src/components/UnitFilters.tsx index 3e280f0..047f2da 100644 --- a/src/components/UnitFilters.tsx +++ b/src/components/UnitFilters.tsx @@ -19,9 +19,7 @@ function UnitFilters({ className }: { className?: string }) { // Инициализация фильтров из URL при монтировании useEffect(() => { const units = searchParams.get("units"); - if (units) { - setFiltersFromURLParams(units.split(",")); - } + if (units) setFiltersFromURLParams(units.split(",")); }, [searchParams, setFiltersFromURLParams]); const filterCount = Object.entries(UNIT_TYPE_MAPPING).filter(([key]) => @@ -52,9 +50,7 @@ function UnitFilters({ className }: { className?: string }) { ) ); setSearchParams({ units: unitsArray.join(",") }); - } else { - setSearchParams({}); - } + } else resetURLParams(); } function resetURLParams() { @@ -79,7 +75,6 @@ function UnitFilters({ className }: { className?: string }) { {!showFiltersPopup && ( + + + ); +} + +export default VirtualDTourIcon; diff --git a/src/components/layouts/DefaultLayout.tsx b/src/components/layouts/DefaultLayout.tsx index 0b78651..5fb84b0 100644 --- a/src/components/layouts/DefaultLayout.tsx +++ b/src/components/layouts/DefaultLayout.tsx @@ -4,45 +4,26 @@ import { useEffect } from "react"; function DefaultLayout() { useEffect(() => { - const setVh = () => { - // Получаем реальную высоту viewport + function setVh() { const vh = window.innerHeight * 0.01; document.documentElement.style.setProperty("--vh", `${vh}px`); - }; + } - // const setVw = () => { - // const vw = window.innerWidth * 0.01; - // document.documentElement.style.setProperty("--vw", `${vw}px`); - // }; - - // Устанавливаем при монтировании setVh(); - // setVw(); - // Обновляем при изменении размера окна и ориентации - window.addEventListener("resize", () => { - setVh(); - // setVw(); - }); - window.addEventListener("orientationchange", () => { - setVh(); - // setVw(); - }); + window.addEventListener("DOMContentLoaded", setVh); + window.addEventListener("resize", setVh) + window.addEventListener("orientationchange", setVh); return () => { - window.removeEventListener("resize", () => { - setVh(); - // setVw(); - }); - window.removeEventListener("orientationchange", () => { - setVh(); - // setVw(); - }); + window.removeEventListener("resize", setVh); + window.removeEventListener("orientationchange", setVh); + window.removeEventListener("DOMContentLoaded", setVh); }; }, []); return ( -
+
diff --git a/src/components/layouts/EmptyLayout.tsx b/src/components/layouts/EmptyLayout.tsx new file mode 100644 index 0000000..f937f73 --- /dev/null +++ b/src/components/layouts/EmptyLayout.tsx @@ -0,0 +1,13 @@ +import { Outlet } from "react-router"; + +function EmptyLayout() { + return ( +
+
+ +
+
+ ); +} + +export default EmptyLayout; diff --git a/src/components/layouts/LayoutWithoutFooter.tsx b/src/components/layouts/LayoutWithoutFooter.tsx index 39ae379..b25edb5 100644 --- a/src/components/layouts/LayoutWithoutFooter.tsx +++ b/src/components/layouts/LayoutWithoutFooter.tsx @@ -3,6 +3,27 @@ import NavMenu from "./NavMenu"; import { useEffect } from "react"; function LayoutWithoutFooter() { + useEffect(() => { + function setVh() { + const vh = window.innerHeight * 0.01; + document.documentElement.style.setProperty("--vh", `${vh}px`); + document.body.style.setProperty("overflow", "hidden"); + } + + setVh(); + + window.addEventListener("resize", setVh); + window.addEventListener("orientationchange", setVh); + window.addEventListener("DOMContentLoaded", setVh); + + return () => { + window.removeEventListener("resize", setVh); + window.removeEventListener("orientationchange", setVh); + window.removeEventListener("DOMContentLoaded", setVh); + document.body.style.removeProperty("overflow"); + }; + }, []); + const location = useLocation(); useEffect(() => { @@ -10,9 +31,9 @@ function LayoutWithoutFooter() { }, [location.pathname]); return ( -
+
-
+
diff --git a/src/components/pages/AboutPage.tsx b/src/components/pages/AboutPage.tsx index ad63037..3a7685a 100644 --- a/src/components/pages/AboutPage.tsx +++ b/src/components/pages/AboutPage.tsx @@ -33,7 +33,7 @@ export default function AboutPage() { function AboutHero() { return ( -
+
+
@@ -86,7 +87,7 @@ function BlockPage() {
-
+

Block {blockNumber}

@@ -133,7 +134,7 @@ function BlockPage() {
-
+
+ + + +

diff --git a/src/components/pages/MasterplanPage.tsx b/src/components/pages/MasterplanPage.tsx index 466ed9e..86d2adb 100644 --- a/src/components/pages/MasterplanPage.tsx +++ b/src/components/pages/MasterplanPage.tsx @@ -43,16 +43,12 @@ function constrainPosition( const scaledWidth = imageSize.width * zoom; const scaledHeight = imageSize.height * zoom; - console.log(imageSize); - // Строгие границы: изображение не должно выходить за пределы контейнера // Максимальная позиция: 0 (верхний левый угол контейнера) // Минимальная позиция: когда правый/нижний край изображения совпадает с правым/нижним краем контейнера const minX = containerSize.width - scaledWidth; const minY = containerSize.height - scaledHeight; - console.log({ minX, minY, position, scaledWidth, scaledHeight, zoom }); - return { x: Math.min(0, Math.max(minX, position.x)), y: Math.min(0, Math.max(minY, position.y)), @@ -105,8 +101,6 @@ function calculateCenterPosition( }; } -// const FRAME_COUNT = 360; -// const FRAME_STEP = 90; const FRAME_COUNT = 120; const FRAME_STEP = 30; @@ -774,10 +768,6 @@ function MasterplanPage() { const currentFrame = Math.floor(currentIndex / FRAME_STEP); - useEffect(() => { - console.log("zoom", zoom); - }, [zoom]); - return (
@@ -900,7 +890,7 @@ function MasterplanPage() { key={`${currentFrame}-${index}`} style={{ display: !blockHasSelectedUnits(index) ? "none" : "block", - transform: `scale(${1 / zoom})`, + scale: 1 / zoom, transformOrigin: `${ x - Math.max(0.00694 * innerWidth, 10) / 2 }px ${y - Math.max(0.00694 * innerWidth, 10) / 2}px`, @@ -913,7 +903,10 @@ function MasterplanPage() { fill="white" stroke="rgba(50, 77, 67, 0.15)" strokeWidth={2} - className="cursor-pointer" + className={clsx( + "cursor-pointer", + !blockHasSelectedUnits(index) && "hidden" + )} /> ))} @@ -942,7 +935,7 @@ function MasterplanPage() { ))} {/* Facilities */} - + {isShowVideo && hoveredMaskIndex === null && facilitiesPoints[currentFrame].map( @@ -951,9 +944,10 @@ function MasterplanPage() { initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} - key={`facility-${currentFrame}-${index}-${isShowVideo}-${hoveredMaskIndex}`} + key={`facility-${currentFrame}-${index}`} style={{ - transform: `scale(${1 / zoom})`, + scale: 1 / zoom, + // transform: `scale(${1 / zoom})`, transformOrigin: `${ x - Math.max(0.02222 * innerWidth, 32) / 2 }px ${y - Math.max(0.02222 * innerWidth, 32) / 2}px`, diff --git a/src/components/pages/SurroundingsPage.tsx b/src/components/pages/SurroundingsPage.tsx index aec68fc..0ca5778 100644 --- a/src/components/pages/SurroundingsPage.tsx +++ b/src/components/pages/SurroundingsPage.tsx @@ -1014,7 +1014,7 @@ function SurroundingsPage({ maxZoom = 3 }: MapProps) { style={imageStyle} viewBox="0 0 4096 2176" > - + {selectedPoint && (Array.isArray(selectedPoint.path) ? ( // Для составных путей: сначала пунктир (index 1), потом сплошная (index 0) diff --git a/src/components/pages/VirtualTourPage.tsx b/src/components/pages/VirtualTourPage.tsx new file mode 100644 index 0000000..500a5fb --- /dev/null +++ b/src/components/pages/VirtualTourPage.tsx @@ -0,0 +1,49 @@ +import { Link, useParams } from "react-router"; +import Button from "../ui/Button"; +import ChevronLeftIcon from "../icons/ChevronLeftIcon"; + +function VirtualTourPage() { + const { blockNumber } = useParams(); + + return ( +
+