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
@@ -22,6 +25,22 @@ export default function ExcursionCard({ title, description, imageUrl, city, minP
стоимость: {cost}
{description}
Человек: от {minPeople} до {maxPeople}
-
-
+
+ {
+ createPortal(
+ setIsModalVisible(false)}
+ >
+
+ {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() {
Сортировка: