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 ( +
+
+
+

{title}

+

{subTitle}

+
+
+ ); +} + +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 ( +
+

{currentTime}

+
+ ); +} + +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 };