코어 라이브러리로 개발하기 위해 최소한의 캘린더 뷰를 위한 css 만을 포함하고 있다.
또한, 처음 스타일드 컴포넌트로 구현되어있었으나, 최소한의 css만을 보여주기 위해 굳이 스타일드컴포넌트로 구현할 필요가 없다고 느껴졌다.
왜냐 ? 스타일드 컴포넌트를 사용하는 이유는 props에 따라 렌더링을 쉽게 다르게 보여줄 수 있는데, UI를 제거한 비즈니스로직만 있는 코어라이브러리에서까지 굳이 쓸 필요는 없다 생각하였고,
스타일드 컴포넌트가 CSS를 JavaScript 객체로 관리하고, 라이브러리 코드 자체도 빌드에 포함되기 때문에 빌드 용량이 클 수 밖에 없다.
따라서 최적화를 위해서도 css로 마이그레이션 하는것은 필수적이였다.
function App(props: CalendarProps) {
const { mainColor, subMainColor, onCheckInOutChange } = props;
return (
<ThemeProvider theme={{ mainColor, subMainColor }}>
<GlobalStyle />
<CalendarProvider
calendarProps={props}
onCheckInOutChange={onCheckInOutChange}
>
<Calendar />
</CalendarProvider>
</ThemeProvider>
);
}
export default App;
function App(props: CalendarProps) {
const { onCheckInOutChange } = props;
return (
<>
<CalendarProvider
calendarProps={props}
onCheckInOutChange={onCheckInOutChange}
>
<Calendar />
</CalendarProvider>
</>
);
}
export default App;
그럼에 따라 context의 props도 변경이 되었다.
스타일과 관련된 defaultProps 제거
const defaultProps: CalendarProps = {
startDay: 0,
numMonths: 2,
language: "en",
maximumMonths: 12,
showBookingDatesView: true,
isRectangular: false,
resetStyle: false,
defaultCheckIn: dayjs().add(7, "day"),
defaultCheckOut: dayjs().add(8, "day"),
};
const defaultProps: CalendarProps = {
startDay: 0,
numMonths: 1,
language: "ko",
defaultCheckIn: dayjs().add(7, "day"),
defaultCheckOut: dayjs().add(8, "day"),
};
type DateCellProps = {
date: number;
month: number;
year: number;
isOtherDay: boolean;
lastDayOfMonth: number;
};
const DateCell = ({
date,
month,
year,
isOtherDay,
lastDayOfMonth,
}: DateCellProps) => {
const { bookingDates, today, calendarSettings } = useContext(CalendarContext);
const { isRectangular, resetStyle } = calendarSettings;
const currentDate = dayjs(new Date(year, month - 1, date));
const { handleClickDate } = useHandleClickDate(today);
const currentDateString = currentDate.format(DATE_FORMAT);
const todayDateString = today.format(DATE_FORMAT);
const isAfterLastDay = date > lastDayOfMonth;
const checkInDateString = bookingDates.checkIn?.format(DATE_FORMAT);
const checkOutDateString = bookingDates.checkOut?.format(DATE_FORMAT);
const isSelectedDate =
!isOtherDay &&
(checkInDateString === currentDateString ||
checkOutDateString === currentDateString);
const isWithinRange =
!isOtherDay &&
checkInDateString &&
checkOutDateString &&
checkInDateString < currentDateString &&
currentDateString < checkOutDateString;
return (
<DatesContainer
onClick={
!isAfterLastDay && !isOtherDay
? () => handleClickDate(currentDate)
: undefined
}
>
{isSelectedDate && (
<Highlighting isRectangular={isRectangular} resetStyle={resetStyle} />
)}
{isWithinRange && (
<MiddleHighlighting
isRectangular={isRectangular}
resetStyle={resetStyle}
/>
)}
{currentDateString === todayDateString && (
<TodayDot isHighlighting={isSelectedDate} resetStyle={resetStyle} />
)}
<DateNum
isBeforeToday={currentDateString < todayDateString}
isOtherDay={isOtherDay}
isHighlighting={isSelectedDate}
isRectangular={isRectangular}
resetStyle={resetStyle}
>
{date}
</DateNum>
</DatesContainer>
);
};
export default DateCell;
const centered = css`
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
`;
const DatesContainer = styled.li`
display: flex;
position: relative;
width: calc(100% / 7);
padding: 1rem 0;
text-align: center;
list-style: none;
box-sizing: border-box;
justify-content: center;
align-items: center;
`;
const DateNum = styled.div<{
isHighlighting?: boolean;
isOtherDay: boolean;
isBeforeToday: boolean;
isRectangular?: boolean;
resetStyle?: boolean;
}>`
display: ${(props) => (props.isOtherDay ? "none" : "block")};
color: ${(props) =>
props.resetStyle
? "var(--color-black)"
: props.isBeforeToday
? "var(--color-light-gray)"
: props.isHighlighting
? "var(--color-white)"
: "var(--color-black)"};
&:hover {
::after {
content: "";
display: block;
border: ${(props) =>
props.resetStyle
? "var(--color-white)"
: props.isBeforeToday
? "var(--color-white)"
: "3px solid var(--color-main)"};
border-radius: ${(props) => (props.isRectangular ? "4px" : "50%")};
width: 40px;
height: 40px;
${centered}
}
}
cursor: pointer;
z-index: 10;
`;
const Highlighting = styled.div<{
isRectangular?: boolean;
resetStyle?: boolean;
}>`
border: ${(props) =>
props.resetStyle ? "var(--color-white)" : "3px solid var(--color-main)"};
background-color: ${(props) =>
props.resetStyle ? "var(--color-white)" : "var(--color-main)"};
border-radius: ${(props) => (props.isRectangular ? "4px" : "50%")};
width: 40px;
height: 40px;
${centered}
`;
const MiddleHighlighting = styled.div<{
isRectangular?: boolean;
resetStyle?: boolean;
}>`
width: 40px;
height: 40px;
${centered}
border-radius: ${(props) => (props.isRectangular ? "4px" : "50%")};
background-color: ${(props) =>
props.resetStyle ? "var(--color-white)" : "var(--color-sub-main)"};
`;
const TodayDot = styled.div<{ isHighlighting: boolean; resetStyle?: boolean }>`
background-color: ${(props) =>
props.resetStyle
? "var(--color-white)"
: props.isHighlighting
? "var(--color-white)"
: "var(--color-main)"};
border-radius: 50%;
width: 5px;
height: 5px;
position: absolute;
bottom: 10%;
left: 50%;
transform: translate(-50%, -50%);
`;
type DateCellProps = {
date: number;
month: number;
year: number;
isOtherDay: boolean;
lastDayOfMonth: number;
};
const DateCell = ({
date,
month,
year,
isOtherDay,
lastDayOfMonth,
}: DateCellProps) => {
const { bookingDates, today } = useContext(CalendarContext);
const currentDate = dayjs(new Date(year, month - 1, date));
const isAfterLastDay = date > lastDayOfMonth;
const { handleClickDate } = useHandleClickDate(today);
const currentDateString = currentDate.format(DATE_FORMAT);
const todayDateString = today.format(DATE_FORMAT);
const checkInDateString = bookingDates.checkIn?.format(DATE_FORMAT);
const checkOutDateString = bookingDates.checkOut?.format(DATE_FORMAT);
const isSelectedDate =
!isOtherDay &&
(checkInDateString === currentDateString ||
checkOutDateString === currentDateString);
const isWithinRange =
!isOtherDay &&
checkInDateString &&
checkOutDateString &&
checkInDateString < currentDateString &&
currentDateString < checkOutDateString;
let classNames = "";
if (currentDateString === todayDateString) {
classNames += "today";
}
if (isSelectedDate) {
classNames += "selected";
}
if (isWithinRange) {
classNames += "within-range";
}
if (isOtherDay) {
classNames += "other-day";
}
return (
<li
className={`datecell-container ${classNames}`}
onClick={
!isAfterLastDay && !isOtherDay
? () => handleClickDate(currentDate)
: undefined
}
>
<div className="date-cell">{date}</div>
</li>
);
};
export default DateCell;
기존 스타일드 컴포넌트에 해당하는 부분들을 조건에따라 className을 주입해주는 방식으로 변경
npm i react-check-in-out-calendar-core
import { Calendar } from "react-check-in-out-calendar-core";
import "./styles/calendar.css";
import "./styles/index.css";
function App() {
return <Calendar />;
}
export default App;
css 적용
.datecell-container.today::after {
content: "";
background-color: var(--color-main);
border-radius: 50%;
width: 5px;
height: 5px;
position: absolute;
bottom: 10%;
left: 50%;
transform: translate(-50%, -50%);
}
.datecell-container .date-cell:hover::after {
content: "";
display: block;
border: 3px solid var(--color-main);
border-radius: 50%;
width: 40px;
height: 40px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.datecell-container.selected .date-cell::after {
content: "";
display: block;
border: 3px solid var(--color-main);
border-radius: 50%;
width: 40px;
height: 40px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.datecell-container.within-range .date-cell::after {
/* within-range에 해당하는 스타일 */
content: "";
display: block;
border: 3px solid var(--color-sub-main);
border-radius: 50%;
width: 40px;
height: 40px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.datecell-container.other-day {
visibility: hidden;
}
빌드 용량차이
코어 라이브러리
UI 라이브러리
272kb ->129kb로 대략 50% 의 용량을 줄일 수 있었다!