마땅한 라이브러리가 없어 직접 만들어봤다.
아이디어
시간과 구분선은 같은 컴포넌트 내부에 작성하여 미리 시간표 레이아웃을 만들어 두고 그 외 예약 정보는 absolute로 적용한다.
시간표 레이아웃
<TimeList>
{TIMELIST.map((time) => (
<div>
<span key={time}>{time}</span>
<hr />
</div>
))}
</TimeList>
export const TimeList = styled.div`
${flexBox("column", "start", "start")};
color: ${({ theme }) => theme.colors.gray};
gap: 4rem;
width: 100%;
height: 100%;
div {
${flexBox()};
width: 100%;
height: 1rem;
span {
width: 4rem;
}
hr {
flex: 1;
height: 1px;
border: none;
background-color: ${({ theme }) => theme.colors.grayBg};
}
}
`;
시간표 레이아웃의 하단 부에 어느 정도의 여백을 두어 Nav 바와 간격을 가지는 것이 좋다고 생각했다.
그러나 스크롤이 있는 div에는 padding이나 margin이 적용되지 않는다.
→ 대신 마지막 div에 padding 적용하여 공백을 생성했다.
div:nth-last-child(1) {
padding-bottom: 2rem;
}
또는
div:last-child {
padding-bottom: 2rem;
}
※ 위 두가지 가상 선택자는 의미가 같음
예약 정보 구현
const ReserveItem = (item: ReserveInfo, room: number) => {
// 시작 위치
const top = TIMELIST.findIndex((time) => time === item.startTime);
// 높이
const [startHour, startMinute] = item.startTime.split(":").map(Number);
const [endHour, endMinute] = item.endTime.split(":").map(Number);
const diff = (endHour - startHour) * 2 + Number(startMinute !== endMinute);
return (
<TimeTableItem
key={item.startTime}
$room={room}
$top={top}
$space={diff}
$color={GroupColor(item.userGroup)}
>
<span>
{item.startTime} ~ {item.endTime}
</span>
<p>
{item.userGroup} {item.userName} {item.userRank}
</p>
</TimeTableItem>
);
};
- 시작 위치
- TIMELIST 배열에서 startTime의 index를 찾아 위치(top)을 구해준다.
- 높이
- endTime과 startTime의 차이를 이용해서 몇 칸을 차지할 지를 구해준다.
- 한 칸은 30분을 의미하므로, 시간 단위와 분 단위를 나누어 계산했다.
- 시간의 경우 → 차 * 2
- 분의 경우 → 일치 여부
export const TimeTableItem = styled.div<{
$top?: number;
$space?: number;
$room?: number;
$color?: string;
}>`
position: absolute;
width: calc((100% - 8rem) / 3);
height: ${({ $space }) => `calc(5rem * ${$space})`};
left: ${({ $room }) => `calc(6rem + ((100% - 8rem) / 3) * ${$room})`};
top: ${({ $top }) => `calc((1.5rem + 5rem * ${$top}))`};
background-color: ${({ theme, $color }) => theme.teamColors[`${$color}Soft`]};
border-radius: 4px;
padding: 1rem 1.5rem;
border-left: 3px solid ${({ theme, $color }) => theme.teamColors[`${$color}Hard`]};
span {
color: ${({ theme }) => theme.colors.gray};
font-size: 0.8rem;
}
p {
${fontStyle("Button")};
color: ${({ theme, $color }) => theme.teamColors[`${$color}Hard`]};
}
`;
- width
- hr의 길이를 3등분한 것이 각 한 칸의 크기가 된다.
- 부모요소의 너비(100%)에서 양쪽 padding과 span 만큼의 크기를 뺀 너비에서 나누기 3을 해줬다.
- height
- gap(4rem)과 hr이 가지는 너비(0.5rem + 0.5rem) → 5rem
- $space 칸만큼 곱해줬다.
- left
- 회의실 종류에 따라 width 만큼 옆으로 이동시켰다.
- top
- 가장 최초의 공백(padding-top: 1rem + div 크기 절반: 0.5rem ⇒ 1.5rem)
- 한 칸의 크기 5rem
모바일 화면 구분
모바일 화면의 경우 3가지 예약 정보가 모두 들어가기엔 좁다고 판단하여 탭 구분을 통해 나눠줬다.
이때 isSelected 변수와 display: none 속성을 활용했다.
<TimeTableItem
key={item.startTime}
$room={room}
$top={top}
$space={diff}
$color={GroupColor(item.userGroup)}
$isSelected={selectedRoom === room}
>
TimeTableItem에 $isSelected 속성 추가
// 모바일의 경우, 선택된 회의실만 보여줌
@media ${({ theme }) => theme.device.mobile} {
width: calc(100% - 6rem);
left: 5rem;
display: ${({ $isSelected }) => ($isSelected ? "block" : "none")};
}
'React' 카테고리의 다른 글
[ React ] 검색 가능한 SelectBox 컴포넌트 구현 (0) | 2024.07.04 |
---|---|
[ React ] 기본 SelectBox 컴포넌트 구현 (1) | 2024.07.03 |
[ React ] weekSelector 컴포넌트 구현 (0) | 2024.06.21 |
useRef로 영역 외 클릭 시 감지 기능 구현하기 (0) | 2024.06.19 |
[ React ] DatePicker 컴포넌트 직접 만들기 (0) | 2024.06.17 |