UNPKG

mobile-react-infinite-calendar

Version:

A mobile-optimized infinite scroll calendar component for React

594 lines (500 loc) 17.6 kB
# React Infinite Calendar Mobile 모바일 최적화된 무한 스크롤 캘린더 컴포넌트 (한국 공휴일 지원) A mobile-optimized infinite scroll calendar component for React with Korean holidays support. ## 특징 - 🔄 **무한 스크롤**: 월간 부드러운 무한 스크롤 - 📱 **모바일 최적화**: 터치 친화적 디자인과 반응형 레이아웃 - 🎯 **자동 포커스**: IntersectionObserver를 통한 자동 월 감지 - 📅 **날짜 선택기**: 내장된 연도/월 드롭다운 선택기 - 🎉 **한국 공휴일 지원**: API 키 설정으로 한국 공휴일 표시 - 🚀 **동적 이벤트 로딩**: API 기반 스마트 이벤트 로딩 및 캐싱 - 🎨 **커스터마이징**: 유연한 테마 및 스타일 옵션 - 🌐 **다국어 지원**: 여러 언어 지원 (ko, en, ja, zh, de, fr, it, es) - ⚡ **고성능**: 가상화 렌더링으로 부드러운 성능 - 🔧 **최적화된 상태 관리**: 격리된 로컬 상태로 성능 최적화 - 📦 **TypeScript**: 완벽한 TypeScript 지원 - 🏗️ **모듈러 아키텍처**: 비즈니스 로직과 UI 완전 분리 - 🌐 **브라우저 호환성**: 모던 브라우저 및 Safari 12.1+ 지원 ## 설치 ```bash npm install mobile-react-infinite-calendar ``` ### Peer Dependencies 다음 패키지들이 설치되어 있어야 합니다: ```bash npm install react react-dom date-fns ``` ## 브라우저 호환성 ### ✅ **지원 브라우저** | 브라우저 | 최소 버전 | 비고 | | -------------- | --------- | ----------------- | | **Chrome** | 58+ | 완전 지원 | | **Firefox** | 55+ | 완전 지원 | | **Safari** | 12.1+ | **완전 지원** | | **iOS Safari** | 12.2+ | **모바일 최적화** | | **Edge** | 79+ | 완전 지원 | ### 🔑 **핵심 기능 호환성** - ✅ **IntersectionObserver**: Safari 12.1+ 지원 - ✅ **CSS Grid/Flexbox**: 모든 모던 브라우저 - ✅ **Touch Events**: 모바일 Safari 완전 지원 - ✅ **Date API**: 네이티브 JavaScript Date 사용 ### 📱 **모바일 Safari 최적화** 이 패키지는 모바일 Safari에서 발생할 수 있는 터치 이슈들을 미리 방지합니다: - ✅ **Passive Scroll Events**: 터치 성능 최적화 - ✅ **Bounce Effect 제어**: iOS 스크롤 바운스 효과 처리 - ✅ **Touch Scrolling**: `-webkit-overflow-scrolling: touch` 적용 - ✅ **주소창 동적 높이**: 자동 높이 계산으로 대응 **터치 최적화 기능:** - 🚫 스크롤 바운스 중 무한 로딩 방지 - ⚡ 하드웨어 가속 스크롤 활성화 - 📏 동적 뷰포트 높이 자동 대응 - 🎯 **버튼/셀렉트 터치 최적화**: - 최소 44px 터치 타겟 크기 보장 - `touch-action: manipulation` 으로 더블탭 지연 제거 - 모바일에서 hover 효과 자동 비활성화 - iOS 하이라이트 제거 ## 빠른 시작 ```tsx import { InfiniteCalendar } from "mobile-react-infinite-calendar"; // 가장 간단한 사용법 (한국 공휴일 지원) function App() { return <InfiniteCalendar holidayServiceKey="your_api_key_here" />; } ``` ## 사용 가이드 ### 1. 기본 사용법 ```tsx import { InfiniteCalendar } from "mobile-react-infinite-calendar"; function App() { return ( <InfiniteCalendar holidayServiceKey="your_api_key_here" onDayAction={(date, dayInfo) => { console.log("날짜 클릭:", date.toDateString()); if (dayInfo?.events.length > 0) { console.log(`이벤트 ${dayInfo.events.length}개`); } }} /> ); } ``` ### 2. 이벤트 관리 ```tsx // 간단한 방식 (권장) const events = [ { date: '2024-01-15', title: '팀 미팅', color: '#3b82f6' }, { date: '2024-01-20', title: '프로젝트 검토' // color, id 생략 시 자동 처리 } ] // 원본 데이터를 포함한 이벤트 const eventsWithOriginalData = [ { date: '2024-01-15', title: '팀 미팅', color: '#3b82f6', originalData: { meetingId: 123, location: '회의실 A', participants: ['김철수', '이영희'], agenda: '프로젝트 진행 상황 검토' } } ] <InfiniteCalendar holidayServiceKey="your_api_key_here" events={eventsWithOriginalData} onDayAction={(date, dayInfo) => { // 원본 데이터 접근 const event = dayInfo?.events[0] if (event?.originalData) { console.log('미팅 위치:', event.originalData.location) console.log('참석자:', event.originalData.participants) } console.log(`날짜 클릭: ${date.toDateString()}`) if (dayInfo?.events.length > 0) { console.log(`이벤트 ${dayInfo.events.length}개`) } }} /> // 상세한 방식 (기존 호환성) const detailedEvents = [ { id: '1', title: '팀 미팅', startTime: '2024-01-15T10:00:00', endTime: '2024-01-15T11:00:00', color: '#3b82f6' } ] ``` ### 3. 한국 공휴일 설정 ```tsx // 🔑 API 키 설정 (필수) <InfiniteCalendar holidayServiceKey="your_api_key_here" /> // 커스텀 공휴일 추가 (자동 공휴일 + 커스텀) const customHolidays = [ { name: '회사 창립일', date: '2024-03-15', color: 'green' }, { name: '팀 워크샵', date: '2024-06-20', color: 'purple' }, { name: '프로젝트 마감', date: '2024-12-31', color: 'orange' }, { name: '생일', date: '2024-08-15', color: 'pink' }, { name: '특별 이벤트', date: '2024-10-10', color: '#4ecdc4' } // hex 색상도 가능 ] <InfiniteCalendar holidayServiceKey="your_api_key_here" holidays={customHolidays} /> ``` #### 한국 공휴일 API 키 발급 1. [공공데이터포털](https://www.data.go.kr/) 접속 2. "특일정보" API 검색 및 신청 3. 발급받은 키를 `holidayServiceKey`에 설정 ```tsx // 개발 환경 설정 // .env 파일 REACT_APP_HOLIDAY_API_KEY=your_api_key_here // 사용 <InfiniteCalendar holidayServiceKey={process.env.REACT_APP_HOLIDAY_API_KEY} /> ``` **지원되는 공휴일:** - 🎌 신정, 설날, 추석 등 주요 공휴일 - 🏛️ 어린이날, 현충일, 광복절 등 국가 기념일 - 🌸 부처님오신날 등 종교 기념일 - 📅 대체공휴일 및 임시공휴일 **사용 가능한 색상:** - `'red'` 🔴 (기본값) - 공휴일 - `'green'` 🟢 - 개인 기념일 - `'purple'` 🟣 - 특별 행사 - `'orange'` 🟠 - 마감일, 알림 - `'pink'` 🩷 - 생일, 기념일 - `'yellow'` 🟡 - 일반 이벤트 - `'#hex'` - 커스텀 색상 (예: `'#4ecdc4'`, `'#ff6b6b'`) ### 4. UI 커스터마이징 ```tsx // 헤더 커스터마이징 <InfiniteCalendar holidayServiceKey="your_api_key_here" options={{ header: { show: true, monthTitle: true, todayButton: false, // 오늘 버튼 숨김 weekDays: false, // 요일 헤더 숨김 datePicker: true } }} /> // 높이 설정 <InfiniteCalendar holidayServiceKey="your_api_key_here" options={{ height: 600 // 고정 높이 (px) // height: 'auto' // 자동 높이 (기본값) // height: '100vh' // CSS 단위도 가능 }} /> // 자동 높이 + 하단 네비게이션 고려 <InfiniteCalendar holidayServiceKey="your_api_key_here" options={{ autoHeight: { bottomOffset: 80, // 하단 네비게이션 높이 minHeight: 300 // 최소 높이 } }} /> // 모바일 Safari에서 더 부드러운 스크롤 <InfiniteCalendar holidayServiceKey="your_api_key_here" options={{ height: 'auto', autoHeight: { bottomOffset: 100, // Safari 주소창 고려 topOffset: 60 // 상단 네비게이션 고려 } }} /> // 스타일 커스터마이징 <InfiniteCalendar holidayServiceKey="your_api_key_here" options={{ classNames: { container: 'my-calendar-container', header: 'custom-header bg-gray-100', dayCell: 'hover:bg-blue-50 rounded-lg', todayButton: 'btn-primary' } }} /> // 최소 UI (임베디드용) <InfiniteCalendar holidayServiceKey="your_api_key_here" options={{ header: false, // 전체 헤더 숨김 height: 400 // 고정 높이 권장 }} /> ``` ### 5. 디버그 및 개발 도구 ```tsx // 간단한 디버깅 (ERROR, WARN, INFO만 출력) <InfiniteCalendar holidayServiceKey="your_api_key_here" options={{ debug: true }} /> // 상세한 디버그 설정 <InfiniteCalendar holidayServiceKey="your_api_key_here" options={{ debug: { enabled: true, level: 'info', // 로그 레벨: 'error' | 'warn' | 'info' | 'debug' showPerformance: false // 성능 로그 표시 여부 } }} /> // 개발 환경에서만 활성화 <InfiniteCalendar holidayServiceKey="your_api_key_here" options={{ debug: process.env.NODE_ENV === 'development' }} /> ``` ## 고급 기능 ### 1. 동적 이벤트 로딩 ```tsx // API 기반 동적 이벤트 로딩 const fetchEvents = async (startDate: Date, endDate: Date) => { const response = await fetch(`/api/events?start=${startDate.toISOString()}&end=${endDate.toISOString()}`) return response.json() } <InfiniteCalendar holidayServiceKey="your_api_key_here" dynamicEvents={fetchEvents} dynamicEventMapping={{ id: 'eventId', title: 'eventName', date: 'eventDate', startTime: 'startTime', endTime: 'endTime', color: 'eventColor' }} onDayAction={(date, dayInfo) => { // 동적 이벤트의 원본 데이터 접근 const event = dayInfo?.events[0] if (event?.originalData) { console.log('원본 API 데이터:', event.originalData) // API에서 받은 모든 필드에 접근 가능 } }} onDynamicEventLoad={(startDate, endDate, events) => { console.log(`로드된 이벤트: ${events.length}개 (${startDate.toDateString()} ~ ${endDate.toDateString()})`) }} /> // 커스텀 데이터 변환 <InfiniteCalendar holidayServiceKey="your_api_key_here" dynamicEvents={fetchEvents} dynamicEventTransform={(apiData) => ({ id: apiData.id || Math.random().toString(), title: apiData.subject || 'Untitled', date: apiData.scheduledDate, color: apiData.priority === 'high' ? '#ff4444' : '#4444ff', originalData: apiData // 원본 데이터 보존 })} /> ``` **동적 이벤트 특징:** - 🔄 **자동 로딩**: 스크롤 시 필요한 월의 이벤트만 로드 - 💾 **스마트 캐싱**: 이미 로드된 월은 캐시에서 빠르게 표시 - 📅 **선행 로딩**: 현재 월 기준 ±2개월 미리 로드 - 🎯 **점프 감지**: 데이트피커로 먼 날짜 이동 시 확장 로드 - 🔄 **재시도 로직**: 네트워크 오류 시 자동 재시도 - 📊 **유연한 매핑**: 다양한 API 응답 구조 지원 ### 2. 다국어 지원 ```tsx // 영어 사용자 function EnglishCalendar() { const holidays = [ { name: "Independence Day", date: "2024-07-04", color: "red" }, { name: "Thanksgiving", date: "2024-11-28", color: "orange" }, ]; return ( <InfiniteCalendar locale="en-US" // 로케일 설정 필수 holidays={holidays} // 공휴일 직접 제공 /> ); } // 일본어 사용자 function JapaneseCalendar() { return <InfiniteCalendar locale="ja-JP" holidays={myJapaneseHolidays} />; } ``` **지원 로케일:** - `ko-KR`, `ko` - 한국어 (기본값, 공휴일 지원) - `en-US`, `en` - 영어 - `ja-JP`, `ja` - 일본어 - `zh-CN`, `zh` - 중국어 - `de-DE`, `de` - 독일어 - `fr-FR`, `fr` - 프랑스어 - `it-IT`, `it` - 이탈리아어 - `es-ES`, `es` - 스페인어 ### 3. 고급 훅 사용 ```tsx import { useCalendarComposer } from "su-react-infinite-calendar"; function CustomCalendarControls() { const { activeMonth, setActiveMonth, monthsData, addNextMonth } = useCalendarComposer({ events: [], holidays: [], holidayServiceKey: "your_api_key_here", }); return ( <button onClick={() => setActiveMonth(new Date())}>Go to Today</button> ); } ``` ### 4. 전체 옵션 예시 ```tsx <InfiniteCalendar events={events} holidays={customHolidays} holidayServiceKey="your_api_key_here" dynamicEvents={fetchEvents} onDayAction={(date, dayInfo) => { openEventModal(date, dayInfo?.events); }} options={{ header: { show: true, monthTitle: true, todayButton: true, weekDays: true, datePicker: false, }, classNames: { container: "shadow-lg rounded-xl", dayCell: "custom-day-cell", }, height: "auto", initialDate: new Date(), debug: process.env.NODE_ENV === "development", }} /> ``` ## 타입 정의 ### CalendarEvent ```tsx // 제네릭을 사용한 유연한 이벤트 타입 interface CalendarEvent<T = any> { date: string; // YYYY-MM-DD 형식 (필수) title?: string; // 툴팁이나 클릭 시 표시용 color?: string; // 도트 색상 (기본: 빨강) id?: string; // 고유 식별자 (자동 생성 가능) originalData?: T; // 원본 데이터를 그대로 보관 } // 사용 예시 type MyApiEvent = { eventId: number; eventName: string; startDateTime: string; endDateTime: string; location: string; participants: string[]; }; const events: CalendarEvent<MyApiEvent>[] = [{ date: '2024-01-15', title: '팀 미팅', color: '#1a73e8', originalData: { eventId: 123, eventName: '월간 팀 미팅', startDateTime: '2024-01-15T10:00:00', endDateTime: '2024-01-15T11:30:00', location: '회의실 A', participants: ['김철수', '이영희'] } }]; ``` ### Holiday ```tsx interface Holiday { id?: string; // Optional (auto-generated if not provided) name: string; date: string; // YYYY-MM-DD format color?: string; // Text color for the date (default: 'red') } ``` ### CalendarOptions ```typescript interface CalendarOptions { // Display 옵션 header?: | boolean | { show?: boolean; monthTitle?: boolean; todayButton?: boolean; weekDays?: boolean; datePicker?: boolean; }; // 스타일 커스터마이징 classNames?: { container?: string; header?: string; monthTitle?: string; weekDay?: string; dayCell?: string; todayButton?: string; }; // 높이 설정 height?: number | "auto" | string; autoHeight?: { topOffset?: number; // 상단 여백 (기본: 0) bottomOffset?: number; // 하단 여백 (기본: 20) minHeight?: number; // 최소 높이 (기본: 400) maxHeight?: number; // 최대 높이 (기본: window.innerHeight) }; // 디버그 옵션 debug?: | boolean | { enabled?: boolean; // 로그 활성화/비활성화 (기본: false) level?: "error" | "warn" | "info" | "debug"; // 로그 레벨 (기본: 'error') showPerformance?: boolean; // 성능 로그 표시 (기본: false) }; // 기타 UI 옵션 initialDate?: Date; } ``` ## API Reference ### Props | Prop | Type | Default | Description | | ----------------------- | ---------------------------------------------------------------------------------- | --------- | --------------------------- | | `events` | `CalendarEvent[]` | `[]` | 정적 이벤트 배열 | | `holidays` | `Holiday[]` | `[]` | 커스텀 공휴일 배열 | | `holidayServiceKey` | `string` | - | 공휴일 API 키 (한국만 해당) | | `dynamicEvents` | `(startDate: Date, endDate: Date) => Promise<any[]>` | - | 동적 이벤트 로딩 함수 | | `dynamicEventMapping` | `object` | - | API 응답 필드 매핑 | | `dynamicEventTransform` | `(apiData: any) => CalendarEvent` | - | 커스텀 데이터 변환 함수 | | `onDynamicEventLoad` | `(startDate: Date, endDate: Date, events: CalendarEvent[]) => void` | - | 동적 이벤트 로드 완료 콜백 | | `onDayAction` | `(date: Date, dayInfo?: { events: CalendarEvent[], holidays: Holiday[] }) => void` | - | 날짜 클릭 시 콜백 | | `locale` | `string` | `'ko-KR'` | 로케일 설정 | | `options` | `CalendarOptions` | - | UI/스타일 옵션 | ## License MIT ## Contributing Contributions are welcome! Please feel free to submit a Pull Request.