๋ชฉ์ฐจ
์์ ๊ฐ์ด ๋ฆฌ์กํธ ์บ๋ฆฐ๋๋ฅผ ์ด์ฉํด์ ๋ ์ง๋ฅผ ๋ฐ์์ค๊ณ ์๊ฐ์ ๋ฐ์ ์ฌ ์ ์๋ค.
ํน์ ๋ ์ง๋ ๋นํ์ฑํ ์ํฌ ์ ์๋ ๊ธฐ๋ฅ๋ ์๋ค.
(์ฐ๋ฆฌ ๊ฐ์ ๊ฒฝ์ฐ์๋ ์์ฝ์ด ๊ฝ์ฐจ๊ฑฐ๋, ํด๋ฌด์ผ์ด๊ฑฐ๋ , ์ง๊ธ์ผ๋ก๋ถํฐ 3๊ฐ์ ๋ด ์ด์ธ์ ๊ฐ์ disabled ๋๋ค.)
1) ๋ฆฌ์กํธ ์บ๋ฆฐ๋ ์ค์น (install)
npm install react-calendar
2) ๋ฆฌ์กํธ ์บ๋ฆฐ๋ ์ฌ์ฉ (import)
import Calendar from 'react-calendar';
import "./Calendar.css";
`react-calendar` ์ `css` import ์์ผ์ฃผ๊ธฐ
3) ๋ ์ง ๊ฐ์ ธ์ค๊ธฐ (Date seleted)
<Calendar
onChange={handleDateChange}
value={date}
onClickDay={handleDateClick}
locale="en-US" // ๋ฏธ๊ตญ ๋ก์ผ์ผ ์ค์ (์ผ์์ผ๋ถํฐ ์์)
next2Label={null}
prev2Label={null}
tileDisabled={tileDisabled}
tileClassName={getTileClass}
formatDay={(locale, date) => moment(date).format('D')}
formatShortWeekday={(locale, date) =>
['์ผ', '์', 'ํ', '์', '๋ชฉ', '๊ธ', 'ํ '][date.getDay()]
}
formatMonthYear={(locale, date) =>
`${moment(date).format('YYYY๋
MMM')}`
}
/>
Calendar ์ปดํฌ๋ํธ์ ์์ ๊ฐ์ด ๊ธฐ๋ณธ Props ์์ฑ๋ค์ ์ฃผ๋ฉด ๋๋๋ฐ
- `value` ์ ํ๋ ๋ ์ง ๋๋ ๋ ์ง ๋ฐฐ์ด์ ์ค์
- `onChange` ๋ ์ง๊ฐ ๋ณ๊ฒฝ๋ ๋ ์คํํ ํจ์
- `onClickDay` ํน์ ๋ ์ง๋ฅผ ํด๋ฆญํ์ ๋ ์คํํ ํจ์
- `locale` ๋ก์ผ์ผ ์ค์ (์: "ko-KR" ํ๋ฉด ํ๊ธ๋ก ํ์๋จ)
locale="en-US" // ๋ฏธ๊ตญ ๋ก์ผ์ผ ์ค์ (์ผ์์ผ๋ถํฐ ์์)
- `next2Label` ๋ค์ 2๋ ์ด๋ ๋ฒํผ (๊ธฐ๋ณธ๊ฐ: », null๋ก ์จ๊ธธ ์ ์์)
- `prev2Label` ์ด์ 2๋ ์ด๋ ๋ฒํผ (๊ธฐ๋ณธ๊ฐ: «, null๋ก ์จ๊ธธ ์ ์์)
- `formatDay` , `formatShortWeekday` , `formatMonthYear`, ` formatYear` ๋ฑ์ผ๋ก ์ปค์คํ ํฌ๋งท์ผ๋ก ํ์ํ ์ ์๋ค.
formatDay={(locale, date) => moment(date).format('D')}
// date ๊ฐ์ฒด๋ฅผ moment.js ํํ๋ก ๋ฐ๊ฟใ
์ 1,2,3 (01,02 x ) ํํ๋ก ํ๊ธฐ
formatShortWeekday={(locale, date) =>
['์ผ', '์', 'ํ', '์', '๋ชฉ', '๊ธ', 'ํ '][date.getDay()]
} // ์์ผ์ ํ๊ตญ์ด๋ก ํ์
formatMonthYear={(locale, date) =>
`${moment(date).format('YYYY๋
MMM')}`
} // ์์ ํ๊ตญ์ด๋ก ํ์
- `tileDisabled`, `tileClassName`, `tileContent` ์ผ๋ก ํน์ ๋ ์ง๋ฅผ ์คํ์ผ๋ง ํ ์ ์๋ค.
tileDisabled={tileDisabled}
tileClassName={getTileClass} // ๋ ์ง ์คํ์ผ๋ง
์ด์ธ์๋
- `onClickMonth` ํน์ ์์ ํด๋ฆญํ์ ๋ ์คํํ ํจ์
- `onClickYear` ํน์ ์ฐ๋๋ฅผ ํด๋ฆญํ์ ๋ ์คํํ ํจ์
- `onActiveStartDateChange` ํ์ฌ ๋ณด์ด๋ ๋ ์ง๊ฐ ๋ณ๊ฒฝ๋ ๋ ์คํํ ํจ์
- `minDate` ์ ํํ ์ ์๋ ์ต์ ๋ ์ง
- `maxDate` ์ ํํ ์ ์๋ ์ต๋ ๋ ์ง
- `view` ๋ชจ๋๋ฅผ ๋ณ๊ฒฝํ ์ ์๋ค. `month`, `year`, `decade`, `century`
- ` selectRange` ๋ค์ค ๋ ์ง ์ ํ๋ ๊ฐ๋ฅ `selectRange={true}`
4) ๋ ์ง ๋ง๊ธฐ (Date disabled)
๋ ์ง๋ฅผ ๋ง๋ ๋ฐฉ๋ฒ์ ๋๊ฐ์ง๊ฐ ์์๋๋ฐ
1. ํ๋๋ ์ค๋๋ก๋ถํฐ 3๊ฐ์ ๋ค์ ๋ ์ง๋ง ๋ณด์ฌ์ฃผ๊ฒ ํ๋ ๋ฐฉ์ → `tileDisabled` ์์ฑ ์ฌ์ฉ
// ๋ ์ง ๋นํ์ฑํ
const tileDisabled = ({ date }) => {
const today = new Date();
const threeMonthsLater = new Date();
threeMonthsLater.setMonth(today.getMonth() + 3); // ์ง๊ธ๋ถํฐ 3๊ฐ์ ๋ค
// ์ค๋๊ณผ 3๊ฐ์ ํ์ ๋ ์ง ์ฌ์ด์ ๋ ์ง๋ ๋นํ์ฑํ ํ์ง ์์
return date < today || date > threeMonthsLater;
};
2. ์์ฝ์ด ๊ฝ์ฐฌ ๊ฒฝ์ฐ disabled ์ํค๋ ๊ฒฝ์ฐ → `tileClassName` ์์ฑ ์ฌ์ฉ
tileClassName={getTileClass}
className ์ ์ถ๊ฐํด์ css ๋จน์ด๊ฒ ํด์ ์์ฝ์ด ๋งํ ๊ฒ ์ฒ๋ผ ํํํจ.
const getTileClass = ({ date }) => {
const formattedDate = date.toLocaleDateString('en-CA'); // 'yyyy-mm-dd' ํ์์ผ๋ก ๋ณํ (๋ก์ปฌ ์๊ฐ ๊ธฐ์ค)
// (... ๊ณ ์ ํด๋ฌด์ ๋ํ ์ฝ๋)
// ์์ฝ ์ ๋ณด ํ์ธ
const slot = dateTime2.find((slot) => slot.reservationSlotDate === formattedDate); // ํด๋น ๋ ์ง์ ์์ฝ ์ ๋ณด ์ฐพ๊ธฐ
if (slot) {
if (slot.slotStatusCount === slot.slotCount) {
return 'reserved'; // ์์ฝ์ด ๋ชจ๋ ์ฐผ๋ค๋ฉด 'reserved'
}
}
return ''; // ์์ฝ์ด ์ฐผ์ง ์๊ฑฐ๋ slot์ด ์์ผ๋ฉด ๋น ๋ฌธ์์ด ๋ฐํ
};
5) ์๊ฐ ๊ฐ์ ธ์ค๊ธฐ (Time Selected)
1. ์์ฝ ๊ฐ๋ฅ (์๊ฐ ์ถ๋ ฅ)
{dateTime.map((slot) => (
!noSlotsMessage ? (
<div key={slot.reservationSlotKey}>
<div className="user-reserve-date-time">
{timeSlots.map((timeSlot, index) => (
<button
key={index}
type="button"
onClick={() => {
handleSlotClick(index); // ์ฌ๋กฏ ์ ํ ๊ด๋ฆฌ
handleSlotClick2(timeSlot.time); // ์ฌ๋กฏ ์๊ฐ ์ถ๋ ฅ
handleSlotClick3(slot.reservationSlotKey);
}}
// ์๋ ์๊ฐ ๊ด๋ จ ์์ฑ์ ์ด์ ์์.. (์๊ฐ ๊ด๋ฆฌ๊น์ง ์์ฐ)
//disabled={disabledTimes.includes(timeSlot.time)} // ๋นํ์ฑํ ์กฐ๊ฑด ์ถ๊ฐ
//style={{
//backgroundColor: selectedSlot === index ? '#fd8517' : 'transparent',
//color: selectedSlot === index ? 'white' : 'black',
//</div></div> opacity: disabledTimes.includes(timeSlot.time) ? 0.5 : 1, // ๋นํ์ฑํ๋ ์ฌ๋กฏ์ ํฌ๋ช
๋ ์กฐ์
//}}
>
{timeSlot.time} {/* ์ฌ๋กฏ ์๊ฐ ํ์ */}
</button>
))}
</div>
</div>
) : (
<div className='msg1'>
์์ฝ ๊ฐ๋ฅํ ์๊ฐ์ด ์์ต๋๋ค. <br /> ๋ค๋ฅธ ๋ ์ง๋ฅผ ์ ํํด์ฃผ์ธ์.
</div>
)
))}
ํ์๋น ์์ฝ์ด ์ด๋ฏธ ๊ฑธ๋ ค์๋ ๊ฒฝ์ฐ ์์ฝ์ด ๋ถ๊ฐํ๋๋ก (1ํ์ = 1์์ฝ) ํ๋ ค๊ณ ํ๋ค.
ํ์ง๋ง, ํน์ ํ์์ ์ฌ๋ฌ ์์ฝ์ ๋ฐ๊ฒํ๊ณ ์์ฝ์ด ๋ชฐ๋ฆฌ๋ ๊ฒฝ์ฐ์๋ ๊ฐ๊ฒ์ฌ์ฅ๋์ด ์ฃผ๋ฌธ์ทจ์๋ก ์ ๋์ํ๋ ์ชฝ์ผ๋ก ๋ณ๊ฒฝํ๊ฒ๋์๋ค.
(์๊ฐ์ ๋ง๋ ๊ธฐ๋ฅ์ ๋บ์ง๋ง ์๋ฌดํผ ๊ตฌํ ํด๋ณธ ๊ฒฐ๊ณผ ์๊ฐ์ ๋ง์ ์๋ ์์)
2. ์์ฝ ๋ถ๊ฐ (์์ฝ ๋ถ๊ฐ ๋ฉ์ธ์ง ๋์)
onClickDay={handleDateClick}
const handleDateClick = (selectedDate) => {
setDate(selectedDate);
const isReserved = getTileClass({ date: selectedDate }) === 'reserved';
setNoSlotsMessage(isReserved); // 'reserved' ๋ ์ง๋ฅผ ํด๋ฆญํ๋ฉด ์์ฝ ๋ถ๊ฐ ๋ฉ์์ง ํ์
if (isReserved) {
console.log('์์ฝ๋ถ๊ฐ');
}
};
์บ๋ฆฐ๋ ์ปดํฌ๋ํธ์ Day Click ์ด๋ฒคํธ์ ํด๋น ํจ์๋ฅผ ๊ฑธ์ด์ค๋ค.