This commit is contained in:
2024-04-05 17:55:54 +05:00
16 changed files with 401 additions and 6 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

@@ -0,0 +1,87 @@
import { useEffect, useState } from "react";
import { environments, transports } from "../../consts/infrastructure";
interface IFilter {
filterId: string;
}
const data: IFilter[] = [
{ filterId: "1" },
{ filterId: "1" },
{ filterId: "2" },
{ filterId: "2" },
{ filterId: "2" },
{ filterId: "3" },
{ filterId: "3" },
{ filterId: "3" },
{ filterId: "4" },
{ filterId: "4" },
{ filterId: "5" },
{ filterId: "5" },
{ filterId: "5" },
{ filterId: "6" },
{ filterId: "6" },
{ filterId: "7" },
];
const InfrastructureFilters = () => {
const [filter, setFilter] = useState("");
function handleOnFilterClick(
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
): void {
const filterId = event.currentTarget.dataset.set;
if (!filterId) return;
setFilter(filterId);
}
useEffect(() => {
const filteredData = data.filter((d) => d.filterId === filter);
console.log("filteredData", filteredData);
}, [filter]);
return (
<div>
<h2 className="font-tenor text-2xl mb-8">Инфраструктура</h2>
<h3 className="text-[18px] mb-5">Окружение:</h3>
<div className="flex flex-col gap-2 mb-8 border-b border-[#9595A2] pb-8">
{environments.map((env) => (
<button
data-set={env.id}
onClick={handleOnFilterClick}
key={env.id}
className={`text-white w-fit ${
filter !== env.id
? "opacity-60 border-b-2 border-b-transparent"
: "border-b-2 border-b-[#0079C2]"
} flex gap-2 font-medium text-sm px-[2px] py-2 transition-all duration-300 ease-in-out`}
>
{env.icon}
<div>{env.label}</div>
</button>
))}
</div>
<div className="flex flex-col gap-2 border-b border-[#9595A2] pb-8">
<h3 className="text-[18px] mb-5">Транспорт:</h3>
{transports.map((trans) => (
<button
data-set={trans.id}
onClick={handleOnFilterClick}
key={trans.id}
className={`text-white w-fit ${
filter !== trans.id
? "opacity-60 border-b-2 border-b-transparent"
: "border-b-2 border-b-[#0079C2]"
} flex gap-2 font-medium text-sm px-[2px] py-2 transition-all duration-300 ease-in-out`}
>
{trans.icon}
<div>{trans.label}</div>
</button>
))}
</div>
</div>
);
};
export default InfrastructureFilters;
@@ -0,0 +1,33 @@
import { rangeStart, rangeEnd, rangeStep } from "../../consts/infrastructure";
interface IInputRangeProps {
selectedRange: number;
handleOnChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
}
const InputRange = ({ handleOnChange, selectedRange }: IInputRangeProps) => {
return (
<div className="flex flex-col gap-3 justify-between h-full">
<div className="flex flex-col gap-1">
<h3 className="text-[18px]">Дистанция ходьбы:</h3>
<p className="text-xs">минут / метров</p>
</div>
<div className="flex flex-col justify-center items-center w-full">
<p className="text-[10px] font-medium">
{selectedRange} / {rangeEnd}
</p>
<input
type="range"
min={rangeStart}
max={rangeEnd}
step={rangeStep}
className="w-full"
value={selectedRange}
onChange={handleOnChange}
/>
</div>
</div>
);
};
export default InputRange;
+26
View File
@@ -0,0 +1,26 @@
import BusIcon from "../icons/BusIcon";
import EducationIcon from "../icons/EducationIcon";
import HealthyIcon from "../icons/HealthyIcon";
import MallIcon from "../icons/MallIcon";
import RestourauntIcon from "../icons/RestourantIcon";
import SportIcon from "../icons/SportIcon";
import TramIcon from "../icons/TramIcon";
import { IInfrastructure } from "../types/Infrastructure";
export const environments: IInfrastructure[] = [
{ id: "1", icon: <MallIcon />, label: "Торговые центры" },
{ id: "2", icon: <RestourauntIcon />, label: "Рестораны" },
{ id: "3", icon: <HealthyIcon />, label: "Здоровье" },
{ id: "4", icon: <EducationIcon />, label: "Образование" },
{ id: "5", icon: <SportIcon />, label: "Спорт" },
];
export const transports: IInfrastructure[] = [
{ id: "6", icon: <BusIcon />, label: "Автобус" },
{ id: "7", icon: <TramIcon />, label: "Трамвай" },
];
export const rangeStart = 200;
export const rangeEnd = 850;
export const rangeStep = 50;
export const arrayLength = (rangeEnd - rangeStart) / rangeStep + 1; // 14
+24
View File
@@ -0,0 +1,24 @@
const BusIcon = () => {
return (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M3 10V16H17V10M3 10V2H17V10M3 10H17"
stroke="currentColor"
strokeWidth="1.25"
strokeLinecap="square"
/>
<circle cx="6" cy="13" r="1" fill="currentColor" />
<circle cx="14" cy="13" r="1" fill="currentColor" />
<rect x="4" y="15.5" width="3" height="3.5" fill="currentColor" />
<rect x="13" y="15.5" width="3" height="3.5" fill="currentColor" />
</svg>
);
};
export default BusIcon;
+21
View File
@@ -0,0 +1,21 @@
const CrossIcon = () => {
return (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12.0002 12.0001L17.6572 6.34356M12.0002 12.0001L6.34337 6.34326M12.0002 12.0001L17.6571 17.657M12.0002 12.0001L6.34326 17.6571"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
};
export default CrossIcon;
+27
View File
@@ -0,0 +1,27 @@
const EducationIcon = () => {
return (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clip-path="url(#clip0_133_6227)">
<path
d="M4.66667 7.1875H2L10 2.5L18 7.1875H15.3333M4.66667 7.1875V14.6875M4.66667 7.1875H8.22222M4.66667 14.6875H3.33333L2 17.5H18L16.6667 14.6875H15.3333M4.66667 14.6875H8.22222M15.3333 7.1875V14.6875M15.3333 7.1875H11.7778M15.3333 14.6875H11.7778M11.7778 7.1875V14.6875M11.7778 7.1875H8.22222M11.7778 14.6875H8.22222M8.22222 7.1875V14.6875"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="square"
/>
</g>
<defs>
<clipPath id="clip0_133_6227">
<rect width="20" height="20" fill="white" />
</clipPath>
</defs>
</svg>
);
};
export default EducationIcon;
+20
View File
@@ -0,0 +1,20 @@
const HealthyIcon = () => {
return (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M13 2H7V7H2V13H7V18H13V13H18V7H13V2Z"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="square"
/>
</svg>
);
};
export default HealthyIcon;
+20
View File
@@ -0,0 +1,20 @@
const MallIcon = () => {
return (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7 6H5.5C4.67157 6 4 6.67157 4 7.5V15.5C4 16.3284 4.67157 17 5.5 17H14.5C15.3284 17 16 16.3284 16 15.5V7.5C16 6.67157 15.3284 6 14.5 6H13M7 6V8.5M7 6H13M7 6V5C7 4.60603 7.0776 4.21593 7.22836 3.85195C7.37913 3.48797 7.6001 3.15726 7.87868 2.87868C8.15726 2.6001 8.48797 2.37913 8.85195 2.22836C9.21593 2.0776 9.60603 2 10 2C10.394 2 10.7841 2.0776 11.1481 2.22836C11.512 2.37913 11.8427 2.6001 12.1213 2.87868C12.3999 3.15726 12.6209 3.48797 12.7716 3.85195C12.9224 4.21593 13 4.60603 13 5V6M13 6V8.5"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="square"
/>
</svg>
);
};
export default MallIcon;
+24
View File
@@ -0,0 +1,24 @@
const RestourauntIcon = () => {
return (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M16.5 18.0002V13.0002M16.5 13.0002V2.00018C15.5 1.33351 12.5 5.80018 12.5 13.0002H16.5Z"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="square"
/>
<path
d="M5.75 18V18.75H7.25V18H5.75ZM7.25 2.5C7.25 2.08579 6.91421 1.75 6.5 1.75C6.08579 1.75 5.75 2.08579 5.75 2.5H7.25ZM10.25 2.5C10.25 2.08579 9.91421 1.75 9.5 1.75C9.08579 1.75 8.75 2.08579 8.75 2.5H10.25ZM4.25 2.5C4.25 2.08579 3.91421 1.75 3.5 1.75C3.08579 1.75 2.75 2.08579 2.75 2.5H4.25ZM7.25 18V8H5.75V18H7.25ZM7.25 8V2.5H5.75V8H7.25ZM6.5 8.75H8V7.25H6.5V8.75ZM8 8C8 8.75 8.00135 8.75 8.00271 8.75C8.00317 8.74999 8.00454 8.74999 8.00546 8.74998C8.00731 8.74997 8.00919 8.74995 8.0111 8.74992C8.01492 8.74986 8.01885 8.74978 8.0229 8.74966C8.03101 8.74943 8.03957 8.74906 8.04858 8.74852C8.0666 8.74746 8.0864 8.74574 8.10784 8.74312C8.15075 8.73787 8.20002 8.72905 8.25447 8.71479C8.36425 8.68604 8.49086 8.63628 8.62546 8.55402C8.8977 8.38766 9.16985 8.10903 9.40842 7.67164C9.87327 6.81942 10.25 5.29363 10.25 2.5H8.75C8.75 5.20637 8.37673 6.43058 8.09158 6.95336C7.95515 7.20347 7.8523 7.26859 7.84329 7.2741C7.83727 7.27778 7.84669 7.271 7.87443 7.26373C7.88787 7.26021 7.90491 7.25676 7.92586 7.2542C7.9363 7.25292 7.94768 7.25187 7.96003 7.25114C7.9662 7.25078 7.97261 7.25049 7.97927 7.2503C7.9826 7.2502 7.98599 7.25013 7.98945 7.25008C7.99117 7.25005 7.99292 7.25003 7.99468 7.25002C7.99555 7.25001 7.99689 7.25001 7.99733 7.25C7.99866 7.25 8 7.25 8 8ZM6.5 7.25H5V8.75H6.5V7.25ZM5 8C5 7.25 5.00134 7.25 5.00267 7.25C5.00311 7.25001 5.00445 7.25001 5.00532 7.25002C5.00708 7.25003 5.00883 7.25005 5.01055 7.25008C5.01401 7.25013 5.0174 7.2502 5.02073 7.2503C5.02739 7.25049 5.0338 7.25078 5.03997 7.25114C5.05232 7.25187 5.0637 7.25292 5.07414 7.2542C5.09509 7.25676 5.11213 7.26021 5.12557 7.26373C5.15331 7.271 5.16273 7.27778 5.15671 7.2741C5.1477 7.26859 5.04484 7.20347 4.90842 6.95336C4.62327 6.43058 4.25 5.20637 4.25 2.5H2.75C2.75 5.29363 3.12673 6.81942 3.59158 7.67164C3.83016 8.10903 4.1023 8.38766 4.37454 8.55402C4.50914 8.63628 4.63575 8.68604 4.74553 8.71479C4.79998 8.72905 4.84925 8.73787 4.89216 8.74312C4.9136 8.74574 4.9334 8.74746 4.95142 8.74852C4.96043 8.74906 4.96899 8.74943 4.9771 8.74966C4.98115 8.74978 4.98508 8.74986 4.9889 8.74992C4.99081 8.74995 4.99269 8.74997 4.99454 8.74998C4.99546 8.74999 4.99683 8.74999 4.99729 8.75C4.99865 8.75 5 8.75 5 8ZM5 8.75C5 8.75 5 8 5 7.25C5 7.25 5 7.25 5 8C5 8.75 5 8.75 5 8.75C5 8 5 7.25 5 7.25V8.75ZM8 7.25C8 7.25 8 8 8 8.75C8 8.75 8 8.75 8 8C8 7.25 8 7.25 8 7.25C8 8 8 8.75 8 8.75V7.25Z"
fill="currentColor"
/>
</svg>
);
};
export default RestourauntIcon;
+39
View File
@@ -0,0 +1,39 @@
const SportIcon = () => {
return (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clip-path="url(#clip0_133_6248)">
<path
d="M11.4085 10.6583C11.704 11.1701 11.8957 11.7351 11.9729 12.321C12.05 12.9069 12.011 13.5022 11.858 14.073C11.7051 14.6438 11.4412 15.1789 11.0815 15.6478C10.7217 16.1166 10.2731 16.51 9.76137 16.8055C9.2496 17.1009 8.68463 17.2927 8.09874 17.3698C7.51285 17.447 6.9175 17.408 6.34669 17.255C5.77587 17.1021 5.24078 16.8382 4.77195 16.4784C4.30311 16.1187 3.90973 15.6701 3.61426 15.1583C3.31878 14.6466 3.127 14.0816 3.04987 13.4957C2.97274 12.9098 3.01176 12.3145 3.16471 11.7437C3.31765 11.1728 3.58154 10.6377 3.94128 10.1689C4.30103 9.70009 4.7496 9.3067 5.26137 9.01123C5.77315 8.71576 6.33811 8.52398 6.924 8.44684C7.5099 8.36971 8.10525 8.40873 8.67606 8.56168C9.24687 8.71463 9.78197 8.97851 10.2508 9.33825C10.7196 9.698 11.113 10.1466 11.4085 10.6583L11.4085 10.6583Z"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="square"
/>
<path
d="M5.37646 8.94482L11.0056 5.69482L11.5056 6.56085L14.1037 5.06085L13.6037 4.19482L16.6348 2.44482L18.3848 5.47591L12.1576 13.69"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="square"
/>
<path
d="M3.79597 15.8419L3.24805 16.1582"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="square"
/>
</g>
<defs>
<clipPath id="clip0_133_6248">
<rect width="20" height="20" fill="currentColor" />
</clipPath>
</defs>
</svg>
);
};
export default SportIcon;
+22
View File
@@ -0,0 +1,22 @@
const TramIcon = () => {
return (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M3 13L3.5 19H16.5L17 13M3 13L3.5 5H8M3 13H17M17 13L16.5 5H12M8 5H12M8 5L6 1H14L12 5"
stroke="white"
strokeWidth="1.5"
strokeLinecap="square"
/>
<circle cx="6" cy="16" r="1" fill="white" />
<circle cx="14" cy="16" r="1" fill="white" />
</svg>
);
};
export default TramIcon;
+3 -4
View File
@@ -2,8 +2,7 @@ import ReactDOM from "react-dom/client";
import "./index.css";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import VirtualTourPage from "./pages/VirtualTourPage.tsx";
import Infra2Page from "./pages/Infra2Page.tsx";
import MainPage from "./pages/MainPage.tsx";
import InfrastructurePage from "./pages/InfrastructurePage.tsx";
const router = createBrowserRouter([
{
@@ -15,8 +14,8 @@ const router = createBrowserRouter([
element: <VirtualTourPage />,
},
{
path: "/infra2",
element: <Infra2Page />,
path: "/infrastructure",
element: <InfrastructurePage />,
},
]);
+46
View File
@@ -0,0 +1,46 @@
import { useState } from "react";
import Button from "../components/Button";
import ArrowLeftIcon from "../components/icons/ArrowLeftIcon";
import CrossIcon from "../icons/CrossIcon";
import InfrastructureFilters from "../components/InfrastructurePage/InfrastructureFilters";
import InputRange from "../components/InfrastructurePage/InputRange";
function InfrastructurePage() {
const [selectedRange, setSelectedRange] = useState<number>(800);
const handleOnInputRangeChange = (
event: React.ChangeEvent<HTMLInputElement>
) => setSelectedRange(Number(event.target.value));
return (
<div className="h-screen overflow-hidden">
<div
className="absolute rounded-full h-[85vh] top-[calc(50vh-47.5%)] left-[calc(54.9vw-42.5vh)] border-[3px] bg-black z-50 aspect-square transition-transform duration-300 ease-in-out"
style={{
background: "radial-gradient(transparent 60%, #0079C2 100%)",
transform: `scale(${selectedRange / 800})`,
}}
></div>
<div className="absolute h-full top-0 left-0 z-[99999999] w-[260px] bg-[#002347] bg-opacity-90 select-none px-6 py-5 flex flex-col justify-between gap-8">
<div className="flex flex-col gap-3">
<div className="flex justify-end">
<CrossIcon />
</div>
<InfrastructureFilters />
<InputRange
selectedRange={selectedRange}
handleOnChange={handleOnInputRangeChange}
/>
</div>
<Button text="Генплан" icon={<ArrowLeftIcon />} widthFull />
</div>
<img
src={`/images/Infra/NKS.png`}
alt=""
className={`absolute h-full w-full select-none pointer-events-none opacity-100}`}
/>
</div>
);
}
export default InfrastructurePage;
+2 -2
View File
@@ -1,14 +1,14 @@
import { Canvas } from "@react-three/fiber";
import { Html, OrbitControls } from "@react-three/drei";
import SphereTour from "../components/SphereTour";
import { Suspense, useRef, useState } from "react";
import { OrbitControls as OrbitControlsImpl } from "three-stdlib";
import { useFullscreen } from "ahooks";
import SphereTour from "../components/SphereTour";
import PointButton from "../components/PointButton";
import WalkIcon from "../components/icons/WalkIcon";
import Button from "../components/Button";
import ArrowLeftIcon from "../components/icons/ArrowLeftIcon";
import FullscreenIcon from "../components/icons/FullscreenIcon";
import { useFullscreen } from "ahooks";
import WindowModeIcon from "../components/icons/WindowModeIcon";
import _images from "../images.json";
import Image from "../types/Image";
+7
View File
@@ -0,0 +1,7 @@
interface IInfrastructure {
id: string;
icon: React.ReactNode;
label: string;
}
export type { IInfrastructure };