diff --git a/src/pages/Timetable/.css.ts b/src/pages/Timetable/.css.ts
index cfac435..b46ade2 100644
--- a/src/pages/Timetable/.css.ts
+++ b/src/pages/Timetable/.css.ts
@@ -17,16 +17,30 @@ export const section = style({
});
const baseFilterButton = style({
+ position: "relative",
padding: "10px 15px",
margin: "0 5px 10px 0",
border: "none",
borderRadius: "20px",
cursor: "pointer",
- transition: "background-color 0.3s ease",
- backgroundColor: "rgba(79, 205, 197, 0.5)",
+ transition: "all 0.3s ease",
+ overflow: "hidden",
+
+ "::before": {
+ content: '""',
+ position: "absolute",
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ backgroundColor: "rgba(255, 255, 255, 0.5)",
+ transition: "background-color 0.3s ease",
+ zIndex: -1,
+ },
+
"@media": {
"screen and (max-width: 768px)": {
- padding: "16px 26px",
+ padding: "8px 12px",
margin: "0 3px 8px 0",
},
},
@@ -37,7 +51,8 @@ export const filterButton = styleVariants({
baseFilterButton,
{
color: vars.color.white,
- ":hover": {
+
+ ":hover::before": {
backgroundColor: "rgba(255, 255, 255, 0.2)",
},
},
@@ -45,26 +60,17 @@ export const filterButton = styleVariants({
active: [
baseFilterButton,
{
+ backgroundImage: "linear-gradient(to top left, #04D1C3, #009efd, #FFFFFF)",
fontWeight: "bold",
- backgroundColor: "rgba(255, 255, 255, 0.3)", // 강조 네모 배경
- color: "transparent", // 텍스트 색상 투명
- backgroundImage: "linear-gradient(to top left, #04D1C3,#009efd, #FFFFFF)", // 텍스트 그라데이션
- WebkitBackgroundClip: "text", // 텍스트에만 그라데이션 적용
- WebkitTextFillColor: "transparent", // 텍스트 색상 채우기 제거
- position: "relative", // 텍스트와 배경 분리
+ WebkitBackgroundClip: "text",
+ backgroundClip: "text",
+ color: "transparent",
+
"::before": {
- // 배경 유지
- content: '""',
- position: "absolute",
- top: 0,
- left: 0,
- right: 0,
- bottom: 0,
- backgroundColor: "rgba(255, 255, 255, 0.3)", // 강조 네모 배경
- borderRadius: "20px", // 배경의 모서리 둥글게
- zIndex: -1, // 배경이 텍스트 뒤로 가게
+ backgroundColor: "rgba(255, 255, 255, 0.5)",
},
- ":hover": {
+
+ ":hover::before": {
backgroundColor: "rgba(255, 255, 255, 0.4)",
},
},
@@ -180,6 +186,7 @@ export const artistImage = style({
export const artistName = style({
fontSize: "1.7rem",
fontFamily: vars.font.pyeongChangLight,
+ wordBreak: "keep-all",
"@media": {
"screen and (max-width: 768px)": {
fontSize: "1.7rem",
diff --git a/src/pages/Timetable/index.tsx b/src/pages/Timetable/index.tsx
index 2d8a65a..0adfc63 100644
--- a/src/pages/Timetable/index.tsx
+++ b/src/pages/Timetable/index.tsx
@@ -1,118 +1,20 @@
-import React, { useEffect, useState, useMemo, useCallback, useRef } from "react";
+import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { timeTableInfo } from "./timeTableInfo.ts";
-import { timeTableFilterProps, timeTableInfoProps } from "../../shared/types/timeTable.ts";
import * as styles from "./.css.ts";
-import { Link } from "react-router-dom";
-import { artistInfoListProps } from "../../shared/types/mainPage.ts";
+import { FilterButton, TimeTableItem } from "./subComponents.tsx";
+import { clearTime, END_DATE, START_DATE, TIME_TABLE_FILTER } from "./utils.tsx";
-// Constants
-const TIME_TABLE_FILTER: timeTableFilterProps[] = [
- { name: "1일차", date: new Date("2024-09-23T00:00:00+09:00") },
- { name: "2일차", date: new Date("2024-09-24T00:00:00+09:00") },
- { name: "3일차", date: new Date("2024-09-25T00:00:00+09:00") },
- { name: "4일차", date: new Date("2024-09-26T00:00:00+09:00") },
-];
-
-const START_DATE = new Date("2024-09-23T00:00:00+09:00");
-const END_DATE = new Date("2024-09-26T00:00:00+09:00");
-
-const clearTime = (date: Date): Date => {
- return new Date(date.getFullYear(), date.getMonth(), date.getDate());
-};
-
-const formatTime = (date: Date): string => {
- return date.toLocaleTimeString("ko-KR", { hour: "2-digit", minute: "2-digit", hour12: false });
-};
-
-const getEventStatus = (
- event: timeTableInfoProps,
- currentTime: Date,
-): "past" | "current" | "future" => {
- let adjustedStartTime = new Date(event.startTime);
-
- // KNU-ARTIST 항목에 대한 시작 시간 조정
- if (event.description.includes("KNU-ARTIST")) {
- if (event.description.includes("댄스")) {
- adjustedStartTime = new Date(adjustedStartTime.getTime() - 5 * 60000);
- } else if (event.description.includes("밴드")) {
- adjustedStartTime = new Date(adjustedStartTime.getTime() - 4 * 60000);
- }
- }
-
- if (currentTime < adjustedStartTime) return "future";
- if (currentTime > event.endTime) return "past";
- return "current";
-};
-
-// Sub-components
-const FilterButton: React.FC<{
- timeTable: timeTableFilterProps;
- onClick: (date: Date) => void;
- isActive: boolean;
-}> = React.memo(({ timeTable, onClick, isActive }) => (
-
-));
-
-const ArtistInfo: React.FC<{
- artist: artistInfoListProps[];
-}> = React.memo(({ artist }) => (
-
-
상세정보
- {artist.map((a: artistInfoListProps, index) => (
-
-
-
{a.name}
-
- ))}
-
-));
-
-const TimeTableItem: React.FC<{
- timeTable: timeTableInfoProps;
- currentTime: Date;
- refCallback: (node: HTMLDivElement | null) => void;
-}> = React.memo(({ timeTable, currentTime, refCallback }) => {
- const status = getEventStatus(timeTable, currentTime);
-
- return (
-
-
{timeTable.title}
- {timeTable.descriptionShow && (
-
- {timeTable.description}
- {timeTable.link && (
-
- {timeTable.link.text}
-
- )}
-
- )}
-
- {timeTable.startTime.getMonth() + 1}/{timeTable.startTime.getDate()} |{" "}
- {formatTime(timeTable.startTime)} ~ {formatTime(timeTable.endTime)}
-
- {timeTable.artist &&
}
- {status === "current" &&
진행 중
}
-
- );
-});
-
-// Main component
+/**
+ * 타임테이블 페이지
+ */
export default function Timetable() {
const [viewTime, setViewTime] = useState(START_DATE);
const [currentTime, setCurrentTime] = useState(new Date());
const lastCurrentEventRef = useRef(null);
+ /**
+ * 현재 날짜에 맞게 기준시간 설정
+ */
useEffect(() => {
const today = clearTime(new Date());
const start = clearTime(START_DATE);
@@ -132,6 +34,11 @@ export default function Timetable() {
return () => clearInterval(timer);
}, []);
+ /**
+ * 필터링된 타임테이블 정보
+ * @see timeTableInfo
+ * @see useMemo
+ */
const filteredTimeTableInfo = useMemo(
() =>
timeTableInfo
@@ -140,23 +47,43 @@ export default function Timetable() {
[viewTime],
);
+ /**
+ * 현재 진행중인 이벤트로 스크롤
+ */
useEffect(() => {
- // 진행 중인 마지막 요소로 스크롤
- if (lastCurrentEventRef.current) {
- if ("scrollIntoView" in lastCurrentEventRef.current) {
- lastCurrentEventRef.current.scrollIntoView({ behavior: "smooth" });
- return;
- }
+ if (
+ lastCurrentEventRef.current &&
+ clearTime(currentTime).getTime() === clearTime(viewTime).getTime()
+ ) {
+ const rootFontSize = parseFloat(getComputedStyle(document.documentElement).fontSize);
+ const offset = rootFontSize * 3.75;
+
+ const elementPosition =
+ lastCurrentEventRef.current!.getBoundingClientRect().top + window.scrollY;
+ const offsetPosition = elementPosition - offset;
+
+ window.scrollTo({
+ top: offsetPosition,
+ behavior: "smooth",
+ });
+ } else {
+ window.scrollTo(0, 0);
}
- window.scrollTo(0, 0);
}, [filteredTimeTableInfo]);
+ /**
+ * 필터 클릭 이벤트
+ */
const handleFilterClick = useCallback((date: Date) => {
setViewTime(clearTime(date));
}, []);
+ /**
+ * 렌더링
+ */
return (
+ {/*상단 필터링 버튼*/}
{TIME_TABLE_FILTER.map((timeTable, index) => (
))}
+ {/*필터링된 타임테이블 정보*/}
{filteredTimeTableInfo.map((timeTable, index) => (
@@ -181,6 +109,7 @@ export default function Timetable() {
))}
+ {/*하단 필터링 버튼*/}
{TIME_TABLE_FILTER.map((timeTable, index) => (
void;
+ isActive: boolean;
+}> = React.memo(({ timeTable, onClick, isActive }) => (
+
+));
+
+/**
+ * 아티스트 정보
+ * @param artist 아티스트 정보
+ * @see artistInfoListProps
+ */
+const ArtistInfo: React.FC<{
+ artist: artistInfoListProps[];
+}> = React.memo(({ artist }) => (
+
+
상세정보
+ {artist.map((a: artistInfoListProps, index) => (
+
+
+
{a.name}
+
+ ))}
+
+));
+
+/**
+ * 타임테이블 아이템
+ * @param timeTable 타임테이블 정보
+ * @param currentTime 현재 시간
+ * @param refCallback 현재 진행중인 이벤트를 가리키기 위한 콜백
+ */
+export const TimeTableItem: React.FC<{
+ timeTable: timeTableInfoProps;
+ currentTime: Date;
+ refCallback: (node: HTMLDivElement | null) => void;
+}> = React.memo(({ timeTable, currentTime, refCallback }) => {
+ const status = getEventStatus(timeTable, currentTime);
+
+ return (
+
+
{timeTable.title}
+ {timeTable.descriptionShow && (
+
+ {timeTable.description}
+ {timeTable.link && (
+
+ {timeTable.link.text}
+
+ )}
+
+ )}
+
+ {timeTable.startTime.getMonth() + 1}/{timeTable.startTime.getDate()} |{" "}
+ {formatTime(timeTable.startTime)} ~ {formatTime(timeTable.endTime)}
+
+ {timeTable.artist &&
}
+ {status === "current" &&
진행 중
}
+
+ );
+});
diff --git a/src/pages/Timetable/timeTableInfo.ts b/src/pages/Timetable/timeTableInfo.ts
index 7d76ec1..aa88aea 100644
--- a/src/pages/Timetable/timeTableInfo.ts
+++ b/src/pages/Timetable/timeTableInfo.ts
@@ -387,8 +387,8 @@ export const timeTableInfo: timeTableInfoProps[] = [
endTime: new Date("2024-09-24 20:14"),
},
{
- title: "재학생 우선 입장",
- description: "재학생 우선 입장 시간",
+ title: "무대 입장(재학생)",
+ description: "팔찌 착용 필요",
descriptionShow: true,
date: new Date("2024-09-23"),
startTime: new Date("2024-09-23 17:00"),
@@ -399,16 +399,16 @@ export const timeTableInfo: timeTableInfoProps[] = [
},
},
{
- title: "전체 입장",
- description: "전체 입장 시간",
- descriptionShow: true,
+ title: "무대 입장(전체)",
+ description: "무대 입장(전체) 시간",
+ descriptionShow: false,
date: new Date("2024-09-23"),
startTime: new Date("2024-09-23 17:50"),
endTime: new Date("2024-09-23 24:00"),
},
{
- title: "재학생 우선 입장",
- description: "재학생 우선 입장 시간",
+ title: "무대 입장(재학생)",
+ description: "팔찌 착용 필요",
descriptionShow: true,
date: new Date("2024-09-24"),
startTime: new Date("2024-09-24 17:00"),
@@ -419,16 +419,16 @@ export const timeTableInfo: timeTableInfoProps[] = [
},
},
{
- title: "전체 입장",
- description: "전체 입장 시간",
- descriptionShow: true,
+ title: "무대 입장(전체)",
+ description: "무대 입장(전체) 시간",
+ descriptionShow: false,
date: new Date("2024-09-24"),
startTime: new Date("2024-09-24 17:50"),
endTime: new Date("2024-09-24 24:00"),
},
{
- title: "재학생 우선 입장",
- description: "재학생 우선 입장 시간",
+ title: "무대 입장(재학생)",
+ description: "팔찌 착용 필요",
descriptionShow: true,
date: new Date("2024-09-25"),
startTime: new Date("2024-09-25 17:00"),
@@ -439,9 +439,9 @@ export const timeTableInfo: timeTableInfoProps[] = [
},
},
{
- title: "전체 입장",
- description: "전체 입장 시간",
- descriptionShow: true,
+ title: "무대 입장(전체)",
+ description: "무대 입장(전체) 시간",
+ descriptionShow: false,
date: new Date("2024-09-25"),
startTime: new Date("2024-09-25 17:50"),
endTime: new Date("2024-09-25 24:00"),
diff --git a/src/pages/Timetable/utils.tsx b/src/pages/Timetable/utils.tsx
new file mode 100644
index 0000000..9b7e298
--- /dev/null
+++ b/src/pages/Timetable/utils.tsx
@@ -0,0 +1,64 @@
+import { timeTableFilterProps, timeTableInfoProps } from "../../shared/types/timeTable.ts";
+
+/**
+ * 일별 필터 버튼
+ * name: 버튼 이름
+ * date: 필터링할 날짜
+ */
+export const TIME_TABLE_FILTER: timeTableFilterProps[] = [
+ { name: "1일차", date: new Date("2024-09-23T00:00:00+09:00") },
+ { name: "2일차", date: new Date("2024-09-24T00:00:00+09:00") },
+ { name: "3일차", date: new Date("2024-09-25T00:00:00+09:00") },
+ { name: "4일차", date: new Date("2024-09-26T00:00:00+09:00") },
+];
+
+/**
+ * 축제 시작일
+ */
+export const START_DATE = new Date("2024-09-23T00:00:00+09:00");
+
+/**
+ * 축제 종료일
+ */
+export const END_DATE = new Date("2024-09-26T00:00:00+09:00");
+
+/**
+ * 시간을 00:00:00으로 설정 (날짜만 유지, 시간 초기화)
+ * @param date 날짜
+ */
+export const clearTime = (date: Date): Date => {
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate());
+};
+
+/**
+ * Date 객체가 들어왔을때 시간을 mm:hh 형식으로 변환
+ * @param date
+ */
+export const formatTime = (date: Date): string => {
+ return date.toLocaleTimeString("ko-KR", { hour: "2-digit", minute: "2-digit", hour12: false });
+};
+
+/**
+ * 이벤트 상태 반환
+ * @param event
+ * @param currentTime past: 지난 이벤트, current: 현재 진행중인 이벤트, future: 미래 이벤트
+ */
+export const getEventStatus = (
+ event: timeTableInfoProps,
+ currentTime: Date,
+): "past" | "current" | "future" => {
+ let adjustedStartTime = new Date(event.startTime);
+
+ // KNU-ARTIST 항목에 대한 시작 시간 조정
+ if (event.description.includes("KNU-ARTIST")) {
+ if (event.description.includes("댄스")) {
+ adjustedStartTime = new Date(adjustedStartTime.getTime() - 5 * 60000);
+ } else if (event.description.includes("밴드")) {
+ adjustedStartTime = new Date(adjustedStartTime.getTime() - 4 * 60000);
+ }
+ }
+
+ if (currentTime < adjustedStartTime) return "future";
+ if (currentTime > event.endTime) return "past";
+ return "current";
+};
diff --git a/src/shared/routing/routerInfo.ts b/src/shared/routing/routerInfo.ts
index 9094ba4..a1a2b63 100644
--- a/src/shared/routing/routerInfo.ts
+++ b/src/shared/routing/routerInfo.ts
@@ -79,7 +79,7 @@ export const routerInfo: routerInfoType[] = [
path: "timetable",
element: lazy(() => import("../../pages/Timetable")),
english: "Timetable",
- korean: "타임테이블",
+ korean: "축제 일정",
expose: true,
mainPage: true,
scrollOptions: "never",