Skip to content

Commit

Permalink
(#85) 🐻 INAE: μŠ€νƒ€λ””μ›€ μ „μ—­ μƒνƒœ 관리와 λ””ν…ŒμΌ λ””μžμΈ μˆ˜μ • 및 머지
Browse files Browse the repository at this point in the history
(#82) 🎨 design:  λ Œλ”λ§ κ΄€λ ¨ λ””ν…ŒμΌ λ””μžμΈ μˆ˜μ •
Merge pull request #85 from KUSITMS-30th-TEAM-A/design/#82-detailDesignRendering
  • Loading branch information
inaemon authored Nov 30, 2024
2 parents 3aa8edf + 0cc934d commit 6314acf
Show file tree
Hide file tree
Showing 11 changed files with 286 additions and 264 deletions.
66 changes: 12 additions & 54 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/components/dialogs/ImgUrlModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const ImgUrlModal = ({ imageUrl, onClose }: { imageUrl: string, onClose: () => v
<img
src={imageUrl}
alt="ν™•λŒ€ 이미지"
className="max-w-full max-h-full" />
className="max-w-[500px] max-h-full" />
<button
onClick={onClose}
className="absolute top-0 right-0 text-grayscale-50 p-2 rounded-full">
Expand Down
70 changes: 70 additions & 0 deletions src/context/StadiumContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React, { createContext, useEffect, useState, useContext, ReactNode } from "react";
import { StadiumType } from "@/src/constants/ZoneData";
import { ZoneGetResponseType } from "@/src/api/StadiumApiType";

// 1. Context νƒ€μž… μ •μ˜
interface StadiumContextType {
selectedStadium: StadiumType;
setSelectedStadium: (stadium: StadiumType) => void;
selectedSection: string;
setSelectedSection: (section: string) => void;
selectedSectionColor: string;
setSelectedSectionColor: (color: string) => void;
zoneNameList: string[];
setZoneNameList: (zones: string[]) => void;
stadiumInfo: ZoneGetResponseType | undefined;
setStadiumInfo: (info: ZoneGetResponseType | undefined) => void;
}

// 2. κΈ°λ³Έκ°’ μ„€μ •
const StadiumContext = createContext<StadiumContextType | null>(null);

// 3. Context Provider μ»΄ν¬λ„ŒνŠΈ
export const StadiumProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
// ν”„λ‘ νŠΈμ—μ„œ μ„ νƒν•œ μŠ€νƒ€λ””μ›€ κ°’ μ „μ—­ 관리 λ³€μˆ˜
const [selectedStadium, setSelectedStadium] = useState<StadiumType>(StadiumType.JAMSIL);
// νŽ˜μ΄μ§€ 이동 및 μƒˆλ‘­κ²Œ λ Œλ”λ§ν•˜λ”λΌλ„ 기쑴에 μ„ νƒν•œ selectedStadium 값이 μ΄ˆκΈ°ν™”λ˜μ§€ μ•Šκ³  μœ μ§€λ˜λ„λ‘ 둜컬 νžˆμŠ€ν† λ¦¬μ— μ €μž₯
useEffect(() => {
// ν΄λΌμ΄μ–ΈνŠΈ μ‚¬μ΄λ“œμ—μ„œλ§Œ localStorage μ‚¬μš©
if (typeof window !== "undefined") {
const savedStadium = localStorage.getItem('selectedStadium');
if (savedStadium) {
setSelectedStadium(JSON.parse(savedStadium));
}
}
}, []);
// μƒνƒœ μ—…λ°μ΄νŠΈ μ‹œ localStorage에 μ €μž₯
useEffect(() => {
if (typeof window !== "undefined") {
localStorage.setItem('selectedStadium', JSON.stringify(selectedStadium));
}
}, [selectedStadium]);


// ν”„λ‘ νŠΈμ—μ„œ μ„ νƒν•œ μŠ€νƒ€λ””μ›€μ— λ”°λ₯Έ μ’Œμ„(zone) 리슀트 μ „μ—­ 관리 λ³€μˆ˜
const [zoneNameList, setZoneNameList] = useState<string[]>([]);

// ν”„λ‘ νŠΈμ—μ„œ μ„ νƒν•œ μŠ€νƒ€λ””μ›€μ— λ”°λ₯Έ μ’Œμ„(zone) κ°’ μ „μ—­ 관리 λ³€μˆ˜
const [selectedSection, setSelectedSection] = useState<string>("");

// μ’Œμ„ 색상, selectedSectionκ³Ό mapping
const [selectedSectionColor, setSelectedSectionColor] = useState<string>("");

// λ°±μ—”λ“œλ‘œλΆ€ν„° λ°›μ•„μ˜¨ μŠ€νƒ€λ””μ›€ 정보 κ°’ μ „μ—­ 관리 λ³€μˆ˜
const [stadiumInfo, setStadiumInfo] = useState<ZoneGetResponseType>();

return (
<StadiumContext.Provider value={{
selectedStadium, setSelectedStadium,
selectedSection, setSelectedSection,
selectedSectionColor, setSelectedSectionColor,
zoneNameList, setZoneNameList,
stadiumInfo, setStadiumInfo
}}>
{children}
</StadiumContext.Provider>
);
};

// 4. Context μ‚¬μš©μ„ μœ„ν•œ μ»€μŠ€ν…€ ν›…
export const useStadiumContext = () => useContext(StadiumContext);
11 changes: 7 additions & 4 deletions src/hooks/useScroll.ts β†’ src/hooks/useGlobalScroll.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useState, useEffect } from "react";

// μ „μ—­(window) 슀크둀 감지 ν›…
const useScroll = () => {
const useGlobalScroll = () => {
const [scrollDirection, setScrollDirection] = useState<"up" | "down" | null>(
null
);
Expand All @@ -11,22 +11,25 @@ const useScroll = () => {
let prevScrollPos = window.scrollY;

const handleScroll = () => {
// μ „μ—­ 슀크둀 λ°©ν–₯
const currentScrollPos = window.scrollY;

// μ „μ—­ 슀크둀 λ°©ν–₯ μ—…λ°μ΄νŠΈ
if (currentScrollPos > prevScrollPos) {
setScrollDirection("down");
} else if (currentScrollPos < prevScrollPos) {
setScrollDirection("up");
}

// μ „μ—­ 슀크둀 μœ„μΉ˜ μ—…λ°μ΄νŠΈ
setScrollPosition(currentScrollPos);
prevScrollPos = currentScrollPos;
};

// Add scroll event listener
// μ „μ—­ 슀크둀 이벀트 λ¦¬μŠ€λ„ˆ 등둝
window.addEventListener("scroll", handleScroll);

// Cleanup event listener on unmount
// μ»΄ν¬λ„ŒνŠΈκ°€ μ–Έλ§ˆμš΄νŠΈλ  λ•Œ 이벀트 λ¦¬μŠ€λ„ˆ 제거
return () => {
window.removeEventListener("scroll", handleScroll);
};
Expand All @@ -35,4 +38,4 @@ const useScroll = () => {
return { scrollDirection, scrollPosition };
};

export default useScroll;
export default useGlobalScroll;
27 changes: 24 additions & 3 deletions src/hooks/useRefScroll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ import { useState, useEffect, useRef } from "react";
// div에 overflow-autoκ°€ css둜 μ„€μ •λœ 경우 window.scrollYλ‘œλŠ” 감지가 λΆˆκ°€ν•˜μ—¬
// ν•΄λ‹Ή div의 μ°Έμ‘°(ref)λ₯Ό μƒμ„±ν•˜κ³ , κ·Έ 참쑰에 이벀트λ₯Ό 바인딩해야 ν•œλ‹€
const useRefScroll = <T extends HTMLElement>() => {
const [scrollDirection, setScrollDirection] = useState<"up" | "down" | null>(
null
);
const [scrollDirection, setScrollDirection] = useState<"up" | "down" | null> (null);
const [scrollPosition, setScrollPosition] = useState(0);
const containerRef = useRef<T | null>(null);

useEffect(() => {
let prevScrollPos = 0;
let startY: number | null = null;

// λ°μŠ€ν¬νƒ‘ μ „μš©
// overflow-autoκ°€ μ„€μ •λœ divμ—μ„œ heightκ°€ μ§§μ•„μ„œ scroll barκ°€ μƒκΈ°λŠ” κ²½μš°μ— μŠ€ν¬λ‘€μ„ κ°μ§€ν•˜λŠ” 이벀트
const handleScroll = () => {
const container = containerRef.current;
if (!container) return;
Expand All @@ -30,6 +30,26 @@ const useRefScroll = <T extends HTMLElement>() => {
prevScrollPos = currentScrollPos;
};

// λ°μŠ€ν¬νƒ‘ μ „μš©
// heightκ°€ 이미 κΈΈμ•„μ„œ scroll barκ°€ μ•ˆ μƒκΈ°λŠ” κ²½μš°μ— 마우슀 휠둜 μŠ€ν¬λ‘€μ„ κ°μ§€ν•˜λŠ” 이벀트
const handleWheel = (e: WheelEvent) => {
const container = containerRef.current;
if (!container) return;

// 마우슀 휠 슀크둀 λ°©ν–₯ 감지
if (e.deltaY > 0) {
setScrollDirection("down");
} else if (e.deltaY < 0) {
setScrollDirection("up");
}

// wheel μ΄λ²€νŠΈλŠ” scrollTop 값을 직접 λ³€κ²½ν•˜μ§€ μ•ŠμœΌλ―€λ‘œ, 이λ₯Ό μ—…λ°μ΄νŠΈ
setScrollPosition(container.scrollTop);
};


// λͺ¨λ°”일 κΈ°κΈ° μ „μš©
// ν„°μΉ˜λ₯Ό κ°μ§€ν•˜λŠ” 이벀트
const handleTouchStart = (e: TouchEvent) => {
startY = e.touches[0].clientY;
};
Expand All @@ -53,6 +73,7 @@ const useRefScroll = <T extends HTMLElement>() => {
const container = containerRef.current;
if (container) {
container.addEventListener("scroll", handleScroll);
container.addEventListener("wheel", handleWheel); // wheel 이벀트 μΆ”κ°€
container.addEventListener("touchstart", handleTouchStart);
container.addEventListener("touchmove", handleTouchMove);
}
Expand Down
62 changes: 2 additions & 60 deletions src/hooks/useStadiumSelector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,66 +3,8 @@ import { StadiumType } from "@/src/constants/ZoneData";
import { ZoneGetParamsType, ZoneGetResponseType } from "@/src/api/StadiumApiType"; // νƒ€μž…λ“€ import
import { handleGetStadiumInfo } from "@/src/api/StadiumApiHandler"; // API 호좜 ν•¨μˆ˜

export const useStadiumSelector = () => {
const [selectedStadium, setSelectedStadium] = useState<StadiumType>(StadiumType.JAMSIL); // 첫 화면은 μž μ‹€λ‘œ μ΄ˆκΈ°ν™”
const [selectedSection, setSelectedSection] = useState<string>("");
const [selectedSectionColor, setSelectedSectionColor] = useState<string>("");

// zoneName만 μΆ”μΆœ
const [zoneNameList, setZoneNameList] = useState<string[]>([]);

// μŠ€νƒ€λ””μ›€ API 데이터 관리
const [stadiumInfo, setStadiumInfo] = useState<ZoneGetResponseType>();

// μŠ€νƒ€λ””μ›€ λ³€κ²½ μ‹œ, μŠ€νƒ€λ””μ›€ 정보 및 ꡬ역λͺ… 리슀트λ₯Ό κ°€μ Έμ˜΅λ‹ˆλ‹€.
const handleStadiumInfo = async () => {
const params: ZoneGetParamsType = {
stadiumName: selectedStadium as string,
};

const stadiumApiData = await handleGetStadiumInfo(params);
if (stadiumApiData) {
setStadiumInfo(stadiumApiData);
setZoneNameList(stadiumApiData.zones.map(zone => zone.zoneName));

// μ„ νƒλœ κ΅¬μ—­μ˜ 색상 μ—…λ°μ΄νŠΈ
const selectedZoneColor = stadiumApiData.zones.find(zone => zone.zoneName === selectedSection)?.zoneColor;
if (selectedZoneColor) {
setSelectedSectionColor(selectedZoneColor);
}
}
};

// μŠ€νƒ€λ””μ›€ 변경될 λ•Œλ§ˆλ‹€ 호좜
useEffect(() => {
handleStadiumInfo();
}, [selectedStadium, selectedSection]); // selectedStadium λ˜λŠ” selectedSection이 변경될 λ•Œλ§ˆλ‹€ μ‹€ν–‰

// μŠ€νƒ€λ””μ›€ 선택 ν•Έλ“€λŸ¬
const handleStadiumSelect = (stadium: StadiumType) => {
setSelectedStadium(stadium);
setSelectedSection(StadiumType.NONE); // μŠ€νƒ€λ””μ›€μ΄ λ°”λ€Œλ©΄ ꡬ역을 μ΄ˆκΈ°ν™”
};

// ꡬ역 클릭 ν•Έλ“€λŸ¬
const handleSectionClick = (zoneName: string) => {
setSelectedSection(zoneName);
};
import { useStadiumContext } from "@/src/context/StadiumContext";

export const useStadiumSelector = () => {


return {
selectedStadium,
setSelectedStadium,
selectedSection,
setSelectedSection,
selectedSectionColor,
setSelectedSectionColor,
zoneNameList,
setZoneNameList,
handleStadiumSelect,
handleSectionClick,
stadiumInfo,
setStadiumInfo
};
};
Loading

0 comments on commit 6314acf

Please sign in to comment.