From c48658b56056d8279765aec29ac0fa9a56fb79ab Mon Sep 17 00:00:00 2001 From: lootboxer <> Date: Wed, 2 Jul 2025 17:16:33 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20?= =?UTF-8?q?=D0=BC=D0=BE=D0=B4=D0=B0=D0=BB=D0=BA=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/home/ExcursionCard.module.scss | 7 +++ src/components/home/ExcursionCard.tsx | 25 ++++++++-- src/components/home/Filters.tsx | 6 +-- src/components/home/Listing.tsx | 49 +++++++------------ src/components/ui/Modal/Modal.module.scss | 7 +-- src/pages/Home.tsx | 4 +- src/services/apiService.ts | 14 ++++-- 7 files changed, 67 insertions(+), 45 deletions(-) diff --git a/src/components/home/ExcursionCard.module.scss b/src/components/home/ExcursionCard.module.scss index 3928551..6b72870 100644 --- a/src/components/home/ExcursionCard.module.scss +++ b/src/components/home/ExcursionCard.module.scss @@ -32,3 +32,10 @@ -webkit-box-orient: vertical; overflow: hidden; } + +.modalCardAttributes { + display: flex; + gap: 8px; + justify-content: center; + font-weight: 700; +} diff --git a/src/components/home/ExcursionCard.tsx b/src/components/home/ExcursionCard.tsx index 85d4e51..f931d00 100644 --- a/src/components/home/ExcursionCard.tsx +++ b/src/components/home/ExcursionCard.tsx @@ -1,6 +1,8 @@ import styles from '@/components/home/ExcursionCard.module.scss' import Button from '@/components/ui/Button/Button'; -import type { Ref } from 'react' +import { useState, type Ref } from 'react' +import { createPortal } from 'react-dom'; +import { Modal } from '../ui/Modal/Modal'; interface ExcursionCardProps { title: string; @@ -14,6 +16,7 @@ interface ExcursionCardProps { } export default function ExcursionCard({ title, description, imageUrl, city, minPeople, maxPeople, cost, ref }: ExcursionCardProps) { + const [isModalVisible, setIsModalVisible] = useState(false) return
{title}
@@ -22,6 +25,22 @@ export default function ExcursionCard({ title, description, imageUrl, city, minP стоимость: {cost} {description} Человек: от {minPeople} до {maxPeople} - -
+ + { + createPortal( + setIsModalVisible(false)} + > +
{title}
+

{title}

+
+ город: {city} + стоимость: {cost} + Человек: от {minPeople} до {maxPeople} +
+
{description}
+
, document.body) + } + + }; diff --git a/src/components/home/Filters.tsx b/src/components/home/Filters.tsx index d8ca02d..27f15c7 100644 --- a/src/components/home/Filters.tsx +++ b/src/components/home/Filters.tsx @@ -12,8 +12,8 @@ interface IFiltersProps { export default function Filters({ onChangeFilter }: IFiltersProps) { const [filter, setFilter] = useState>({}) - const handleRangeChange = (range: { min: number | ''; max: number | '' }) => { - console.log('Выбранный диапазон:', range); + const handleRangeChange = (range: { min: number | 0; max: number | 0 }) => { + setFilter({ ...filter, minCost: +range.min, maxCost: +range.max }) }; return
setFilter({ ...filter, city: e.target.value })} /> - +e.target.value} placeholder='10' /> + setFilter({ ...filter, countPeople: +e.target.value })} placeholder='10' />
diff --git a/src/components/home/Listing.tsx b/src/components/home/Listing.tsx index 4a078fc..4904438 100644 --- a/src/components/home/Listing.tsx +++ b/src/components/home/Listing.tsx @@ -1,7 +1,7 @@ import styles from '@/components/home/Listing.module.scss'; import ExcursionCard from '@/components/home/ExcursionCard' import type { IExcursionCard } from '@/types'; -import { useEffect, useRef, useState, useCallback } from 'react'; +import { useEffect, useRef, useState, useCallback, type ReactNode, forwardRef, useImperativeHandle } from 'react'; import type { IExcursionsFilter } from '@/types'; import ApiService from '@/services/apiService'; import Loader from '@/components/ui/Loader/Loader'; @@ -13,26 +13,19 @@ interface IListingProps { isPriceSortAsc?: boolean; } -const Listing = (props: IListingProps) => { +const Listing = forwardRef((props: IListingProps, ref) => { const offset = useRef(0) - const cardRefs = useRef([]) - const lastCardIndex = useRef(null) + const lastCardRef = useRef(null) const [isLoading, setIsLoading] = useState(false) const [excursions, setExcursions] = useState([]) - - const setCardRef = useCallback((el: HTMLDivElement, index: number) => { - cardRefs.current[index] = el; - }, []); - const apiService = new ApiService() - - const fetchExcursions = async (newOffset: number, filter?: Partial) => { + const fetchExcursions = async () => { setIsLoading(true) try { - const newExcursions = await (apiService.getExcursions({ limit: LIMIT, offset: newOffset, filter, isPriceSortAsc: props.isPriceSortAsc })) + const newExcursions = await (apiService.getExcursions({ limit: LIMIT, offset: offset.current, filter: props.filter, isPriceSortAsc: props.isPriceSortAsc })) setExcursions((prev) => [...prev, ...newExcursions]) - offset.current += LIMIT + offset.current = offset.current + LIMIT } finally { setIsLoading(false) } @@ -40,34 +33,30 @@ const Listing = (props: IListingProps) => { useEffect(() => { offset.current = 0 - lastCardIndex.current = null + lastCardRef.current = null setExcursions([]) - fetchExcursions(offset.current) + fetchExcursions() }, [props.filter, props.isPriceSortAsc]) useEffect(() => { - if (!excursions.length) { - return - } const observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting && !isLoading) { - fetchExcursions(offset.current, props.filter) + fetchExcursions() } }) - }, { threshold: .8 }) + }, { threshold: .9 }) - if (lastCardIndex.current !== null) { - observer.unobserve(cardRefs.current[lastCardIndex.current]); + if (!excursions.length) { + return } - if (cardRefs.current.length) { - lastCardIndex.current = cardRefs.current.length - 1; - const lastEl = cardRefs.current[lastCardIndex.current]; - if (lastEl instanceof Element) { - observer.observe(lastEl); - } + + if (lastCardRef.current !== null) { + observer.unobserve(lastCardRef.current) } + observer.observe(lastCardRef.current); + return () => observer.disconnect(); }, [excursions]) @@ -81,7 +70,7 @@ const Listing = (props: IListingProps) => { setCardRef(el, index)} + ref={(el) => { lastCardRef.current = index === excursions.length - 1 ? el : null }} minPeople={excursion.minCountPeople} maxPeople={excursion.maxCountPeople} /> @@ -95,7 +84,7 @@ const Listing = (props: IListingProps) => { } ); -} +}) export default Listing; diff --git a/src/components/ui/Modal/Modal.module.scss b/src/components/ui/Modal/Modal.module.scss index 8f3ba20..95148c9 100644 --- a/src/components/ui/Modal/Modal.module.scss +++ b/src/components/ui/Modal/Modal.module.scss @@ -15,9 +15,10 @@ background: white; padding: 20px; border-radius: 8px; - max-width: 500px; - width: 100%; + max-width: 60%; + height: 100%; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + overflow: scroll; } .modalHeader { @@ -35,6 +36,6 @@ } .modalContent { + height: 100%; margin-bottom: 20px; } - diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 9f6b296..441424c 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -9,7 +9,7 @@ export default function Home() { const [filter, setFilter] = useState({}) const [isPriceSortOrderAsc, setIsPriceSortOrderAsc] = useState(true) function changeFiltersHandle(filter: Partial) { - setFilter(filter) + setFilter({ ...filter }) // Использую деструктуризацию для вызова перерисовки при нажатии на энтер } return
@@ -21,7 +21,7 @@ export default function Home() {
Сортировка: