From bbc38a11547db32dd42d8beeda964d336f981afc Mon Sep 17 00:00:00 2001
From: dmdgpdi <33450285+dmdgpdi@users.noreply.github.com>
Date: Wed, 31 Jul 2024 15:12:12 +0900
Subject: [PATCH] feat: create Slot Component (#19)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* feat: Slot 컴포넌트 관련 유틸 함수 구현
* refactor: Task type을 공통 type으로 분리
* design: Slot 관련 css 속성 추가
* feat: TimeSlot 컴포넌트 구현
* feat: TimeSlot 컴포넌트 구현
* feat: Slot 컴포넌트 구현
* refactor: calculateTaskOffsetAndHeightPercent 함수 반환값 변경
---
src/timetable/components/Slot.module.scss | 48 +++++++++++++++++++++
src/timetable/components/Slot.tsx | 21 ++++++++++
src/timetable/components/TaskSlot.tsx | 49 ++++++++++++++++++++++
src/timetable/components/TimeSlot.tsx | 17 ++++++++
src/timetable/components/Timetable.type.ts | 10 +++++
src/timetable/utils/index.ts | 43 +++++++++++++++++++
6 files changed, 188 insertions(+)
create mode 100644 src/timetable/components/Slot.module.scss
create mode 100644 src/timetable/components/Slot.tsx
create mode 100644 src/timetable/components/TaskSlot.tsx
create mode 100644 src/timetable/components/TimeSlot.tsx
create mode 100644 src/timetable/components/Timetable.type.ts
create mode 100644 src/timetable/utils/index.ts
diff --git a/src/timetable/components/Slot.module.scss b/src/timetable/components/Slot.module.scss
new file mode 100644
index 00000000..af3dffc9
--- /dev/null
+++ b/src/timetable/components/Slot.module.scss
@@ -0,0 +1,48 @@
+.slot {
+ display: flex;
+ gap: 10px;
+}
+
+.timeSlotLayout {
+ position: relative;
+ flex: 0 1 10%; // flex-grow: 0, flex-shrink: 1, flex-basis: 10%
+ min-width: 50px;
+ max-width: 70px;
+ height: 100%;
+}
+
+.headerDate {
+ position: absolute;
+ top: -0.5em;
+ margin: 0;
+ padding: 0;
+}
+
+.taskSlotLayout {
+ box-sizing: border-box;
+ position: relative;
+ flex: 1 1 90%; // flex-grow: 1, flex-shrink: 1, flex-basis: 90%
+ min-width: 100px;
+ height: 100%;
+}
+
+.taskSlotBackground {
+ position: absolute;
+ width: 100%;
+ z-index: 0;
+}
+
+.taskSlotContent {
+ z-index: 20;
+ position: relative;
+}
+
+.title {
+ font-size: 1.25rem;
+ margin: 20px 0;
+}
+
+.description {
+ font-size: 1rem;
+ margin: 20px 0;
+}
diff --git a/src/timetable/components/Slot.tsx b/src/timetable/components/Slot.tsx
new file mode 100644
index 00000000..2c34165a
--- /dev/null
+++ b/src/timetable/components/Slot.tsx
@@ -0,0 +1,21 @@
+import TimeSlot from '@/timetable/components/TimeSlot';
+import TaskSlot from '@/timetable/components/TaskSlot';
+import type { Task } from './Timetable.type';
+import styled from './Slot.module.scss';
+
+interface SlotProps {
+ headerDate: Date;
+ slotTime: number;
+ taskItem: Task | undefined;
+}
+
+function Slot({ headerDate, slotTime, taskItem }: SlotProps) {
+ return (
+
+
+
+
+ );
+}
+
+export default Slot;
diff --git a/src/timetable/components/TaskSlot.tsx b/src/timetable/components/TaskSlot.tsx
new file mode 100644
index 00000000..1e0ded28
--- /dev/null
+++ b/src/timetable/components/TaskSlot.tsx
@@ -0,0 +1,49 @@
+import { add } from 'date-fns';
+import { calculateTaskOffsetAndHeightPercent } from '@/timetable/utils';
+import styled from './Slot.module.scss';
+import type { Task } from './Timetable.type';
+
+interface TaskSlotProps {
+ headerDate: Date;
+ slotTime: number;
+ taskItem: Task | undefined;
+}
+
+function TaskSlot({ headerDate, slotTime, taskItem }: TaskSlotProps) {
+ if (!taskItem) {
+ return ;
+ }
+
+ const { startTime, endTime, slotColor, title, subTitle } = taskItem;
+
+ const slotStartTime = headerDate;
+ const slotEndTime = add(headerDate, { minutes: slotTime });
+
+ const { offsetPercent, heightPercent } = calculateTaskOffsetAndHeightPercent(
+ slotStartTime,
+ slotEndTime,
+ startTime,
+ endTime,
+ slotTime,
+ );
+
+ return (
+
+ );
+}
+
+export default TaskSlot;
diff --git a/src/timetable/components/TimeSlot.tsx b/src/timetable/components/TimeSlot.tsx
new file mode 100644
index 00000000..2027c87d
--- /dev/null
+++ b/src/timetable/components/TimeSlot.tsx
@@ -0,0 +1,17 @@
+import { getHourAndMinutesFormat } from '@/timetable/utils';
+import styled from './Slot.module.scss';
+
+interface TimeSlotProps {
+ headerDate: Date;
+}
+
+function TimeSlot({ headerDate }: TimeSlotProps) {
+ const currentTime = getHourAndMinutesFormat(headerDate);
+ return (
+
+ );
+}
+
+export default TimeSlot;
diff --git a/src/timetable/components/Timetable.type.ts b/src/timetable/components/Timetable.type.ts
new file mode 100644
index 00000000..4a39ca78
--- /dev/null
+++ b/src/timetable/components/Timetable.type.ts
@@ -0,0 +1,10 @@
+interface Task {
+ id: number;
+ title: string;
+ subTitle: string;
+ slotColor: string;
+ startTime: Date;
+ endTime: Date;
+}
+
+export type { Task };
diff --git a/src/timetable/utils/index.ts b/src/timetable/utils/index.ts
new file mode 100644
index 00000000..e4d32eee
--- /dev/null
+++ b/src/timetable/utils/index.ts
@@ -0,0 +1,43 @@
+const getHourAndMinutesFormat = (data: Date) => {
+ const hours = data.getHours();
+ const minutes = data.getMinutes();
+ const minutesFormat = minutes < 10 ? `0${minutes}` : minutes;
+ const currentTime = minutes === 0 ? hours : `${hours}:${minutesFormat}`;
+
+ return currentTime; // HH:MM
+};
+
+// 시간을 분단위로 바꿔버리고 더해주는 함수
+const sumHoursAndMinutes = (date: Date) => date.getHours() * 60 + date.getMinutes();
+
+const calculateTaskOffsetAndHeightPercent = (
+ slotStartTime: Date,
+ slotEndTime: Date,
+ taskStartTime: Date,
+ taskEndTime: Date,
+ slotTime: number,
+) => {
+ const slotStartMinutes = sumHoursAndMinutes(slotStartTime);
+ const slotEndMinutes = sumHoursAndMinutes(slotEndTime);
+ const taskStartMinutes = sumHoursAndMinutes(taskStartTime);
+ const taskEndMinutes = sumHoursAndMinutes(taskEndTime);
+
+ let offsetPercent = 0;
+ let endPercent = 100;
+
+ if (slotStartMinutes < taskStartMinutes) {
+ // 슬롯의 시작시간보다 task의 시작 시간이 늦었다면(즉 slot 도중에 시작했다면)
+ offsetPercent = ((taskStartMinutes - slotStartMinutes) / slotTime) * 100;
+ }
+
+ if (taskEndMinutes < slotEndMinutes) {
+ // task의 끝나는 시간이 slot의 종료 시간보다 늦다면(즉 slot 도중에 끝난다면)
+ endPercent = ((taskEndMinutes - slotStartMinutes) / slotTime) * 100;
+ }
+
+ const heightPercent = endPercent - offsetPercent;
+
+ return { offsetPercent, heightPercent };
+};
+
+export { getHourAndMinutesFormat, sumHoursAndMinutes, calculateTaskOffsetAndHeightPercent };