From a2a503d5d51d1b96f2894b65d3409caa2bd07938 Mon Sep 17 00:00:00 2001 From: inmake Date: Thu, 21 Mar 2024 18:16:42 +0500 Subject: [PATCH] fix --- package.json | 4 + src/app/globals.css | 23 ++- src/app/page.tsx | 7 +- src/app/projects/page.tsx | 2 +- src/components/Histories.tsx | 248 +++++++++++++++++++++++++++ src/components/VideoSliderMobile.tsx | 12 +- src/components/Winners.tsx | 38 ++++ tsconfig.json | 30 +++- yarn.lock | 51 +++++- 9 files changed, 394 insertions(+), 21 deletions(-) create mode 100644 src/components/Histories.tsx create mode 100644 src/components/Winners.tsx diff --git a/package.json b/package.json index 61c685c..e116a0a 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "react-input-mask": "^2.0.4", "react-rangeslider": "^2.2.0", "react-swipeable": "^7.0.1", + "react-transition-group": "^4.4.5", "usehooks-ts": "^3.0.1", "zustand": "^4.5.2" }, @@ -30,6 +31,9 @@ "@types/react-dom": "^18", "@types/react-input-mask": "^3.0.5", "@types/react-rangeslider": "^2.2.7", + "@types/react-swipeable": "^5.2.0", + "@types/react-swipeable-views": "^0.13.5", + "@types/react-transition-group": "^4.4.10", "@typescript-eslint/eslint-plugin": "^7.2.0", "@typescript-eslint/parser": "^7.2.0", "@vitejs/plugin-react-swc": "^3.6.0", diff --git a/src/app/globals.css b/src/app/globals.css index bbb7ee7..0ea3dbc 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -5,10 +5,14 @@ @tailwind components; @tailwind utilities; +:root { + --color: #fff; +} + body { font-family: "Inter", sans-serif; background-color: #14161f; - color: #fff; + color: var(--color); } .font-gilroy { @@ -22,6 +26,10 @@ body { -webkit-text-fill-color: transparent; } +.text-gradient-none { + -webkit-text-fill-color: var(--color); +} + .bg-gradient { background: linear-gradient(87deg, #798fff 15%, #d375ff 100%); } @@ -109,3 +117,16 @@ body { .feedback-field::placeholder { @apply lg:text-base text-sm font-semibold text-[#77787d]; } + +.entering { + opacity: 1; +} +.entered { + opacity: 1; +} +.exiting { + opacity: 0; +} +.exited { + opacity: 0; +} diff --git a/src/app/page.tsx b/src/app/page.tsx index 1a0f0f3..2a2b9c5 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -27,6 +27,7 @@ import VKIcon from "@components/icons/VKIcon"; import YouTubeIcon from "@components/icons/YouTubeIcon"; import TelegramIcon from "@components/icons/TelegramIcon"; import { IProject } from "../types/IProject"; +import Histories from "@components/Histories"; export default function App() { const [selectedVideo, setSelectedVideo] = useState( @@ -56,7 +57,7 @@ export default function App() { return (
-
+
-
+
Продавайте недвижимость @@ -365,6 +366,8 @@ export default function App() {
+ +
diff --git a/src/app/projects/page.tsx b/src/app/projects/page.tsx index 77be681..c080bd3 100644 --- a/src/app/projects/page.tsx +++ b/src/app/projects/page.tsx @@ -40,7 +40,7 @@ function ProjectsPage() {
-
+
{projects.map((project, index) => ( diff --git a/src/components/Histories.tsx b/src/components/Histories.tsx new file mode 100644 index 0000000..e2ef928 --- /dev/null +++ b/src/components/Histories.tsx @@ -0,0 +1,248 @@ +import { createRef, useEffect, useLayoutEffect, useState } from "react"; +import { useSwipeable } from "react-swipeable"; +import ArrowLeftIcon from "./icons/ArrowLeftIcon"; +import ArrowRightIcon from "./icons/ArrowRightIcon"; +import { Transition } from "react-transition-group"; + +const CARD_COUNT = 3; +const videos = [ + { + id: 1, + src: "videos/video.mp4", + title: "Вся информация о жилом комплексе на одном экране", + desc: "Инструмент продаж graff.estate для проекта LIFE RESIDENCE: новый подход к презентации недвижимости", + }, + { + id: 2, + src: "videos/video.mp4", + title: "Говно", + desc: "Жопа", + }, + { + id: 3, + src: "videos/video.mp4", + title: "И в", + desc: "прод", + }, +]; + +const Histories = () => { + const [offset, setOffset] = useState(0); + const [videoRefs, setVideoRefs] = useState< + React.RefObject[] + >([]); + const [videoProgress, setVideoProgress] = useState(0); + const [cardWidth, setCardWidth] = useState(0); + const handlers = useSwipeable({ + onSwipedLeft: handleOnRightMove, + onSwipedRight: handleOnLeftMove, + trackMouse: true, + }); + const [selectedVideo, setSelectedVideo] = useState(0); + const [_document, setDocument] = useState(); + + function handleOnLeftMove(): void { + if (offset < 0) { + setOffset((prev) => prev + 1); + } + } + + function handleOnRightMove(): void { + if (offset > -2) { + setOffset((prev) => prev - 1); + } + } + + useEffect(() => { + setDocument(document); + + setVideoRefs((elRefs) => + Array.from({ length: CARD_COUNT }, (_, index) => index).map( + (_, i) => elRefs[i] || createRef() + ) + ); + }, []); + + useEffect(() => { + if (!_document) return; + + const clientWidth = _document.children[0].clientWidth; + + if (clientWidth >= 1600) { + setCardWidth(420); + } else if (clientWidth >= 1280) { + setCardWidth(379); + } else if (clientWidth >= 640) { + setCardWidth(370); + } else { + setCardWidth(344); + } + }, [_document]); + + useEffect(() => { + if (offset !== 0) { + setSelectedVideo(offset * -1); + } else { + setSelectedVideo(0); + } + }, [offset]); + + useEffect(() => { + videoRefs.forEach((video, index) => { + if (!video.current) return; + if (index === selectedVideo) { + video.current.play(); + } else { + video.current.pause(); + } + }); + }, [videoRefs, selectedVideo]); + + useEffect(() => { + const currentVideoRef = videoRefs[selectedVideo]; + if (!currentVideoRef || !currentVideoRef.current) return; + + const progress = Math.round( + (100 / currentVideoRef.current.duration) * + currentVideoRef.current.currentTime + ); + setVideoProgress(progress); + + const interval = setInterval(() => { + if (!currentVideoRef || !currentVideoRef.current) return; + const progress = + (100 / currentVideoRef.current.duration) * + currentVideoRef.current.currentTime; + + setVideoProgress(progress); + if (progress === 100) { + clearInterval(interval); + } + }, 1000); + + return () => clearInterval(interval); + }, [selectedVideo, videoRefs]); + + return ( +
+
+
+
+
+

+

+ Истории +

+

graff.estate

+

+ + {videos.map((item, index) => ( + + {(state) => ( +
+

+ {item.title} +

+

+ {item.desc} +

+
+ )} +
+ ))} +
+
+ + +
+
+
+
+
+
+ {videos.map((video, index) => ( +
+
+ ))} +
+
+
+
+

+ Вся информация о жилом комплексе на одном экране +

+

+ Инструмент продаж graff.estate для проекта LIFE RESIDENCE: новый + подход к презентации недвижимости +

+
+
+
+
+
+ {videos.map((video, index) => ( +
+ ))} +
+
+
+
+ ); +}; + +export default Histories; diff --git a/src/components/VideoSliderMobile.tsx b/src/components/VideoSliderMobile.tsx index 7ade613..d3af0fc 100644 --- a/src/components/VideoSliderMobile.tsx +++ b/src/components/VideoSliderMobile.tsx @@ -63,15 +63,9 @@ function VideoSliderMobile() { const [activeIndex, setActiveIndex] = useState(0); const videoRefs = items.map(() => useRef(null)); const handlers = useSwipeable({ - onSwiped: (e) => { - if (e.dir === "Left") { - handleClickNext(); - } - - if (e.dir === "Right") { - handleClickPrev(); - } - }, + trackMouse: true, + onSwipedLeft: handleClickNext, + onSwipedRight: handleClickPrev, }); function handleClickPrev() { diff --git a/src/components/Winners.tsx b/src/components/Winners.tsx new file mode 100644 index 0000000..c0b6638 --- /dev/null +++ b/src/components/Winners.tsx @@ -0,0 +1,38 @@ +import InsertArrowIcon from "../icons/InsertArrowIcon"; + +const Winners = () => { + return ( +
+
+ awards +
+
+
+

+

+ Победители
BuildUP 2023 +

+ в номинации IT +

+

+ В 2023 году наш продукт для застройщиков graff.estate был признан + лучшим в категории IT на Акселераторе технологических стартапов от + лидеров в строительстве и девелопменте Build UP от Фонда «Сколково» +

+
+ +
+
+ ); +}; + +export default Winners; diff --git a/tsconfig.json b/tsconfig.json index ca775ff..b595baf 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,10 @@ { "compilerOptions": { - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -18,12 +22,26 @@ } ], "paths": { - "@components/*": ["./src/components/*"], + "@components/*": [ + "./src/components/*" + ], // "@types/*": ["./src/types/*"], - "@utils/*": ["./src/utils/*"], - "@stores/*": ["./src/stores/*"], + "@utils/*": [ + "./src/utils/*" + ], + "@stores/*": [ + "./src/stores/*" + ] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + "dist/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] } diff --git a/yarn.lock b/yarn.lock index f3d3d1f..eb764af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19,6 +19,13 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.1.tgz#431f9a794d173b53720e69a6464abc6f0e2a5c57" + integrity sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ== + dependencies: + regenerator-runtime "^0.14.0" + "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -333,6 +340,27 @@ dependencies: "@types/react" "*" +"@types/react-swipeable-views@^0.13.5": + version "0.13.5" + resolved "https://registry.yarnpkg.com/@types/react-swipeable-views/-/react-swipeable-views-0.13.5.tgz#f9dc947b7424d34cade864185bfa831a818a45f6" + integrity sha512-ni6WjO7gBq2xB2Y/ZiRdQOgjGOxIik5ow2s7xKieDq8DxsXTdV46jJslSBVK2yoIJHf6mG3uqNTwxwgzbXRRzg== + dependencies: + "@types/react" "*" + +"@types/react-swipeable@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@types/react-swipeable/-/react-swipeable-5.2.0.tgz#69d6088819f0c03b13eb3fd98bfe995ef311c105" + integrity sha512-aQMubLpV45W8fTQufnm5j8yxYVEp/d3JJkqpPr9xcRPQ6Q6MSJUdNpsaR2uogILSIFzrAisC8AqdR1JlvjuZMA== + dependencies: + react-swipeable "*" + +"@types/react-transition-group@^4.4.10": + version "4.4.10" + resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.10.tgz#6ee71127bdab1f18f11ad8fb3322c6da27c327ac" + integrity sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q== + dependencies: + "@types/react" "*" + "@types/react@*", "@types/react@^18": version "18.2.66" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.66.tgz#d2eafc8c4e70939c5432221adb23d32d76bfe451" @@ -975,6 +1003,14 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dom-helpers@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" + integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== + dependencies: + "@babel/runtime" "^7.8.7" + csstype "^3.0.2" + eastasianwidth@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" @@ -2404,7 +2440,7 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prop-types@^15.8.1: +prop-types@^15.6.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -2464,11 +2500,21 @@ react-rangeslider@^2.2.0: classnames "^2.2.3" resize-observer-polyfill "^1.4.2" -react-swipeable@^7.0.1: +react-swipeable@*, react-swipeable@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/react-swipeable/-/react-swipeable-7.0.1.tgz#cd299f5986c5e4a7ee979839658c228f660e1e0c" integrity sha512-RKB17JdQzvECfnVj9yDZsiYn3vH0eyva/ZbrCZXZR0qp66PBRhtg4F9yJcJTWYT5Adadi+x4NoG53BxKHwIYLQ== +react-transition-group@^4.4.5: + version "4.4.5" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" + integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== + dependencies: + "@babel/runtime" "^7.5.5" + dom-helpers "^5.0.1" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react@^18: version "18.2.0" resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" @@ -2673,6 +2719,7 @@ streamsearch@^1.1.0: integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== "string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0: + name string-width-cjs version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==