parent
							
								
									3f36c618b4
								
							
						
					
					
						commit
						89f68c5a50
					
				
				 22 changed files with 3157 additions and 8 deletions
			
			
		| @ -0,0 +1,242 @@ | ||||
| import React, { useState, useRef, useEffect } from 'react'; | ||||
| 
 | ||||
| import { | ||||
|   getDateAccordingToMonth, | ||||
|   shallowClone, | ||||
|   getValueType, | ||||
| } from './shared/generalUtils'; | ||||
| import { | ||||
|   TYPE_SINGLE_DATE, | ||||
|   TYPE_RANGE, | ||||
|   TYPE_MUTLI_DATE, | ||||
| } from './shared/constants'; | ||||
| import { useLocaleUtils, useLocaleLanguage } from './shared/hooks'; | ||||
| 
 | ||||
| import { Header, MonthSelector, YearSelector, DaysList } from './components'; | ||||
| 
 | ||||
| export const Calendar = ({ | ||||
|   value, | ||||
|   onChange, | ||||
|   onDisabledDayError, | ||||
|   calendarClassName, | ||||
|   calendarTodayClassName, | ||||
|   calendarSelectedDayClassName, | ||||
|   calendarRangeStartClassName, | ||||
|   calendarRangeBetweenClassName, | ||||
|   calendarRangeEndClassName, | ||||
|   disabledDays, | ||||
|   colorPrimary, | ||||
|   colorPrimaryLight, | ||||
|   slideAnimationDuration, | ||||
|   minimumDate, | ||||
|   maximumDate, | ||||
|   selectorStartingYear, | ||||
|   selectorEndingYear, | ||||
|   locale, | ||||
|   shouldHighlightWeekends, | ||||
|   renderFooter, | ||||
|   customDaysClassName, | ||||
|   events, | ||||
| }) => { | ||||
|   const calendarElement = useRef(null); | ||||
|   const [mainState, setMainState] = useState({ | ||||
|     activeDate: null, | ||||
|     monthChangeDirection: '', | ||||
|     isMonthSelectorOpen: false, | ||||
|     isYearSelectorOpen: false, | ||||
|   }); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     const handleKeyUp = ({ key }) => { | ||||
|       /* istanbul ignore else */ | ||||
|       if (key === 'Tab') | ||||
|         calendarElement.current.classList.remove('-noFocusOutline'); | ||||
|     }; | ||||
|     calendarElement.current.addEventListener('keyup', handleKeyUp, false); | ||||
|     return () => { | ||||
|       // calendarElement.current.removeEventListener('keyup', handleKeyUp, false);
 | ||||
|     }; | ||||
|   }); | ||||
| 
 | ||||
|   const { getToday } = useLocaleUtils(locale); | ||||
|   const { weekDays: weekDaysList, isRtl } = useLocaleLanguage(locale); | ||||
|   const today = getToday(); | ||||
| 
 | ||||
|   const createStateToggler = (property) => () => { | ||||
|     setMainState({ ...mainState, [property]: !mainState[property] }); | ||||
|   }; | ||||
| 
 | ||||
|   const toggleMonthSelector = createStateToggler('isMonthSelectorOpen'); | ||||
|   const toggleYearSelector = createStateToggler('isYearSelectorOpen'); | ||||
| 
 | ||||
|   const getComputedActiveDate = () => { | ||||
|     const valueType = getValueType(value); | ||||
|     if (valueType === TYPE_MUTLI_DATE && value.length) | ||||
|       return shallowClone(value[0]); | ||||
|     if (valueType === TYPE_SINGLE_DATE && value) return shallowClone(value); | ||||
|     if (valueType === TYPE_RANGE && value.from) return shallowClone(value.from); | ||||
|     return shallowClone(today); | ||||
|   }; | ||||
| 
 | ||||
|   const activeDate = mainState.activeDate | ||||
|     ? shallowClone(mainState.activeDate) | ||||
|     : getComputedActiveDate(); | ||||
| 
 | ||||
|   const weekdays = weekDaysList.map((weekDay) => ( | ||||
|     <abbr key={weekDay.name} title={weekDay.name} className="Calendar__weekDay"> | ||||
|       {weekDay.short} | ||||
|     </abbr> | ||||
|   )); | ||||
| 
 | ||||
|   const handleMonthChange = (direction) => { | ||||
|     setMainState({ | ||||
|       ...mainState, | ||||
|       monthChangeDirection: direction, | ||||
|     }); | ||||
|   }; | ||||
| 
 | ||||
|   const updateDate = () => { | ||||
|     setMainState({ | ||||
|       ...mainState, | ||||
|       activeDate: getDateAccordingToMonth( | ||||
|         activeDate, | ||||
|         mainState.monthChangeDirection | ||||
|       ), | ||||
|       monthChangeDirection: '', | ||||
|     }); | ||||
|   }; | ||||
| 
 | ||||
|   const selectMonth = (newMonthNumber) => { | ||||
|     setMainState({ | ||||
|       ...mainState, | ||||
|       activeDate: { ...activeDate, month: newMonthNumber }, | ||||
|       isMonthSelectorOpen: false, | ||||
|     }); | ||||
|   }; | ||||
| 
 | ||||
|   const selectYear = (year) => { | ||||
|     setMainState({ | ||||
|       ...mainState, | ||||
|       activeDate: { ...activeDate, year }, | ||||
|       isYearSelectorOpen: false, | ||||
|     }); | ||||
|   }; | ||||
| 
 | ||||
|   return ( | ||||
|     <div | ||||
|       className={`Calendar -noFocusOutline ${calendarClassName} -${isRtl ? 'rtl' : 'ltr' | ||||
|         }`}
 | ||||
|       role="grid" | ||||
|       style={{ | ||||
|         '--cl-color-primary': colorPrimary, | ||||
|         '--cl-color-primary-light': colorPrimaryLight, | ||||
|         '--animation-duration': slideAnimationDuration, | ||||
|       }} | ||||
|       ref={calendarElement} | ||||
|     > | ||||
|       <Header | ||||
|         maximumDate={maximumDate} | ||||
|         minimumDate={minimumDate} | ||||
|         activeDate={activeDate} | ||||
|         onMonthChange={handleMonthChange} | ||||
|         onMonthSelect={toggleMonthSelector} | ||||
|         onYearSelect={toggleYearSelector} | ||||
|         monthChangeDirection={mainState.monthChangeDirection} | ||||
|         isMonthSelectorOpen={mainState.isMonthSelectorOpen} | ||||
|         isYearSelectorOpen={mainState.isYearSelectorOpen} | ||||
|         locale={locale} | ||||
|       /> | ||||
| 
 | ||||
|       <MonthSelector | ||||
|         isOpen={mainState.isMonthSelectorOpen} | ||||
|         activeDate={activeDate} | ||||
|         onMonthSelect={selectMonth} | ||||
|         maximumDate={maximumDate} | ||||
|         minimumDate={minimumDate} | ||||
|         locale={locale} | ||||
|       /> | ||||
| 
 | ||||
|       <YearSelector | ||||
|         isOpen={mainState.isYearSelectorOpen} | ||||
|         activeDate={activeDate} | ||||
|         onYearSelect={selectYear} | ||||
|         selectorStartingYear={selectorStartingYear} | ||||
|         selectorEndingYear={selectorEndingYear} | ||||
|         maximumDate={maximumDate} | ||||
|         minimumDate={minimumDate} | ||||
|         locale={locale} | ||||
|       /> | ||||
| 
 | ||||
|       <div className="Calendar__weekDays">{weekdays}</div> | ||||
| 
 | ||||
|       <DaysList | ||||
|         activeDate={activeDate} | ||||
|         value={value} | ||||
|         monthChangeDirection={mainState.monthChangeDirection} | ||||
|         onSlideChange={updateDate} | ||||
|         disabledDays={disabledDays} | ||||
|         onDisabledDayError={onDisabledDayError} | ||||
|         minimumDate={minimumDate} | ||||
|         maximumDate={maximumDate} | ||||
|         onChange={onChange} | ||||
|         calendarTodayClassName={calendarTodayClassName} | ||||
|         calendarSelectedDayClassName={calendarSelectedDayClassName} | ||||
|         calendarRangeStartClassName={calendarRangeStartClassName} | ||||
|         calendarRangeEndClassName={calendarRangeEndClassName} | ||||
|         calendarRangeBetweenClassName={calendarRangeBetweenClassName} | ||||
|         locale={locale} | ||||
|         shouldHighlightWeekends={shouldHighlightWeekends} | ||||
|         customDaysClassName={customDaysClassName} | ||||
|         events={events} | ||||
|         isQuickSelectorOpen={ | ||||
|           mainState.isYearSelectorOpen || mainState.isMonthSelectorOpen | ||||
|         } | ||||
|       /> | ||||
|       <div className="Calendar__footer">{renderFooter()}</div> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
| 
 | ||||
| Calendar.defaultProps = { | ||||
|   minimumDate: null, | ||||
|   maximumDate: null, | ||||
|   colorPrimary: '#0eca2d', | ||||
|   colorPrimaryLight: '#cff4d5', | ||||
|   slideAnimationDuration: '0.4s', | ||||
|   calendarClassName: '', | ||||
|   locale: 'en', | ||||
|   value: null, | ||||
|   renderFooter: () => null, | ||||
|   customDaysClassName: [], | ||||
|   events: [ | ||||
|     { | ||||
|       id: 1, | ||||
|       day: 15, | ||||
|       occasions: [ | ||||
|         { | ||||
|           title: 'دی به دین روز، چهارمین جشن دیگان', | ||||
|           dotColor: '#eeffds1', | ||||
|         }, | ||||
|         { | ||||
|           title: 'lorem', | ||||
|           dotColor: 'blue' | ||||
|         } | ||||
|       ] | ||||
| 
 | ||||
|     }, | ||||
|     { | ||||
|       id: 2, | ||||
|       day: 22, | ||||
|       title: 'مصاحبه کاری', | ||||
|       dotColor: 'red', | ||||
|     }, | ||||
|     { | ||||
|       id: 3, | ||||
|       title: 'اجاره خانه', | ||||
|       day: 21, | ||||
|       dotColor: 'blue', | ||||
|     }, | ||||
|   ], | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| @ -0,0 +1,672 @@ | ||||
| .DatePicker { | ||||
|   position: relative; | ||||
|   display: inline-block; | ||||
|   z-index: 100; | ||||
| } | ||||
| 
 | ||||
| .DatePicker__input { | ||||
|   background: #fff; | ||||
|   border: 1px solid #ddd; | ||||
|   padding: 0.4em 0.8em; | ||||
|   font-family: inherit; | ||||
|   text-align: center; | ||||
|   font-size: 12px; | ||||
| } | ||||
| 
 | ||||
| .DatePicker__input.-rtl { | ||||
|   direction: rtl; | ||||
| } | ||||
| 
 | ||||
| .DatePicker__input::placeholder { | ||||
|   color: #979797; | ||||
| } | ||||
| 
 | ||||
| .DatePicker__calendarContainer.-top + .DatePicker__calendarArrow { | ||||
|   top: auto; | ||||
|   bottom: calc(100% + 10px); | ||||
|   transform: translateY(-2.5rem) rotate(180deg); | ||||
|   animation: fadeArrowFlipped 0.3s forwards; | ||||
| } | ||||
| 
 | ||||
| .DatePicker__calendarContainer { | ||||
|   position: absolute; | ||||
|   top: calc(100% + 20px); | ||||
|   left: 50%; | ||||
|   transform: translateX(-50%); | ||||
| } | ||||
| 
 | ||||
| .DatePicker__calendarContainer.-top { | ||||
|   top: auto; | ||||
|   bottom: calc(100% + 20px); | ||||
| } | ||||
| 
 | ||||
| .Calendar, | ||||
| .Calendar * { | ||||
|   margin: 0; | ||||
|   padding: 0; | ||||
|   box-sizing: border-box; | ||||
|   direction: ltr; | ||||
| } | ||||
| 
 | ||||
| .Calendar, | ||||
| .Calendar.-rtl * { | ||||
|   direction: rtl; | ||||
| } | ||||
| 
 | ||||
| .DatePicker__calendarArrow { | ||||
|   position: absolute; | ||||
|   width: 0; | ||||
|   height: 0; | ||||
|   top: calc(100% + 10px); | ||||
|   left: 0; | ||||
|   right: 0; | ||||
|   margin: 0 auto; | ||||
|   border-style: solid; | ||||
|   z-index: 10; | ||||
|   border-width: 0 10px 10px 10px; | ||||
|   border-color: transparent transparent #fff transparent; | ||||
| } | ||||
| 
 | ||||
| .Calendar { | ||||
|   --cl-color-black: #444444; | ||||
|   --cl-color-disabled: #d4d4d4; | ||||
|   --cl-color-error: #ff2929; | ||||
|   font-size: 10px; | ||||
|   background: #fff; | ||||
|   box-shadow: 0 1em 4em rgba(0, 0, 0, 0.07); | ||||
|   border-radius: 1em; | ||||
|   position: relative; | ||||
|   user-select: none; | ||||
|   padding-top: 1.2em; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   width: 33em; | ||||
|   z-index: 10; | ||||
|   max-width: 90vw; | ||||
|   min-height: 36.7em; | ||||
| } | ||||
| 
 | ||||
| .DatePicker .Calendar, | ||||
| .DatePicker__calendarArrow { | ||||
|   transform: translateY(2.5em); | ||||
|   opacity: 0; | ||||
|   animation: fadeCalendar 0.3s forwards; | ||||
| } | ||||
| 
 | ||||
| .DatePicker__calendarContainer.-top .Calendar { | ||||
|   transform: translateY(-2.5em); | ||||
| } | ||||
| 
 | ||||
| .Calendar.-noFocusOutline *:focus { | ||||
|   outline: none !important; | ||||
| } | ||||
| 
 | ||||
| .Calendar > :not(.Calendar__footer) button { | ||||
|   font-family: inherit; | ||||
|   background: transparent; | ||||
|   cursor: pointer; | ||||
|   -webkit-tap-highlight-color: transparent; | ||||
|   outline: none; | ||||
| } | ||||
| 
 | ||||
| .Calendar__header { | ||||
|   display: flex; | ||||
|   color: var(--cl-color-black); | ||||
|   padding: 2em 2.9em; | ||||
|   align-items: center; | ||||
|   overflow: hidden; | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthArrowWrapper { | ||||
|   line-height: 0; | ||||
|   font-size: 1em; | ||||
|   padding: 3px; | ||||
|   position: relative; | ||||
|   border: none; | ||||
|   z-index: 1; | ||||
|   opacity: 1; | ||||
|   transition: 0.2s; | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthArrowWrapper:focus { | ||||
|   outline: 1px dashed rgba(0, 0, 0, 0.4); | ||||
|   outline-offset: 2px; | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthArrowWrapper:disabled, | ||||
| .Calendar__monthArrowWrapper.-hidden { | ||||
|   opacity: 0; | ||||
|   pointer-events: none; | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthArrowWrapper.-left { | ||||
|   transform: rotate(90deg); | ||||
| } | ||||
| .Calendar.-rtl .Calendar__monthArrowWrapper.-left { | ||||
|   transform: rotate(-90deg); | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthArrowWrapper.-right { | ||||
|   transform: rotate(-90deg); | ||||
| } | ||||
| .Calendar.-rtl .Calendar__monthArrowWrapper.-right { | ||||
|   transform: rotate(90deg); | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthArrowWrapper:active .Calendar__monthArrow { | ||||
|   transform: scale(0.7); | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthArrow { | ||||
|   border-radius: 50%; | ||||
|   transition: var(--animation-duration) transform; | ||||
|   pointer-events: none; | ||||
|   background-repeat: no-repeat; | ||||
|   display: block; | ||||
|   width: 1.7em; | ||||
|   height: 1.7em; | ||||
|   background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cg class='nc-icon-wrapper' fill='%23000000'%3E%3Cdefs stroke='none'%3E%3C/defs%3E%3Cpath class='cls-1' d='M12 23.25V.75' fill='none' stroke='%23000000' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5px'%3E%3C/path%3E%3Cpath class='cls-2' d='M22.5 11.25L12 .75 1.5 11.25' fill='none' stroke='%23000000' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5px' fill-rule='evenodd'%3E%3C/path%3E%3C/g%3E%3C/svg%3E"); | ||||
|   background-size: 100% 100%; | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthYearContainer { | ||||
|   flex: 1; | ||||
|   position: relative; | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthYear { | ||||
|   font-size: 1.6em; | ||||
|   font-weight: 500; | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   position: absolute; | ||||
|   top: 0; | ||||
|   bottom: 0; | ||||
|   left: 50%; | ||||
|   will-change: transform, opacity; | ||||
|   backface-visibility: hidden; | ||||
|   transform: translateZ(0); | ||||
|   transition: var(--animation-duration); | ||||
|   line-height: 1; | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthYear.-hiddenNext { | ||||
|   opacity: 0; | ||||
|   transform: translateX(50%); | ||||
| } | ||||
| 
 | ||||
| .Calendar.-rtl .Calendar__monthYear.-hiddenNext { | ||||
|   transform: translateX(-150%); | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthYear.-hiddenPrevious { | ||||
|   opacity: 0; | ||||
|   transform: translateX(-150%); | ||||
| } | ||||
| 
 | ||||
| .Calendar.-rtl .Calendar__monthYear.-hiddenPrevious { | ||||
|   transform: translateX(50%); | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthYear.-shown { | ||||
|   opacity: 1; | ||||
|   margin-top: auto; | ||||
|   margin-bottom: auto; | ||||
|   transform: translateX(-50%); | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthYear.-shownAnimated { | ||||
|   animation: var(--animation-duration) fadeTextToCenter forwards; | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthYear > * { | ||||
|   padding: 0.2em 0.5em; | ||||
|   border: 1px solid transparent; | ||||
|   transition: var(--animation-duration); | ||||
|   font-size: 1.05em; | ||||
|   display: flex; | ||||
|   justify-content: center; | ||||
|   align-items: center; | ||||
|   transform: translateX(0) scale(0.95); | ||||
|   will-change: transform; | ||||
|   border-radius: 5px; | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthYear:not(.-shown) > *, | ||||
| .Calendar__monthYear > *.-hidden { | ||||
|   cursor: default; | ||||
|   pointer-events: none; | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthText { | ||||
|   margin-left: -0.3em; | ||||
| } | ||||
| .Calendar__yearText:last-child { | ||||
|   margin-right: -0.3em; | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthYear.-shown > *:hover, | ||||
| .Calendar:not(.-noFocusOutline) .Calendar__monthYear.-shown > *:focus, | ||||
| .Calendar__monthYear > *.-activeBackground { | ||||
|   background: #f5f5f5; | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthText:hover { | ||||
|   transform: translateX(-0.2em) scale(0.95); | ||||
| } | ||||
| .Calendar.-rtl .Calendar__monthText:hover { | ||||
|   transform: translateX(0.2em) scale(0.95); | ||||
| } | ||||
| 
 | ||||
| .Calendar__yearText:hover { | ||||
|   transform: translateX(0.2em) scale(0.95); | ||||
| } | ||||
| .Calendar.-rtl .Calendar__yearText:hover { | ||||
|   transform: translateX(-0.2em) scale(0.95); | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthYear .Calendar__yearText.-hidden { | ||||
|   transform: translateX(50%); | ||||
|   opacity: 0; | ||||
| } | ||||
| 
 | ||||
| .Calendar.-rtl .Calendar__monthYear .Calendar__yearText.-hidden { | ||||
|   transform: translateX(-50%); | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthYear .Calendar__monthText.-hidden { | ||||
|   transform: translateX(-50%); | ||||
|   opacity: 0; | ||||
| } | ||||
| 
 | ||||
| .Calendar.-rtl .Calendar__monthYear .Calendar__monthText.-hidden { | ||||
|   transform: translateX(50%); | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthYear:not(.-shown) > * { | ||||
|   pointer-events: none; | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthSelectorAnimationWrapper, | ||||
| .Calendar__yearSelectorAnimationWrapper { | ||||
|   position: absolute; | ||||
|   width: 100%; | ||||
|   height: 80%; | ||||
|   bottom: 0; | ||||
|   display: flex; | ||||
|   justify-content: center; | ||||
|   align-items: center; | ||||
|   overflow: hidden; | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthSelectorWrapper { | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
|   display: flex; | ||||
|   justify-content: center; | ||||
|   align-items: center; | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthSelector { | ||||
|   padding: 0 2.5em; | ||||
|   align-content: center; | ||||
|   padding-bottom: 2em; | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthSelector, | ||||
| .Calendar__yearSelector { | ||||
|   display: flex; | ||||
|   flex-wrap: wrap; | ||||
|   position: relative; | ||||
|   z-index: 2; | ||||
|   background-color: #fff; | ||||
|   transform: translateY(-150%); | ||||
|   will-change: transform; | ||||
|   transition: 0.6s; | ||||
|   height: 100%; | ||||
| } | ||||
| 
 | ||||
| .Calendar__yearSelectorWrapper { | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
| } | ||||
| 
 | ||||
| .Calendar__yearSelectorWrapper::after, | ||||
| .Calendar__yearSelectorWrapper::before { | ||||
|   content: ''; | ||||
|   width: 100%; | ||||
|   height: 5em; | ||||
|   position: absolute; | ||||
|   left: 0; | ||||
|   opacity: 0; | ||||
|   transition: 0.4s; | ||||
|   transition-delay: 0.2s; | ||||
| } | ||||
| 
 | ||||
| .Calendar__yearSelectorWrapper::after { | ||||
|   background-image: linear-gradient(to bottom, #fff, #fff 10%, rgba(245, 245, 245, 0)); | ||||
|   top: -0.1em; | ||||
| } | ||||
| 
 | ||||
| .Calendar__yearSelectorWrapper::before { | ||||
|   background-image: linear-gradient(to top, #fff, #fff 10%, rgba(245, 245, 245, 0)); | ||||
|   bottom: 0; | ||||
| } | ||||
| 
 | ||||
| .Calendar__yearSelectorWrapper.-faded::after, | ||||
| .Calendar__yearSelectorWrapper.-faded::before { | ||||
|   opacity: 1; | ||||
|   z-index: 3; | ||||
| } | ||||
| 
 | ||||
| .Calendar__yearSelector { | ||||
|   align-content: flex-start; | ||||
|   scrollbar-width: 0; | ||||
|   overflow: scroll; | ||||
|   position: relative; | ||||
|   width: 100%; | ||||
|   padding: 5em 2em; | ||||
|   -ms-overflow-style: none; | ||||
| } | ||||
| 
 | ||||
| .Calendar__yearSelector::-webkit-scrollbar { | ||||
|   display: none; | ||||
| } | ||||
| 
 | ||||
| .Calendar__yearSelectorItem { | ||||
|   width: 25%; | ||||
|   display: flex; | ||||
|   justify-content: center; | ||||
| } | ||||
| 
 | ||||
| .Calendar__yearSelectorItem:not(:nth-child(-n + 4)) { | ||||
|   margin-top: 1.5em; | ||||
| } | ||||
| 
 | ||||
| .Calendar__yearSelectorText { | ||||
|   border: none; | ||||
|   font-size: 1.4em; | ||||
|   min-width: 85%; | ||||
|   padding: 0.2em 0.5em; | ||||
|   border-radius: 8.5px; | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthSelector.-open, | ||||
| .Calendar__yearSelector.-open { | ||||
|   transform: translateY(0); | ||||
| } | ||||
| 
 | ||||
| .Calendar__yearSelectorText:focus, | ||||
| .Calendar__monthSelectorItemText:focus { | ||||
|   outline: 1px dashed rgba(0, 0, 0, 0.4); | ||||
|   outline-offset: 2px; | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthSelectorItem { | ||||
|   width: calc(100% / 3); | ||||
|   display: flex; | ||||
|   justify-content: center; | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthSelectorItem:not(:nth-child(-n + 3)) { | ||||
|   margin-top: 2em; | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthSelectorItemText { | ||||
|   border: none; | ||||
|   padding: 0.4em 0.4em; | ||||
|   border-radius: 8.5px; | ||||
|   font-size: 1.3em; | ||||
|   min-width: 70%; | ||||
|   transition: 0.3s; | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthSelectorItem:not(.-active) .Calendar__monthSelectorItemText:not(:disabled):hover, | ||||
| .Calendar__yearSelectorItem:not(.-active) .Calendar__yearSelectorText:not(:disabled):hover { | ||||
|   background: #f5f5f5; | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthSelectorItemText:disabled, | ||||
| .Calendar__yearSelectorText:disabled { | ||||
|   opacity: 0.5; | ||||
|   cursor: default; | ||||
| } | ||||
| 
 | ||||
| .Calendar__monthSelectorItem.-active .Calendar__monthSelectorItemText, | ||||
| .Calendar__yearSelectorItem.-active .Calendar__yearSelectorText { | ||||
|   background-color: var(--cl-color-primary); | ||||
|   color: #fff; | ||||
| } | ||||
| 
 | ||||
| .Calendar__weekDays { | ||||
|   display: flex; | ||||
|   justify-content: space-between; | ||||
|   color: var(--cl-color-disabled); | ||||
|   font-size: 1.2em; | ||||
|   margin-bottom: 0.7em; | ||||
|   padding: 0 2.6em; | ||||
|   position: relative; | ||||
| } | ||||
| 
 | ||||
| .Calendar__weekDay { | ||||
|   display: block; | ||||
|   width: calc(100% / 7); | ||||
|   text-align: center; | ||||
|   text-decoration: none; | ||||
| } | ||||
| 
 | ||||
| .Calendar__sectionWrapper { | ||||
|   position: relative; | ||||
|   min-height: 25.8em; | ||||
|   overflow: hidden; | ||||
| } | ||||
| 
 | ||||
| .Calendar__section { | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   padding: 0 3.2em; | ||||
|   position: absolute; | ||||
|   color: var(--cl-color-black); | ||||
|   top: 0; | ||||
|   padding-top: 0.5em; | ||||
|   left: 0; | ||||
|   width: 100%; | ||||
|   will-change: transform, opacity; | ||||
|   transform: translateZ(0); | ||||
|   backface-visibility: hidden; | ||||
|   transition: var(--animation-duration); | ||||
| } | ||||
| 
 | ||||
| .Calendar__section.-hiddenPrevious { | ||||
|   opacity: 0.5; | ||||
|   transform: translateX(-90%); | ||||
| } | ||||
| 
 | ||||
| .Calendar.-rtl .Calendar__section.-hiddenPrevious { | ||||
|   transform: translateX(90%); | ||||
| } | ||||
| 
 | ||||
| .Calendar__section.-hiddenNext { | ||||
|   opacity: 0.5; | ||||
|   transform: translateX(90%); | ||||
| } | ||||
| 
 | ||||
| .Calendar.-rtl .Calendar__section.-hiddenNext { | ||||
|   transform: translateX(-90%); | ||||
| } | ||||
| 
 | ||||
| .Calendar__section.-shown { | ||||
|   opacity: 1; | ||||
|   transform: translateX(0); | ||||
| } | ||||
| 
 | ||||
| .Calendar__section.-shownAnimated { | ||||
|   animation: var(--animation-duration) FadeContentToCenter forwards; | ||||
| } | ||||
| 
 | ||||
| .Calendar__weekRow { | ||||
|   display: flex; | ||||
|   width: 100%; | ||||
| } | ||||
| 
 | ||||
| .Calendar__day { | ||||
|   display: block; | ||||
|   width: calc(100% / 7); | ||||
|   text-align: center; | ||||
|   padding: calc(0.25em - 1px) 0; | ||||
|   font-size: 1.6em; | ||||
|   border-radius: 50%; | ||||
|   transition: 0.2s; | ||||
|   border: 1px solid transparent; | ||||
|   margin-bottom: 0.3em; | ||||
|   color: rgba(0, 0, 0, 0.8); | ||||
|   display: flex; | ||||
|   justify-content: center; | ||||
|   align-items: center; | ||||
|   cursor: pointer; | ||||
| } | ||||
| 
 | ||||
| .Calendar__day:focus { | ||||
|   outline: 1px dashed rgba(0, 0, 0, 0.4); | ||||
|   outline-offset: 2px; | ||||
| } | ||||
| 
 | ||||
| .Calendar__day.-ltr { | ||||
|   min-height: 2.6em; | ||||
|   font-size: 1.45em; | ||||
| } | ||||
| 
 | ||||
| .Calendar__day.-rtl { | ||||
|   font-size: 1.55em; | ||||
|   height: 2.45em; | ||||
| } | ||||
| 
 | ||||
| .Calendar__day:not(.-blank):not(.-selectedStart):not(.-selectedEnd):not(.-selectedBetween):not(.-selected):hover { | ||||
|   background: #eaeaea; | ||||
|   border-radius: 50%; | ||||
|   color: var(--cl-color-black); | ||||
|   border-color: transparent; | ||||
| } | ||||
| 
 | ||||
| .Calendar__day.-selected, | ||||
| .Calendar__day.-selectedStart, | ||||
| .Calendar__day.-selectedEnd { | ||||
|   background: var(--cl-color-primary); | ||||
|   color: #fff; | ||||
| } | ||||
| 
 | ||||
| .Calendar__day.-ltr.-selectedStart { | ||||
|   border-radius: 0; | ||||
|   border-top-left-radius: 100em; | ||||
|   border-bottom-left-radius: 100em; | ||||
| } | ||||
| 
 | ||||
| .Calendar__day.-rtl.-selectedStart { | ||||
|   border-radius: 0; | ||||
|   border-top-right-radius: 100em; | ||||
|   border-bottom-right-radius: 100em; | ||||
| } | ||||
| 
 | ||||
| .Calendar__day.-selectedBetween { | ||||
|   background: var(--cl-color-primary-light); | ||||
|   color: var(--cl-color-primary); | ||||
|   border-radius: 0; | ||||
| } | ||||
| 
 | ||||
| .Calendar__day.-ltr.-selectedEnd { | ||||
|   border-top-right-radius: 100em; | ||||
|   border-bottom-right-radius: 100em; | ||||
| } | ||||
| 
 | ||||
| .Calendar__day.-rtl.-selectedEnd { | ||||
|   border-top-left-radius: 100em; | ||||
|   border-bottom-left-radius: 100em; | ||||
| } | ||||
| 
 | ||||
| .Calendar__day.-weekend:not(.-selected):not(.-blank):not(.-selectedStart):not(.-selectedEnd):not(.-selectedBetween) { | ||||
|   color: var(--cl-color-error); | ||||
| } | ||||
| 
 | ||||
| .Calendar__day.-weekend.-today:not(.-selectedStart):not(.-selectedEnd):not(.-selectedBetween)::after { | ||||
|   background: var(--cl-color-error); | ||||
| } | ||||
| 
 | ||||
| .Calendar__day.-disabled { | ||||
|   color: var(--cl-color-disabled) !important; | ||||
|   background: transparent !important; | ||||
|   cursor: default !important; | ||||
| } | ||||
| .Calendar__day.-selected { | ||||
|   border-radius: 50%; | ||||
| } | ||||
| .Calendar__day.-today:not(.-selectedStart):not(.-selectedEnd):not(.-selectedBetween) { | ||||
|   font-weight: 600; | ||||
|   color: var(--cl-color-black); | ||||
|   color: #000; | ||||
|   position: relative; | ||||
| } | ||||
| 
 | ||||
| .Calendar__day.-today:not(.-selectedStart):not(.-selectedEnd):not(.-selectedBetween)::after { | ||||
|   content: ''; | ||||
|   position: absolute; | ||||
|   bottom: 0.2em; | ||||
|   display: block; | ||||
|   width: 0.6em; | ||||
|   height: 1px; | ||||
|   background: #000; | ||||
|   left: 50%; | ||||
|   opacity: 0.5; | ||||
|   transform: translateX(-50%); | ||||
|   transition: 0.2s; | ||||
| } | ||||
| 
 | ||||
| .Calendar__day.-today:hover:not(.-selectedStart):not(.-selectedEnd):not(.-selectedBetween)::after { | ||||
|   opacity: 0; | ||||
| } | ||||
| 
 | ||||
| .Calendar__day.-blank { | ||||
|   color: transparent; | ||||
|   cursor: default; | ||||
|   pointer-events: none; | ||||
| } | ||||
| 
 | ||||
| .Calendar__footer { | ||||
|   position: relative; | ||||
|   z-index: 1; | ||||
| } | ||||
| 
 | ||||
| @keyframes fadeCalendar { | ||||
|   from { | ||||
|     opacity: 0; | ||||
|   } | ||||
| 
 | ||||
|   to { | ||||
|     opacity: 1; | ||||
|     transform: translateY(0); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @keyframes fadeArrowFlipped { | ||||
|   from { | ||||
|     opacity: 0; | ||||
|   } | ||||
| 
 | ||||
|   to { | ||||
|     opacity: 1; | ||||
|     transform: translateY(0) rotate(180deg); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @keyframes fadeTextToCenter { | ||||
|   to { | ||||
|     opacity: 1; | ||||
|     transform: translateX(-50%); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @keyframes FadeContentToCenter { | ||||
|   to { | ||||
|     opacity: 1; | ||||
|     transform: translateX(0); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,203 @@ | ||||
| import React, { useState, useEffect, useRef, useLayoutEffect } from 'react'; | ||||
| 
 | ||||
| import { Calendar } from './Calendar'; | ||||
| import DatePickerInput from './DatePickerInput'; | ||||
| import { getValueType } from './shared/generalUtils'; | ||||
| import { TYPE_SINGLE_DATE, TYPE_MUTLI_DATE, TYPE_RANGE } from './shared/constants'; | ||||
| 
 | ||||
| const DatePicker = ({ | ||||
|   value, | ||||
|   onChange, | ||||
|   formatInputText, | ||||
|   inputPlaceholder, | ||||
|   inputClassName, | ||||
|   inputName, | ||||
|   renderInput, | ||||
|   wrapperClassName, | ||||
|   calendarClassName, | ||||
|   calendarTodayClassName, | ||||
|   calendarSelectedDayClassName, | ||||
|   calendarRangeStartClassName, | ||||
|   calendarRangeBetweenClassName, | ||||
|   calendarRangeEndClassName, | ||||
|   calendarPopperPosition, | ||||
|   disabledDays, | ||||
|   onDisabledDayError, | ||||
|   colorPrimary, | ||||
|   colorPrimaryLight, | ||||
|   slideAnimationDuration, | ||||
|   minimumDate, | ||||
|   maximumDate, | ||||
|   selectorStartingYear, | ||||
|   selectorEndingYear, | ||||
|   locale, | ||||
|   shouldHighlightWeekends, | ||||
|   renderFooter, | ||||
|   customDaysClassName, | ||||
| }) => { | ||||
|   const calendarContainerElement = useRef(null); | ||||
|   const inputElement = useRef(null); | ||||
|   const shouldPreventToggle = useRef(false); | ||||
|   const [isCalendarOpen, setCalendarVisiblity] = useState(false); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     const handleBlur = () => { | ||||
|       setCalendarVisiblity(false); | ||||
|     }; | ||||
|     window.addEventListener('blur', handleBlur, false); | ||||
|     return () => { | ||||
|       window.removeEventListener('blur', handleBlur, false); | ||||
|     }; | ||||
|   }, []); | ||||
| 
 | ||||
|   // handle input focus/blur
 | ||||
|   useEffect(() => { | ||||
|     const valueType = getValueType(value); | ||||
|     if (valueType === TYPE_MUTLI_DATE) return; // no need to close the calendar
 | ||||
|     const shouldCloseCalendar = | ||||
|       valueType === TYPE_SINGLE_DATE ? !isCalendarOpen : !isCalendarOpen && value.from && value.to; | ||||
|     if (shouldCloseCalendar) inputElement.current.blur(); | ||||
|   }, [value, isCalendarOpen]); | ||||
| 
 | ||||
|   const handleBlur = e => { | ||||
|     e.persist(); | ||||
|     if (!isCalendarOpen) return; | ||||
|     const isInnerElementFocused = calendarContainerElement.current.contains(e.relatedTarget); | ||||
|     if (shouldPreventToggle.current) { | ||||
|       shouldPreventToggle.current = false; | ||||
|       inputElement.current.focus(); | ||||
|     } else if (isInnerElementFocused && e.relatedTarget) { | ||||
|       e.relatedTarget.focus(); | ||||
|     } else { | ||||
|       setCalendarVisiblity(false); | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   const openCalendar = () => { | ||||
|     if (!shouldPreventToggle.current) setCalendarVisiblity(true); | ||||
|   }; | ||||
| 
 | ||||
|   // Keep the calendar in the screen bounds if input is near the window edges
 | ||||
|   useLayoutEffect(() => { | ||||
|     if (!isCalendarOpen) return; | ||||
|     const { left, width, height, top } = calendarContainerElement.current.getBoundingClientRect(); | ||||
|     const { clientWidth, clientHeight } = document.documentElement; | ||||
|     const isOverflowingFromRight = left + width > clientWidth; | ||||
|     const isOverflowingFromLeft = left < 0; | ||||
|     const isOverflowingFromBottom = top + height > clientHeight; | ||||
| 
 | ||||
|     const getLeftStyle = () => { | ||||
|       const overflowFromRightDistance = left + width - clientWidth; | ||||
| 
 | ||||
|       if (!isOverflowingFromRight && !isOverflowingFromLeft) return; | ||||
|       const overflowFromLeftDistance = Math.abs(left); | ||||
|       const rightPosition = isOverflowingFromLeft ? overflowFromLeftDistance : 0; | ||||
| 
 | ||||
|       const leftStyle = isOverflowingFromRight | ||||
|         ? `calc(50% - ${overflowFromRightDistance}px)` | ||||
|         : `calc(50% + ${rightPosition}px)`; | ||||
|       return leftStyle; | ||||
|     }; | ||||
| 
 | ||||
|     calendarContainerElement.current.style.left = getLeftStyle(); | ||||
|     if ( | ||||
|       (calendarPopperPosition === 'auto' && isOverflowingFromBottom) || | ||||
|       calendarPopperPosition === 'top' | ||||
|     ) { | ||||
|       calendarContainerElement.current.classList.add('-top'); | ||||
|     } | ||||
|   }, [isCalendarOpen]); | ||||
| 
 | ||||
|   const handleCalendarChange = newValue => { | ||||
|     const valueType = getValueType(value); | ||||
|     onChange(newValue); | ||||
|     if (valueType === TYPE_SINGLE_DATE) setCalendarVisiblity(false); | ||||
|     else if (valueType === TYPE_RANGE && newValue.from && newValue.to) setCalendarVisiblity(false); | ||||
|   }; | ||||
| 
 | ||||
|   const handleKeyUp = ({ key }) => { | ||||
|     switch (key) { | ||||
|       case 'Enter': | ||||
|         setCalendarVisiblity(true); | ||||
|         break; | ||||
|       case 'Escape': | ||||
|         setCalendarVisiblity(false); | ||||
|         shouldPreventToggle.current = true; | ||||
|         break; | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     if (!isCalendarOpen && shouldPreventToggle.current) { | ||||
|       inputElement.current.focus(); | ||||
|       shouldPreventToggle.current = false; | ||||
|     } | ||||
|   }, [shouldPreventToggle, isCalendarOpen]); | ||||
| 
 | ||||
|   return ( | ||||
|     <div | ||||
|       onFocus={openCalendar} | ||||
|       onBlur={handleBlur} | ||||
|       onKeyUp={handleKeyUp} | ||||
|       className={`DatePicker ${wrapperClassName}`} | ||||
|       role="presentation" | ||||
|     > | ||||
|       <DatePickerInput | ||||
|         ref={inputElement} | ||||
|         formatInputText={formatInputText} | ||||
|         value={value} | ||||
|         inputPlaceholder={inputPlaceholder} | ||||
|         inputClassName={inputClassName} | ||||
|         renderInput={renderInput} | ||||
|         inputName={inputName} | ||||
|         locale={locale} | ||||
|       /> | ||||
|       {isCalendarOpen && ( | ||||
|         <> | ||||
|           <div | ||||
|             ref={calendarContainerElement} | ||||
|             className="DatePicker__calendarContainer" | ||||
|             data-testid="calendar-container" | ||||
|             role="presentation" | ||||
|             onMouseDown={() => { | ||||
|               shouldPreventToggle.current = true; | ||||
|             }} | ||||
|           > | ||||
|             <Calendar | ||||
|               value={value} | ||||
|               onChange={handleCalendarChange} | ||||
|               calendarClassName={calendarClassName} | ||||
|               calendarTodayClassName={calendarTodayClassName} | ||||
|               calendarSelectedDayClassName={calendarSelectedDayClassName} | ||||
|               calendarRangeStartClassName={calendarRangeStartClassName} | ||||
|               calendarRangeBetweenClassName={calendarRangeBetweenClassName} | ||||
|               calendarRangeEndClassName={calendarRangeEndClassName} | ||||
|               disabledDays={disabledDays} | ||||
|               colorPrimary={colorPrimary} | ||||
|               colorPrimaryLight={colorPrimaryLight} | ||||
|               slideAnimationDuration={slideAnimationDuration} | ||||
|               onDisabledDayError={onDisabledDayError} | ||||
|               minimumDate={minimumDate} | ||||
|               maximumDate={maximumDate} | ||||
|               selectorStartingYear={selectorStartingYear} | ||||
|               selectorEndingYear={selectorEndingYear} | ||||
|               locale={locale} | ||||
|               shouldHighlightWeekends={shouldHighlightWeekends} | ||||
|               renderFooter={renderFooter} | ||||
|               customDaysClassName={customDaysClassName} | ||||
|             /> | ||||
|           </div> | ||||
|           <div className="DatePicker__calendarArrow" /> | ||||
|         </> | ||||
|       )} | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
| 
 | ||||
| DatePicker.defaultProps = { | ||||
|   wrapperClassName: '', | ||||
|   locale: 'en', | ||||
|   calendarPopperPosition: 'auto', | ||||
| }; | ||||
| 
 | ||||
| export default DatePicker; | ||||
| @ -0,0 +1,93 @@ | ||||
| import React from 'react'; | ||||
| 
 | ||||
| import { useLocaleUtils, useLocaleLanguage } from './shared/hooks'; | ||||
| import { putZero, getValueType } from './shared/generalUtils'; | ||||
| import { TYPE_SINGLE_DATE, TYPE_RANGE, TYPE_MUTLI_DATE } from './shared/constants'; | ||||
| 
 | ||||
| const DatePickerInput = React.forwardRef( | ||||
|   ( | ||||
|     { value, inputPlaceholder, inputClassName, inputName, formatInputText, renderInput, locale }, | ||||
|     ref, | ||||
|   ) => { | ||||
|     const { getLanguageDigits } = useLocaleUtils(locale); | ||||
|     const { | ||||
|       from: fromWord, | ||||
|       to: toWord, | ||||
|       yearLetterSkip, | ||||
|       digitSeparator, | ||||
|       defaultPlaceholder, | ||||
|       isRtl, | ||||
|     } = useLocaleLanguage(locale); | ||||
| 
 | ||||
|     const getSingleDayValue = () => { | ||||
|       if (!value) return ''; | ||||
|       const year = getLanguageDigits(value.year); | ||||
|       const month = getLanguageDigits(putZero(value.month)); | ||||
|       const day = getLanguageDigits(putZero(value.day)); | ||||
|       return `${year}/${month}/${day}`; | ||||
|     }; | ||||
| 
 | ||||
|     const getDayRangeValue = () => { | ||||
|       if (!value.from || !value.to) return ''; | ||||
|       const { from, to } = value; | ||||
|       const fromText = `${getLanguageDigits(putZero(from.year)) | ||||
|         .toString() | ||||
|         .slice(yearLetterSkip)}/${getLanguageDigits(putZero(from.month))}/${getLanguageDigits( | ||||
|         putZero(from.day), | ||||
|       )}`;
 | ||||
|       const toText = `${getLanguageDigits(putZero(to.year)) | ||||
|         .toString() | ||||
|         .slice(yearLetterSkip)}/${getLanguageDigits(putZero(to.month))}/${getLanguageDigits( | ||||
|         putZero(to.day), | ||||
|       )}`;
 | ||||
|       return `${fromWord} ${fromText} ${toWord} ${toText}`; | ||||
|     }; | ||||
| 
 | ||||
|     const getMultiDateValue = () => { | ||||
|       return value.map(date => getLanguageDigits(date.day)).join(`${digitSeparator} `); | ||||
|     }; | ||||
| 
 | ||||
|     const getValue = () => { | ||||
|       if (formatInputText()) return formatInputText(); | ||||
|       const valueType = getValueType(value); | ||||
|       switch (valueType) { | ||||
|         case TYPE_SINGLE_DATE: | ||||
|           return getSingleDayValue(); | ||||
|         case TYPE_RANGE: | ||||
|           return getDayRangeValue(); | ||||
|         case TYPE_MUTLI_DATE: | ||||
|           return getMultiDateValue(); | ||||
|       } | ||||
|     }; | ||||
| 
 | ||||
|     const placeholderValue = inputPlaceholder || defaultPlaceholder; | ||||
|     const render = () => { | ||||
|       return ( | ||||
|         renderInput({ ref }) || ( | ||||
|           <input | ||||
|             data-testid="datepicker-input" | ||||
|             readOnly | ||||
|             ref={ref} | ||||
|             value={getValue()} | ||||
|             name={inputName} | ||||
|             placeholder={placeholderValue} | ||||
|             className={`DatePicker__input -${isRtl ? 'rtl' : 'ltr'} ${inputClassName}`} | ||||
|             aria-label={placeholderValue} | ||||
|           /> | ||||
|         ) | ||||
|       ); | ||||
|     }; | ||||
| 
 | ||||
|     return render(); | ||||
|   }, | ||||
| ); | ||||
| 
 | ||||
| DatePickerInput.defaultProps = { | ||||
|   formatInputText: () => '', | ||||
|   renderInput: () => null, | ||||
|   inputPlaceholder: '', | ||||
|   inputClassName: '', | ||||
|   inputName: '', | ||||
| }; | ||||
| 
 | ||||
| export default DatePickerInput; | ||||
| @ -0,0 +1,385 @@ | ||||
| import React, { useRef, useEffect } from 'react'; | ||||
| 
 | ||||
| import { | ||||
|   getSlideDate, | ||||
|   handleSlideAnimationEnd, | ||||
|   animateContent, | ||||
| } from '../shared/sliderHelpers'; | ||||
| import { | ||||
|   deepCloneObject, | ||||
|   isSameDay, | ||||
|   createUniqueRange, | ||||
|   getValueType, | ||||
| } from '../shared/generalUtils'; | ||||
| import { | ||||
|   TYPE_SINGLE_DATE, | ||||
|   TYPE_RANGE, | ||||
|   TYPE_MUTLI_DATE, | ||||
| } from '../shared/constants'; | ||||
| import handleKeyboardNavigation from '../shared/keyboardNavigation'; | ||||
| import { useLocaleUtils, useLocaleLanguage } from '../shared/hooks'; | ||||
| 
 | ||||
| const DaysList = ({ | ||||
|   activeDate, | ||||
|   value, | ||||
|   monthChangeDirection, | ||||
|   onSlideChange, | ||||
|   disabledDays, | ||||
|   onDisabledDayError, | ||||
|   minimumDate, | ||||
|   maximumDate, | ||||
|   onChange, | ||||
|   locale, | ||||
|   calendarTodayClassName, | ||||
|   calendarSelectedDayClassName, | ||||
|   calendarRangeStartClassName, | ||||
|   calendarRangeEndClassName, | ||||
|   calendarRangeBetweenClassName, | ||||
|   shouldHighlightWeekends, | ||||
|   isQuickSelectorOpen, | ||||
|   customDaysClassName, | ||||
|   events, | ||||
| }) => { | ||||
|   const calendarSectionWrapper = useRef(null); | ||||
|   const { isRtl, weekDays: weekDaysList } = useLocaleLanguage(locale); | ||||
|   const { | ||||
|     getToday, | ||||
|     isBeforeDate, | ||||
|     checkDayInDayRange, | ||||
|     getMonthFirstWeekday, | ||||
|     getMonthLength, | ||||
|     getLanguageDigits, | ||||
|     getMonthName, | ||||
|   } = useLocaleUtils(locale); | ||||
|   const today = getToday(); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     if (!monthChangeDirection) return; | ||||
|     animateContent({ | ||||
|       direction: monthChangeDirection, | ||||
|       parent: calendarSectionWrapper.current, | ||||
|     }); | ||||
|   }, [monthChangeDirection]); | ||||
| 
 | ||||
|   const getDayRangeValue = (day) => { | ||||
|     const clonedDayRange = deepCloneObject(value); | ||||
|     const dayRangeValue = | ||||
|       clonedDayRange.from && clonedDayRange.to | ||||
|         ? { from: null, to: null } | ||||
|         : clonedDayRange; | ||||
|     const dayRangeProp = !dayRangeValue.from ? 'from' : 'to'; | ||||
|     dayRangeValue[dayRangeProp] = day; | ||||
|     const { from, to } = dayRangeValue; | ||||
| 
 | ||||
|     // swap from and to values if from is later than to
 | ||||
|     if (isBeforeDate(dayRangeValue.to, dayRangeValue.from)) { | ||||
|       dayRangeValue.from = to; | ||||
|       dayRangeValue.to = from; | ||||
|     } | ||||
| 
 | ||||
|     const checkIncludingDisabledDay = (disabledDay) => { | ||||
|       return checkDayInDayRange({ | ||||
|         day: disabledDay, | ||||
|         from: dayRangeValue.from, | ||||
|         to: dayRangeValue.to, | ||||
|       }); | ||||
|     }; | ||||
|     const includingDisabledDay = disabledDays.find(checkIncludingDisabledDay); | ||||
|     if (includingDisabledDay) { | ||||
|       onDisabledDayError(includingDisabledDay); | ||||
|       return value; | ||||
|     } | ||||
| 
 | ||||
|     return dayRangeValue; | ||||
|   }; | ||||
| 
 | ||||
|   const getMultiDateValue = (day) => { | ||||
|     const isAlreadyExisting = value.some((valueDay) => | ||||
|       isSameDay(valueDay, day) | ||||
|     ); | ||||
|     const addedToValue = [...value, day]; | ||||
|     const removedFromValue = value.filter( | ||||
|       (valueDay) => !isSameDay(valueDay, day) | ||||
|     ); | ||||
|     return isAlreadyExisting ? removedFromValue : addedToValue; | ||||
|   }; | ||||
| 
 | ||||
|   const handleDayClick = (day) => { | ||||
|     const getNewValue = () => { | ||||
|       const valueType = getValueType(value); | ||||
|       switch (valueType) { | ||||
|         case TYPE_SINGLE_DATE: | ||||
|           return day; | ||||
|         case TYPE_RANGE: | ||||
|           return getDayRangeValue(day); | ||||
|         case TYPE_MUTLI_DATE: | ||||
|           return getMultiDateValue(day); | ||||
|       } | ||||
|     }; | ||||
|     const newValue = getNewValue(); | ||||
|     onChange(newValue); | ||||
|   }; | ||||
| 
 | ||||
|   const isSingleDateSelected = (day) => { | ||||
|     const valueType = getValueType(value); | ||||
|     if (valueType === TYPE_SINGLE_DATE) return isSameDay(day, value); | ||||
|     if (valueType === TYPE_MUTLI_DATE) | ||||
|       return value.some((valueDay) => isSameDay(valueDay, day)); | ||||
|   }; | ||||
| 
 | ||||
|   const getDayStatus = (dayItem) => { | ||||
|     const isToday = isSameDay(dayItem, today); | ||||
|     const isSelected = isSingleDateSelected(dayItem); | ||||
|     const { from: startingDay, to: endingDay } = value || {}; | ||||
|     const isStartingDayRange = isSameDay(dayItem, startingDay); | ||||
|     const isEndingDayRange = isSameDay(dayItem, endingDay); | ||||
|     const isWithinRange = checkDayInDayRange({ | ||||
|       day: dayItem, | ||||
|       from: startingDay, | ||||
|       to: endingDay, | ||||
|     }); | ||||
|     return { | ||||
|       isToday, | ||||
|       isSelected, | ||||
|       isStartingDayRange, | ||||
|       isEndingDayRange, | ||||
|       isWithinRange, | ||||
|     }; | ||||
|   }; | ||||
| 
 | ||||
|   const getDayClassNames = (dayItem) => { | ||||
|     const { | ||||
|       isToday, | ||||
|       isSelected, | ||||
|       isStartingDayRange, | ||||
|       isEndingDayRange, | ||||
|       isWithinRange, | ||||
|     } = getDayStatus(dayItem); | ||||
|     const customDayItemClassName = customDaysClassName.find((day) => | ||||
|       isSameDay(dayItem, day) | ||||
|     ); | ||||
|     const classNames = '' | ||||
|       .concat(isToday && !isSelected ? ` -today ${calendarTodayClassName}` : '') | ||||
|       .concat(!dayItem.isStandard ? ' -blank' : '') | ||||
|       .concat(dayItem.isWeekend && shouldHighlightWeekends ? ' -weekend' : '') | ||||
|       .concat( | ||||
|         customDayItemClassName ? ` ${customDayItemClassName.className}` : '' | ||||
|       ) | ||||
|       .concat(isSelected ? ` -selected ${calendarSelectedDayClassName}` : '') | ||||
|       .concat( | ||||
|         isStartingDayRange | ||||
|           ? ` -selectedStart ${calendarRangeStartClassName}` | ||||
|           : '' | ||||
|       ) | ||||
|       .concat( | ||||
|         isEndingDayRange ? ` -selectedEnd ${calendarRangeEndClassName}` : '' | ||||
|       ) | ||||
|       .concat( | ||||
|         isWithinRange | ||||
|           ? ` -selectedBetween ${calendarRangeBetweenClassName}` | ||||
|           : '' | ||||
|       ) | ||||
|       .concat(dayItem.isDisabled ? ' -disabled' : ''); | ||||
|     return classNames; | ||||
|   }; | ||||
| 
 | ||||
|   const getViewMonthDays = (date) => { | ||||
|     // to match month starting date with the correct weekday label
 | ||||
|     const prependingBlankDays = createUniqueRange( | ||||
|       getMonthFirstWeekday(date), | ||||
|       'starting-blank' | ||||
|     ); | ||||
|     const standardDays = createUniqueRange(getMonthLength(date)).map((day) => ({ | ||||
|       ...day, | ||||
|       isStandard: true, | ||||
|       month: date.month, | ||||
|       year: date.year, | ||||
|     })); | ||||
|     const allDays = [...prependingBlankDays, ...standardDays]; | ||||
|     return allDays; | ||||
|   }; | ||||
| 
 | ||||
|   const handleDayPress = ({ isDisabled, ...dayItem }) => { | ||||
|     if (isDisabled) { | ||||
|       onDisabledDayError(dayItem); // good for showing error messages
 | ||||
|     } else handleDayClick(dayItem); | ||||
|   }; | ||||
| 
 | ||||
|   const isDayReachableByKeyboard = ({ | ||||
|     isOnActiveSlide, | ||||
|     isStandard, | ||||
|     isSelected, | ||||
|     isStartingDayRange, | ||||
|     isToday, | ||||
|     day, | ||||
|   }) => { | ||||
|     if (isQuickSelectorOpen || !isOnActiveSlide || !isStandard) return false; | ||||
|     if (isSelected || isStartingDayRange || isToday || day === 1) return true; | ||||
|   }; | ||||
| 
 | ||||
|   const renderEachWeekDays = ( | ||||
|     { id, value: day, month, year, isStandard }, | ||||
|     index | ||||
|   ) => { | ||||
|     const dayItem = { day, month, year }; | ||||
|     const isInDisabledDaysRange = disabledDays.some((disabledDay) => | ||||
|       isSameDay(dayItem, disabledDay) | ||||
|     ); | ||||
|     const isBeforeMinimumDate = isBeforeDate(dayItem, minimumDate); | ||||
|     const isAfterMaximumDate = isBeforeDate(maximumDate, dayItem); | ||||
|     const isNotInValidRange = | ||||
|       isStandard && (isBeforeMinimumDate || isAfterMaximumDate); | ||||
|     const isDisabled = isInDisabledDaysRange || isNotInValidRange; | ||||
|     const isWeekend = weekDaysList.some( | ||||
|       (weekDayItem, weekDayItemIndex) => | ||||
|         weekDayItem.isWeekend && weekDayItemIndex === index | ||||
|     ); | ||||
|     const additionalClass = getDayClassNames({ | ||||
|       ...dayItem, | ||||
|       isWeekend, | ||||
|       isStandard, | ||||
|       isDisabled, | ||||
|     }); | ||||
|     const dayLabel = `${weekDaysList[index].name}, ${day} ${getMonthName( | ||||
|       month | ||||
|     )} ${year}`;
 | ||||
|     const isOnActiveSlide = month === activeDate.month; | ||||
|     const dayStatus = getDayStatus(dayItem); | ||||
|     const { isSelected, isStartingDayRange, isEndingDayRange, isWithinRange } = | ||||
|       dayStatus; | ||||
|     const shouldEnableKeyboardNavigation = isDayReachableByKeyboard({ | ||||
|       ...dayItem, | ||||
|       ...dayStatus, | ||||
|       isOnActiveSlide, | ||||
|       isStandard, | ||||
|     }); | ||||
|     const specialDay = (sDay) => { | ||||
|       // let specialDayArray = [];
 | ||||
|       // console.log(sDay);
 | ||||
| 
 | ||||
|       // specialDayArray.push(events[i].day);
 | ||||
|       var eventDate; | ||||
|       for (var i = 0; i < events.length; i++) { | ||||
|         if (sDay == events[i].day) { | ||||
|           
 | ||||
|           return sDay; | ||||
|         
 | ||||
|         } | ||||
|       } | ||||
|       //  sDay = events[0].day;
 | ||||
|       // return sDay;
 | ||||
|       // console.log(sDay);
 | ||||
| 
 | ||||
|       // console.log(specialDayArray[0])
 | ||||
|     }; | ||||
| 
 | ||||
|     return ( | ||||
|       <div | ||||
|         style={{ | ||||
|           display: 'flex', | ||||
|           flexDirection: 'column', | ||||
|           alignItems: 'center', | ||||
|         }} | ||||
|         tabIndex={shouldEnableKeyboardNavigation ? '0' : '-1'} | ||||
|         key={id} | ||||
|         className={`Calendar__day -${isRtl ? 'rtl' : 'ltr'} ${additionalClass}`} | ||||
|         onClick={() => { | ||||
|           handleDayPress({ ...dayItem, isDisabled }); | ||||
|           console.log(specialDay(day)); | ||||
|           console.log(day) | ||||
|         }} | ||||
|         onKeyDown={({ key }) => { | ||||
|           /* istanbul ignore else */ | ||||
|           if (key === 'Enter') handleDayPress({ ...dayItem, isDisabled }); | ||||
|         }} | ||||
|         aria-disabled={isDisabled} | ||||
|         aria-label={dayLabel} | ||||
|         aria-selected={ | ||||
|           isSelected || isStartingDayRange || isEndingDayRange || isWithinRange | ||||
|         } | ||||
|         {...(!isStandard || !isOnActiveSlide || isQuickSelectorOpen | ||||
|           ? { 'aria-hidden': true } | ||||
|           : {})} | ||||
|         role="gridcell" | ||||
|         data-is-default-selectable={shouldEnableKeyboardNavigation} | ||||
|       > | ||||
|         {!isStandard ? '' : getLanguageDigits(day)} | ||||
|         {specialDay(day) == day ?  '.' : null} | ||||
|       </div> | ||||
|     ); | ||||
|   }; | ||||
| 
 | ||||
|   const renderMonthDays = (isInitialActiveChild) => { | ||||
|     const date = getSlideDate({ | ||||
|       activeDate, | ||||
|       isInitialActiveChild, | ||||
|       monthChangeDirection, | ||||
|       parent: calendarSectionWrapper.current, | ||||
|     }); | ||||
|     const allDays = getViewMonthDays(date); | ||||
|     const renderSingleWeekRow = (weekRowIndex) => { | ||||
|       const eachWeekDays = allDays | ||||
|         .slice(weekRowIndex * 7, weekRowIndex * 7 + 7) | ||||
|         .map(renderEachWeekDays); | ||||
|       return ( | ||||
|         <div | ||||
|           key={String(weekRowIndex)} | ||||
|           className="Calendar__weekRow" | ||||
|           role="row" | ||||
|         > | ||||
|           {eachWeekDays} | ||||
|         </div> | ||||
|       ); | ||||
|     }; | ||||
|     return Array.from(Array(6).keys()).map(renderSingleWeekRow); | ||||
|   }; | ||||
| 
 | ||||
|   const handleKeyDown = (e) => { | ||||
|     handleKeyboardNavigation(e, { allowVerticalArrows: true }); | ||||
|   }; | ||||
| 
 | ||||
|   return ( | ||||
|     <div | ||||
|       ref={calendarSectionWrapper} | ||||
|       className="Calendar__sectionWrapper" | ||||
|       role="presentation" | ||||
|       data-testid="days-section-wrapper" | ||||
|       onKeyDown={handleKeyDown} | ||||
|     > | ||||
|       <div | ||||
|         onAnimationEnd={(e) => { | ||||
|           handleSlideAnimationEnd(e); | ||||
|           onSlideChange(); | ||||
|         }} | ||||
|         className="Calendar__section -shown" | ||||
|         role="rowgroup" | ||||
|       > | ||||
|         {renderMonthDays(true)} | ||||
|       </div> | ||||
|       <div | ||||
|         onAnimationEnd={(e) => { | ||||
|           handleSlideAnimationEnd(e); | ||||
|           onSlideChange(); | ||||
|         }} | ||||
|         className="Calendar__section -hiddenNext" | ||||
|         role="rowgroup" | ||||
|       > | ||||
|         {renderMonthDays(false)} | ||||
|       </div> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
| 
 | ||||
| DaysList.defaultProps = { | ||||
|   onChange: () => {}, | ||||
|   onDisabledDayError: () => {}, | ||||
|   disabledDays: [], | ||||
|   calendarTodayClassName: '', | ||||
|   calendarSelectedDayClassName: '', | ||||
|   calendarRangeStartClassName: '', | ||||
|   calendarRangeBetweenClassName: '', | ||||
|   calendarRangeEndClassName: '', | ||||
|   shouldHighlightWeekends: false, | ||||
| }; | ||||
| 
 | ||||
| export default DaysList; | ||||
| @ -0,0 +1,189 @@ | ||||
| import React, { useEffect, useRef } from 'react'; | ||||
| 
 | ||||
| import { isSameDay } from '../shared/generalUtils'; | ||||
| import { getSlideDate, animateContent, handleSlideAnimationEnd } from '../shared/sliderHelpers'; | ||||
| import { useLocaleUtils, useLocaleLanguage } from '../shared/hooks'; | ||||
| 
 | ||||
| const Header = ({ | ||||
|   maximumDate, | ||||
|   minimumDate, | ||||
|   onMonthChange, | ||||
|   activeDate, | ||||
|   monthChangeDirection, | ||||
|   onMonthSelect, | ||||
|   onYearSelect, | ||||
|   isMonthSelectorOpen, | ||||
|   isYearSelectorOpen, | ||||
|   locale, | ||||
| }) => { | ||||
|   const headerElement = useRef(null); | ||||
|   const monthYearWrapperElement = useRef(null); | ||||
| 
 | ||||
|   const { getMonthName, isBeforeDate, getLanguageDigits } = useLocaleUtils(locale); | ||||
|   const { | ||||
|     isRtl, | ||||
|     nextMonth, | ||||
|     previousMonth, | ||||
|     openMonthSelector, | ||||
|     closeMonthSelector, | ||||
|     openYearSelector, | ||||
|     closeYearSelector, | ||||
|   } = useLocaleLanguage(locale); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     if (!monthChangeDirection) return; | ||||
|     animateContent({ | ||||
|       direction: monthChangeDirection, | ||||
|       parent: monthYearWrapperElement.current, | ||||
|     }); | ||||
|   }, [monthChangeDirection]); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     const isOpen = isMonthSelectorOpen || isYearSelectorOpen; | ||||
|     const monthText = headerElement.current.querySelector( | ||||
|       '.Calendar__monthYear.-shown .Calendar__monthText', | ||||
|     ); | ||||
|     const yearText = monthText.nextSibling; | ||||
|     const hasActiveBackground = element => element.classList.contains('-activeBackground'); | ||||
|     const isInitialRender = | ||||
|       !isOpen && !hasActiveBackground(monthText) && !hasActiveBackground(yearText); | ||||
|     if (isInitialRender) return; | ||||
| 
 | ||||
|     const arrows = [...headerElement.current.querySelectorAll('.Calendar__monthArrowWrapper')]; | ||||
|     const hasMonthSelectorToggled = isMonthSelectorOpen || hasActiveBackground(monthText); | ||||
|     const primaryElement = hasMonthSelectorToggled ? monthText : yearText; | ||||
|     const secondaryElement = hasMonthSelectorToggled ? yearText : monthText; | ||||
| 
 | ||||
|     let translateXDirection = hasMonthSelectorToggled ? 1 : -1; | ||||
|     if (isRtl) translateXDirection *= -1; | ||||
|     const scale = !isOpen ? 0.95 : 1; | ||||
|     const translateX = !isOpen ? 0 : `${(translateXDirection * secondaryElement.offsetWidth) / 2}`; | ||||
|     if (!isOpen) { | ||||
|       secondaryElement.removeAttribute('aria-hidden'); | ||||
|     } else { | ||||
|       secondaryElement.setAttribute('aria-hidden', true); | ||||
|     } | ||||
|     secondaryElement.setAttribute('tabindex', isOpen ? '-1' : '0'); | ||||
|     secondaryElement.style.transform = ''; | ||||
|     primaryElement.style.transform = `scale(${scale}) ${ | ||||
|       translateX ? `translateX(${translateX}px)` : '' | ||||
|     }`;
 | ||||
|     primaryElement.classList.toggle('-activeBackground'); | ||||
|     secondaryElement.classList.toggle('-hidden'); | ||||
|     arrows.forEach(arrow => { | ||||
|       const isHidden = arrow.classList.contains('-hidden'); | ||||
|       arrow.classList.toggle('-hidden'); | ||||
|       if (isHidden) { | ||||
|         arrow.removeAttribute('aria-hidden'); | ||||
|         arrow.setAttribute('tabindex', '0'); | ||||
|       } else { | ||||
|         arrow.setAttribute('aria-hidden', true); | ||||
|         arrow.setAttribute('tabindex', '-1'); | ||||
|       } | ||||
|     }); | ||||
|   }, [isMonthSelectorOpen, isYearSelectorOpen]); | ||||
| 
 | ||||
|   const getMonthYearText = isInitialActiveChild => { | ||||
|     const date = getSlideDate({ | ||||
|       isInitialActiveChild, | ||||
|       monthChangeDirection, | ||||
|       activeDate, | ||||
|       parent: monthYearWrapperElement.current, | ||||
|     }); | ||||
|     const year = getLanguageDigits(date.year); | ||||
|     const month = getMonthName(date.month); | ||||
|     return { month, year }; | ||||
|   }; | ||||
| 
 | ||||
|   const isNextMonthArrowDisabled = | ||||
|     maximumDate && | ||||
|     isBeforeDate(maximumDate, { ...activeDate, month: activeDate.month + 1, day: 1 }); | ||||
|   const isPreviousMonthArrowDisabled = | ||||
|     minimumDate && | ||||
|     (isBeforeDate({ ...activeDate, day: 1 }, minimumDate) || | ||||
|       isSameDay(minimumDate, { ...activeDate, day: 1 })); | ||||
| 
 | ||||
|   const onMonthChangeTrigger = direction => { | ||||
|     const isMonthChanging = Array.from(monthYearWrapperElement.current.children).some(child => | ||||
|       child.classList.contains('-shownAnimated'), | ||||
|     ); | ||||
|     if (isMonthChanging) return; | ||||
|     onMonthChange(direction); | ||||
|   }; | ||||
| 
 | ||||
|   // first button text is the one who shows the current month and year(initial active child)
 | ||||
|   const monthYearButtons = [true, false].map(isInitialActiveChild => { | ||||
|     const { month, year } = getMonthYearText(isInitialActiveChild); | ||||
|     const isActiveMonth = month === getMonthName(activeDate.month); | ||||
|     const hiddenStatus = { | ||||
|       ...(isActiveMonth ? {} : { 'aria-hidden': true }), | ||||
|     }; | ||||
|     return ( | ||||
|       <div | ||||
|         onAnimationEnd={handleSlideAnimationEnd} | ||||
|         className={`Calendar__monthYear ${isInitialActiveChild ? '-shown' : '-hiddenNext'}`} | ||||
|         role="presentation" | ||||
|         key={String(isInitialActiveChild)} | ||||
|         {...hiddenStatus} | ||||
|       > | ||||
|         <button | ||||
|           onClick={onMonthSelect} | ||||
|           type="button" | ||||
|           className="Calendar__monthText" | ||||
|           aria-label={isMonthSelectorOpen ? closeMonthSelector : openMonthSelector} | ||||
|           tabIndex={isActiveMonth ? '0' : '-1'} | ||||
|           {...hiddenStatus} | ||||
|         > | ||||
|           {month} | ||||
|         </button> | ||||
|         <button | ||||
|           onClick={onYearSelect} | ||||
|           type="button" | ||||
|           className="Calendar__yearText" | ||||
|           aria-label={isYearSelectorOpen ? closeYearSelector : openYearSelector} | ||||
|           tabIndex={isActiveMonth ? '0' : '-1'} | ||||
|           {...hiddenStatus} | ||||
|         > | ||||
|           {year} | ||||
|         </button> | ||||
|       </div> | ||||
|     ); | ||||
|   }); | ||||
| 
 | ||||
|   return ( | ||||
|     <div ref={headerElement} className="Calendar__header"> | ||||
|       <button | ||||
|         className="Calendar__monthArrowWrapper -right" | ||||
|         onClick={() => { | ||||
|           onMonthChangeTrigger('PREVIOUS'); | ||||
|         }} | ||||
|         aria-label={previousMonth} | ||||
|         type="button" | ||||
|         disabled={isPreviousMonthArrowDisabled} | ||||
|       > | ||||
|         <span className="Calendar__monthArrow" /> | ||||
|       </button> | ||||
|       <div | ||||
|         className="Calendar__monthYearContainer" | ||||
|         ref={monthYearWrapperElement} | ||||
|         data-testid="month-year-container" | ||||
|       > | ||||
|           | ||||
|         {monthYearButtons} | ||||
|       </div> | ||||
|       <button | ||||
|         className="Calendar__monthArrowWrapper -left" | ||||
|         onClick={() => { | ||||
|           onMonthChangeTrigger('NEXT'); | ||||
|         }} | ||||
|         aria-label={nextMonth} | ||||
|         type="button" | ||||
|         disabled={isNextMonthArrowDisabled} | ||||
|       > | ||||
|         <span className="Calendar__monthArrow" /> | ||||
|       </button> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
| 
 | ||||
| export default Header; | ||||
| @ -0,0 +1,74 @@ | ||||
| import React, { useRef, useEffect } from 'react'; | ||||
| 
 | ||||
| import { isSameDay } from '../shared/generalUtils'; | ||||
| import handleKeyboardNavigation from '../shared/keyboardNavigation'; | ||||
| import { useLocaleUtils, useLocaleLanguage } from '../shared/hooks'; | ||||
| 
 | ||||
| const MonthSelector = ({ activeDate, maximumDate, minimumDate, onMonthSelect, isOpen, locale }) => { | ||||
|   const monthSelector = useRef(null); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     const classToggleMethod = isOpen ? 'add' : 'remove'; | ||||
|     monthSelector.current.classList[classToggleMethod]('-open'); | ||||
|   }, [isOpen]); | ||||
| 
 | ||||
|   const { getMonthNumber, isBeforeDate } = useLocaleUtils(locale); | ||||
|   const { months: monthsList } = useLocaleLanguage(locale); | ||||
| 
 | ||||
|   const handleKeyDown = e => { | ||||
|     handleKeyboardNavigation(e, { allowVerticalArrows: false }); | ||||
|   }; | ||||
| 
 | ||||
|   const renderMonthSelectorItems = () => | ||||
|     monthsList.map(persianMonth => { | ||||
|       const monthNumber = getMonthNumber(persianMonth); | ||||
|       const monthDate = { day: 1, month: monthNumber, year: activeDate.year }; | ||||
|       const isAfterMaximumDate = | ||||
|         maximumDate && isBeforeDate(maximumDate, { ...monthDate, month: monthNumber }); | ||||
|       const isBeforeMinimumDate = | ||||
|         minimumDate && | ||||
|         (isBeforeDate({ ...monthDate, month: monthNumber + 1 }, minimumDate) || | ||||
|           isSameDay({ ...monthDate, month: monthNumber + 1 }, minimumDate)); | ||||
|       const isSelected = monthNumber === activeDate.month; | ||||
|       return ( | ||||
|         <li | ||||
|           key={persianMonth} | ||||
|           className={`Calendar__monthSelectorItem ${isSelected ? '-active' : ''}`} | ||||
|         > | ||||
|           <button | ||||
|             tabIndex={isSelected && isOpen ? '0' : '-1'} | ||||
|             onClick={() => { | ||||
|               onMonthSelect(monthNumber); | ||||
|             }} | ||||
|             className="Calendar__monthSelectorItemText" | ||||
|             type="button" | ||||
|             disabled={isAfterMaximumDate || isBeforeMinimumDate} | ||||
|             aria-pressed={isSelected} | ||||
|             data-is-default-selectable={isSelected} | ||||
|           > | ||||
|             {persianMonth} | ||||
|           </button> | ||||
|         </li> | ||||
|       ); | ||||
|     }); | ||||
|   return ( | ||||
|     <div | ||||
|       role="presentation" | ||||
|       className="Calendar__monthSelectorAnimationWrapper" | ||||
|       {...(isOpen ? {} : { 'aria-hidden': true })} | ||||
|     > | ||||
|       <div | ||||
|         role="presentation" | ||||
|         data-testid="month-selector-wrapper" | ||||
|         className="Calendar__monthSelectorWrapper" | ||||
|         onKeyDown={handleKeyDown} | ||||
|       > | ||||
|         <ul ref={monthSelector} className="Calendar__monthSelector" data-testid="month-selector"> | ||||
|           {renderMonthSelectorItems()} | ||||
|         </ul> | ||||
|       </div> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
| 
 | ||||
| export default MonthSelector; | ||||
| @ -0,0 +1,99 @@ | ||||
| import React, { useRef, useEffect } from 'react'; | ||||
| 
 | ||||
| import { MINIMUM_SELECTABLE_YEAR_SUBTRACT, MAXIMUM_SELECTABLE_YEAR_SUM } from '../shared/constants'; | ||||
| import handleKeyboardNavigation from '../shared/keyboardNavigation'; | ||||
| import { useLocaleUtils } from '../shared/hooks'; | ||||
| 
 | ||||
| const YearSelector = ({ | ||||
|   isOpen, | ||||
|   activeDate, | ||||
|   onYearSelect, | ||||
|   selectorStartingYear, | ||||
|   selectorEndingYear, | ||||
|   maximumDate, | ||||
|   minimumDate, | ||||
|   locale, | ||||
| }) => { | ||||
|   const wrapperElement = useRef(null); | ||||
|   const yearListElement = useRef(null); | ||||
| 
 | ||||
|   const { getLanguageDigits, getToday } = useLocaleUtils(locale); | ||||
|   const startingYearValue = | ||||
|     selectorStartingYear || getToday().year - MINIMUM_SELECTABLE_YEAR_SUBTRACT; | ||||
|   const endingYearValue = selectorEndingYear || getToday().year + MAXIMUM_SELECTABLE_YEAR_SUM; | ||||
|   const allYears = []; | ||||
|   for (let i = startingYearValue; i <= endingYearValue; i += 1) { | ||||
|     allYears.push(i); | ||||
|   } | ||||
|   useEffect(() => { | ||||
|     const classToggleMethod = isOpen ? 'add' : 'remove'; | ||||
|     const activeSelectorYear = wrapperElement.current.querySelector( | ||||
|       '.Calendar__yearSelectorItem.-active', | ||||
|     ); | ||||
|     if (!activeSelectorYear) { | ||||
|       throw new RangeError( | ||||
|         `Provided value for year is out of selectable year range. You're probably using a wrong locale prop value or your provided value's locale is different from the date picker locale. Try changing the 'locale' prop or the value you've provided.`, | ||||
|       ); | ||||
|     } | ||||
|     wrapperElement.current.classList[classToggleMethod]('-faded'); | ||||
|     yearListElement.current.scrollTop = | ||||
|       activeSelectorYear.offsetTop - activeSelectorYear.offsetHeight * 5; | ||||
|     yearListElement.current.classList[classToggleMethod]('-open'); | ||||
|   }, [isOpen]); | ||||
| 
 | ||||
|   const renderSelectorYears = () => { | ||||
|     return allYears.map(item => { | ||||
|       const isAfterMaximumDate = maximumDate && item > maximumDate.year; | ||||
|       const isBeforeMinimumDate = minimumDate && item < minimumDate.year; | ||||
|       const isSelected = activeDate.year === item; | ||||
|       return ( | ||||
|         <li key={item} className={`Calendar__yearSelectorItem ${isSelected ? '-active' : ''}`}> | ||||
|           <button | ||||
|             tabIndex={isSelected && isOpen ? '0' : '-1'} | ||||
|             className="Calendar__yearSelectorText" | ||||
|             type="button" | ||||
|             onClick={() => { | ||||
|               onYearSelect(item); | ||||
|             }} | ||||
|             disabled={isAfterMaximumDate || isBeforeMinimumDate} | ||||
|             aria-pressed={isSelected} | ||||
|             data-is-default-selectable={isSelected} | ||||
|           > | ||||
|             {getLanguageDigits(item)} | ||||
|           </button> | ||||
|         </li> | ||||
|       ); | ||||
|     }); | ||||
|   }; | ||||
| 
 | ||||
|   const handleKeyDown = e => { | ||||
|     handleKeyboardNavigation(e, { allowVerticalArrows: false }); | ||||
|   }; | ||||
| 
 | ||||
|   return ( | ||||
|     <div | ||||
|       className="Calendar__yearSelectorAnimationWrapper" | ||||
|       role="presentation" | ||||
|       {...(isOpen ? {} : { 'aria-hidden': true })} | ||||
|     > | ||||
|       <div | ||||
|         ref={wrapperElement} | ||||
|         className="Calendar__yearSelectorWrapper" | ||||
|         role="presentation" | ||||
|         data-testid="year-selector-wrapper" | ||||
|         onKeyDown={handleKeyDown} | ||||
|       > | ||||
|         <ul ref={yearListElement} className="Calendar__yearSelector" data-testid="year-selector"> | ||||
|           {renderSelectorYears()} | ||||
|         </ul> | ||||
|       </div> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
| 
 | ||||
| YearSelector.defaultProps = { | ||||
|   selectorStartingYear: 0, | ||||
|   selectorEndingYear: 0, | ||||
| }; | ||||
| 
 | ||||
| export default YearSelector; | ||||
| @ -0,0 +1,4 @@ | ||||
| export { default as Header } from './Header'; | ||||
| export { default as MonthSelector } from './MonthSelector'; | ||||
| export { default as YearSelector } from './YearSelector'; | ||||
| export { default as DaysList } from './DaysList'; | ||||
| @ -0,0 +1,3 @@ | ||||
| export { default } from './DatePicker'; | ||||
| export * from './Calendar'; | ||||
| export { default as utils } from './shared/localeUtils'; | ||||
| @ -0,0 +1,104 @@ | ||||
| export const PERSIAN_NUMBERS = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹']; | ||||
| 
 | ||||
| export const PERSIAN_MONTHS = [ | ||||
|   'فروردین', | ||||
|   'اردیبهشت', | ||||
|   'خرداد', | ||||
|   'تیر', | ||||
|   'مرداد', | ||||
|   'شهریور', | ||||
|   'مهر', | ||||
|   'آبان', | ||||
|   'آذر', | ||||
|   'دی', | ||||
|   'بهمن', | ||||
|   'اسفند', | ||||
| ]; | ||||
| 
 | ||||
| export const GREGORIAN_MONTHS = [ | ||||
|   'January', | ||||
|   'February', | ||||
|   'March', | ||||
|   'April', | ||||
|   'May', | ||||
|   'June', | ||||
|   'July', | ||||
|   'August', | ||||
|   'September', | ||||
|   'October', | ||||
|   'November', | ||||
|   'December', | ||||
| ]; | ||||
| 
 | ||||
| export const PERSIAN_WEEK_DAYS = [ | ||||
|   { | ||||
|     name: 'شنبه', | ||||
|     short: 'ش', | ||||
|   }, | ||||
|   { | ||||
|     name: 'یکشنبه', | ||||
|     short: 'ی', | ||||
|   }, | ||||
|   { | ||||
|     name: 'دوشنبه', | ||||
|     short: 'د', | ||||
|   }, | ||||
|   { | ||||
|     name: 'سه شنبه', | ||||
|     short: 'س', | ||||
|   }, | ||||
|   { | ||||
|     name: 'چهارشنبه', | ||||
|     short: 'چ', | ||||
|   }, | ||||
|   { | ||||
|     name: 'پنجشنبه', | ||||
|     short: 'پ', | ||||
|   }, | ||||
|   { | ||||
|     name: 'جمعه', | ||||
|     short: 'ج', | ||||
|     isWeekend: true, | ||||
|   }, | ||||
| ]; | ||||
| 
 | ||||
| export const GREGORIAN_WEEK_DAYS = [ | ||||
|   { | ||||
|     name: 'Sunday', | ||||
|     short: 'S', | ||||
|     isWeekend: true, | ||||
|   }, | ||||
|   { | ||||
|     name: 'Monday', | ||||
|     short: 'M', | ||||
|   }, | ||||
|   { | ||||
|     name: 'Tuesday', | ||||
|     short: 'T', | ||||
|   }, | ||||
|   { | ||||
|     name: 'Wednesday', | ||||
|     short: 'W', | ||||
|   }, | ||||
|   { | ||||
|     name: 'Thursday', | ||||
|     short: 'T', | ||||
|   }, | ||||
|   { | ||||
|     name: 'Friday', | ||||
|     short: 'F', | ||||
|   }, | ||||
|   { | ||||
|     name: 'Saturday', | ||||
|     short: 'S', | ||||
|     isWeekend: true, | ||||
|   }, | ||||
| ]; | ||||
| 
 | ||||
| export const MINIMUM_SELECTABLE_YEAR_SUBTRACT = 100; | ||||
| 
 | ||||
| export const MAXIMUM_SELECTABLE_YEAR_SUM = 50; | ||||
| 
 | ||||
| export const TYPE_SINGLE_DATE = 'SINGLE_DATE'; | ||||
| export const TYPE_RANGE = 'RANGE'; | ||||
| export const TYPE_MUTLI_DATE = 'MUTLI_DATE'; | ||||
| @ -0,0 +1,69 @@ | ||||
| import { TYPE_SINGLE_DATE, TYPE_RANGE, TYPE_MUTLI_DATE } from './constants'; | ||||
| 
 | ||||
| /* | ||||
|   These utility functions don't depend on locale of the date picker(Persian or Gregorian) | ||||
| */ | ||||
| 
 | ||||
| const createUniqueRange = (number, startingId) => | ||||
|   Array.from(Array(number).keys()).map(key => ({ | ||||
|     value: key + 1, | ||||
|     id: `${startingId}-${key}`, | ||||
|   })); | ||||
| 
 | ||||
| const isSameDay = (day1, day2) => { | ||||
|   if (!day1 || !day2) return false; | ||||
|   return day1.day === day2.day && day1.month === day2.month && day1.year === day2.year; | ||||
| }; | ||||
| 
 | ||||
| const putZero = number => (number.toString().length === 1 ? `0${number}` : number); | ||||
| 
 | ||||
| const toExtendedDay = date => [date.year, date.month, date.day]; | ||||
| 
 | ||||
| const shallowClone = value => ({ ...value }); | ||||
| 
 | ||||
| const deepCloneObject = obj => | ||||
|   JSON.parse(JSON.stringify(obj, (key, value) => (typeof value === 'undefined' ? null : value))); | ||||
| 
 | ||||
| const getDateAccordingToMonth = (date, direction) => { | ||||
|   const toSum = direction === 'NEXT' ? 1 : -1; | ||||
|   let newMonthIndex = date.month + toSum; | ||||
|   let newYear = date.year; | ||||
|   if (newMonthIndex < 1) { | ||||
|     newMonthIndex = 12; | ||||
|     newYear -= 1; | ||||
|   } | ||||
|   if (newMonthIndex > 12) { | ||||
|     newMonthIndex = 1; | ||||
|     newYear += 1; | ||||
|   } | ||||
|   const newDate = { year: newYear, month: newMonthIndex, day: 1 }; | ||||
|   return newDate; | ||||
| }; | ||||
| 
 | ||||
| const hasProperty = (object, propertyName) => | ||||
|   Object.prototype.hasOwnProperty.call(object || {}, propertyName); | ||||
| 
 | ||||
| const getValueType = value => { | ||||
|   if (Array.isArray(value)) return TYPE_MUTLI_DATE; | ||||
|   if (hasProperty(value, 'from') && hasProperty(value, 'to')) return TYPE_RANGE; | ||||
|   if ( | ||||
|     !value || | ||||
|     (hasProperty(value, 'year') && hasProperty(value, 'month') && hasProperty(value, 'day')) | ||||
|   ) { | ||||
|     return TYPE_SINGLE_DATE; | ||||
|   } | ||||
|   throw new TypeError( | ||||
|     `The passed value is malformed! Please make sure you're using one of the valid value types for date picker.`, | ||||
|   ); | ||||
| }; | ||||
| 
 | ||||
| export { | ||||
|   createUniqueRange, | ||||
|   isSameDay, | ||||
|   putZero, | ||||
|   toExtendedDay, | ||||
|   shallowClone, | ||||
|   deepCloneObject, | ||||
|   getDateAccordingToMonth, | ||||
|   getValueType, | ||||
| }; | ||||
| @ -0,0 +1,10 @@ | ||||
| import { useMemo } from 'react'; | ||||
| 
 | ||||
| import utils from './localeUtils'; | ||||
| import getLanguageText from './localeLanguages'; | ||||
| 
 | ||||
| const useLocaleUtils = locale => useMemo(() => utils(locale), [locale]); | ||||
| 
 | ||||
| const useLocaleLanguage = locale => useMemo(() => getLanguageText(locale), [locale]); | ||||
| 
 | ||||
| export { useLocaleUtils, useLocaleLanguage }; | ||||
| @ -0,0 +1,44 @@ | ||||
| const handleArrowKeys = (e, { allowVerticalArrows }) => { | ||||
|   const { activeElement } = document; | ||||
|   const getNthChildSafe = (element, index) => (element ? element.children[index] : null); | ||||
|   const getStandardItem = item => item && (item.hasAttribute('aria-hidden') ? null : item); | ||||
|   const { nextSibling: nextRow, previousSibling: previousRow } = activeElement.parentElement; | ||||
|   const nextSibling = getStandardItem(activeElement.nextSibling || getNthChildSafe(nextRow, 0)); | ||||
|   const previousRowLength = previousRow ? previousRow.children.length - 1 : 0; | ||||
|   const previousSibling = getStandardItem( | ||||
|     activeElement.previousSibling || getNthChildSafe(previousRow, previousRowLength), | ||||
|   ); | ||||
|   const getVerticalSibling = row => | ||||
|     getNthChildSafe(row, Array.from(activeElement.parentElement.children).indexOf(activeElement)); | ||||
|   const downSibling = getStandardItem(getVerticalSibling(nextRow)); | ||||
|   const upSibling = getStandardItem(getVerticalSibling(previousRow)); | ||||
|   const isDefaultSelectable = activeElement.dataset.isDefaultSelectable === 'true'; | ||||
| 
 | ||||
|   if (!isDefaultSelectable) activeElement.tabIndex = '-1'; | ||||
|   const focusIfAvailable = element => { | ||||
|     e.preventDefault(); | ||||
|     /* istanbul ignore else */ | ||||
|     if (element) { | ||||
|       element.setAttribute('tabindex', '0'); | ||||
|       element.focus(); | ||||
|     } | ||||
|   }; | ||||
|   switch (e.key) { | ||||
|     case 'ArrowRight': | ||||
|       focusIfAvailable(nextSibling); | ||||
|       break; | ||||
|     case 'ArrowLeft': | ||||
|       focusIfAvailable(previousSibling); | ||||
|       break; | ||||
|     case 'ArrowDown': | ||||
|       /* istanbul ignore else */ | ||||
|       if (allowVerticalArrows) focusIfAvailable(downSibling); | ||||
|       break; | ||||
|     case 'ArrowUp': | ||||
|       /* istanbul ignore else */ | ||||
|       if (allowVerticalArrows) focusIfAvailable(upSibling); | ||||
|       break; | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| export default handleArrowKeys; | ||||
| @ -0,0 +1,85 @@ | ||||
| import jalaali from 'jalaali-js'; | ||||
| 
 | ||||
| import { | ||||
|   GREGORIAN_MONTHS, | ||||
|   PERSIAN_MONTHS, | ||||
|   GREGORIAN_WEEK_DAYS, | ||||
|   PERSIAN_WEEK_DAYS, | ||||
|   PERSIAN_NUMBERS, | ||||
| } from './constants'; | ||||
| import { toExtendedDay } from './generalUtils'; | ||||
| 
 | ||||
| const localeLanguages = { | ||||
|   en: { | ||||
|     months: GREGORIAN_MONTHS, | ||||
|     weekDays: GREGORIAN_WEEK_DAYS, | ||||
|     weekStartingIndex: 0, | ||||
|     getToday(gregorainTodayObject) { | ||||
|       return gregorainTodayObject; | ||||
|     }, | ||||
|     toNativeDate(date) { | ||||
|       return new Date(date.year, date.month - 1, date.day); | ||||
|     }, | ||||
|     getMonthLength(date) { | ||||
|       return new Date(date.year, date.month, 0).getDate(); | ||||
|     }, | ||||
|     transformDigit(digit) { | ||||
|       return digit; | ||||
|     }, | ||||
|     nextMonth: 'Next Month', | ||||
|     previousMonth: 'Previous Month', | ||||
|     openMonthSelector: 'Open Month Selector', | ||||
|     openYearSelector: 'Open Year Selector', | ||||
|     closeMonthSelector: 'Close Month Selector', | ||||
|     closeYearSelector: 'Close Year Selector', | ||||
|     from: 'from', | ||||
|     to: 'to', | ||||
|     defaultPlaceholder: 'Select...', | ||||
|     digitSeparator: ',', | ||||
|     yearLetterSkip: 0, | ||||
|     isRtl: false, | ||||
|   }, | ||||
|   fa: { | ||||
|     months: PERSIAN_MONTHS, | ||||
|     weekDays: PERSIAN_WEEK_DAYS, | ||||
|     weekStartingIndex: 1, | ||||
|     getToday({ year, month, day }) { | ||||
|       const { jy, jm, jd } = jalaali.toJalaali(year, month, day); | ||||
|       return { year: jy, month: jm, day: jd }; | ||||
|     }, | ||||
|     toNativeDate(date) { | ||||
|       const gregorian = jalaali.toGregorian(...toExtendedDay(date)); | ||||
|       return new Date(gregorian.gy, gregorian.gm - 1, gregorian.gd); | ||||
|     }, | ||||
|     getMonthLength(date) { | ||||
|       return jalaali.jalaaliMonthLength(date.year, date.month); | ||||
|     }, | ||||
|     transformDigit(digit) { | ||||
|       return digit | ||||
|         .toString() | ||||
|         .split('') | ||||
|         .map(letter => PERSIAN_NUMBERS[Number(letter)]) | ||||
|         .join(''); | ||||
|     }, | ||||
|     nextMonth: 'ماه بعد', | ||||
|     previousMonth: 'ماه قبل', | ||||
|     openMonthSelector: 'نمایش انتخابگر ماه', | ||||
|     openYearSelector: 'نمایش انتخابگر سال', | ||||
|     closeMonthSelector: 'بستن انتخابگر ماه', | ||||
|     closeYearSelector: 'بستن انتخابگر ماه', | ||||
|     from: 'از', | ||||
|     to: 'تا', | ||||
|     defaultPlaceholder: 'انتخاب...', | ||||
|     digitSeparator: '،', | ||||
|     yearLetterSkip: -2, | ||||
|     isRtl: true, | ||||
|   }, | ||||
| }; | ||||
| 
 | ||||
| const getLocaleDetails = locale => { | ||||
|   if (typeof locale === 'string') return localeLanguages[locale]; | ||||
|   return locale; | ||||
| }; | ||||
| 
 | ||||
| export { localeLanguages }; | ||||
| export default getLocaleDetails; | ||||
| @ -0,0 +1,61 @@ | ||||
| /* | ||||
|   These utility functions highly depend on locale of the date picker(Persian or Gregorian) | ||||
| */ | ||||
| 
 | ||||
| import getLocaleDetails from './localeLanguages'; | ||||
| 
 | ||||
| const utils = (locale = 'en') => { | ||||
|   const { | ||||
|     months: monthsList, | ||||
|     getToday: localeGetToday, | ||||
|     toNativeDate, | ||||
|     getMonthLength, | ||||
|     weekStartingIndex, | ||||
|     transformDigit: getLanguageDigits, | ||||
|   } = typeof locale === 'string' ? getLocaleDetails(locale) : locale; | ||||
| 
 | ||||
|   const getToday = () => { | ||||
|     const todayDate = new Date(); | ||||
|     const year = todayDate.getFullYear(); | ||||
|     const month = todayDate.getMonth() + 1; | ||||
|     const day = todayDate.getDate(); | ||||
|     return localeGetToday({ year, month, day }); | ||||
|   }; | ||||
| 
 | ||||
|   const getMonthName = month => monthsList[month - 1]; | ||||
| 
 | ||||
|   const getMonthNumber = monthName => monthsList.indexOf(monthName) + 1; | ||||
| 
 | ||||
|   const getMonthFirstWeekday = date => { | ||||
|     const gregorianDate = toNativeDate({ ...date, day: 1 }); | ||||
|     const weekday = gregorianDate.getDay(); | ||||
|     const dayIndex = weekday + weekStartingIndex; | ||||
|     return dayIndex % 7; | ||||
|   }; | ||||
| 
 | ||||
|   const isBeforeDate = (day1, day2) => { | ||||
|     if (!day1 || !day2) return false; | ||||
|     return toNativeDate(day1) < toNativeDate(day2); | ||||
|   }; | ||||
| 
 | ||||
|   const checkDayInDayRange = ({ day, from, to }) => { | ||||
|     if (!day || !from || !to) return false; | ||||
|     const nativeDay = toNativeDate(day); | ||||
|     const nativeFrom = toNativeDate(from); | ||||
|     const nativeTo = toNativeDate(to); | ||||
|     return nativeDay > nativeFrom && nativeDay < nativeTo; | ||||
|   }; | ||||
| 
 | ||||
|   return { | ||||
|     getToday, | ||||
|     getMonthName, | ||||
|     getMonthNumber, | ||||
|     getMonthLength, | ||||
|     getMonthFirstWeekday, | ||||
|     isBeforeDate, | ||||
|     checkDayInDayRange, | ||||
|     getLanguageDigits, | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| export default utils; | ||||
| @ -0,0 +1,33 @@ | ||||
| import { getDateAccordingToMonth } from './generalUtils'; | ||||
| 
 | ||||
| const getSlideDate = ({ parent, isInitialActiveChild, activeDate, monthChangeDirection }) => { | ||||
|   if (!parent) { | ||||
|     return isInitialActiveChild ? activeDate : getDateAccordingToMonth(activeDate, 'NEXT'); | ||||
|   } | ||||
|   const child = parent.children[isInitialActiveChild ? 0 : 1]; | ||||
|   const isActiveSlide = | ||||
|     child.classList.contains('-shown') || child.classList.contains('-shownAnimated'); // check -shownAnimated for Safari bug
 | ||||
|   return isActiveSlide ? activeDate : getDateAccordingToMonth(activeDate, monthChangeDirection); | ||||
| }; | ||||
| 
 | ||||
| const animateContent = ({ parent, direction }) => { | ||||
|   const wrapperChildren = Array.from(parent.children); | ||||
|   const shownItem = wrapperChildren.find(child => child.classList.contains('-shown')); | ||||
|   const hiddenItem = wrapperChildren.find(child => child !== shownItem); | ||||
|   const baseClass = shownItem.classList[0]; | ||||
|   const isNextMonth = direction === 'NEXT'; | ||||
|   const getAnimationClass = value => (value ? '-hiddenNext' : '-hiddenPrevious'); | ||||
|   hiddenItem.style.transition = 'none'; | ||||
|   shownItem.style.transition = ''; | ||||
|   shownItem.className = `${baseClass} ${getAnimationClass(!isNextMonth)}`; | ||||
|   hiddenItem.className = `${baseClass} ${getAnimationClass(isNextMonth)}`; | ||||
|   hiddenItem.classList.add('-shownAnimated'); | ||||
| }; | ||||
| 
 | ||||
| const handleSlideAnimationEnd = ({ target }) => { | ||||
|   target.classList.remove('-hiddenNext'); | ||||
|   target.classList.remove('-hiddenPrevious'); | ||||
|   target.classList.replace('-shownAnimated', '-shown'); | ||||
| }; | ||||
| 
 | ||||
| export { animateContent, getSlideDate, handleSlideAnimationEnd }; | ||||
| @ -0,0 +1,710 @@ | ||||
| .DatePicker { | ||||
|     position: relative; | ||||
|     display: inline-block; | ||||
|     z-index: 100; | ||||
|   } | ||||
|    | ||||
|   .DatePicker__input { | ||||
|     background: #fff; | ||||
|     border: 1px solid rgba(0, 0, 0, 0.4); | ||||
|     padding: 0.6em 1.2em; | ||||
|     font-family:'iranSans'; | ||||
|     text-align: center; | ||||
|     font-size: 12px; | ||||
|     border-radius: 7px; | ||||
|      | ||||
|   } | ||||
|   .DatePicker__input:focus{ | ||||
|     outline:1px solid #06BACE; | ||||
|     color: #06BACE; | ||||
|   } | ||||
|    | ||||
|   .DatePicker__input.-rtl { | ||||
|     direction: rtl; | ||||
|   } | ||||
|    | ||||
|   .DatePicker__input::placeholder { | ||||
|     color: #979797; | ||||
|   } | ||||
|   .DatePicker__input:focus::placeholder{ | ||||
|     color: #06BACE; | ||||
|   } | ||||
|    | ||||
|   .DatePicker__calendarContainer.-top + .DatePicker__calendarArrow { | ||||
|     top: auto; | ||||
|     bottom: calc(100% + 10px); | ||||
|     transform: translateY(-2.5rem) rotate(180deg); | ||||
|     animation: fadeArrowFlipped 0.3s forwards; | ||||
|   } | ||||
|    | ||||
|   .DatePicker__calendarContainer { | ||||
|     position: absolute; | ||||
|     top: calc(100% + 20px); | ||||
|     left: 50%; | ||||
|     transform: translateX(-50%); | ||||
|   } | ||||
|    | ||||
|   .DatePicker__calendarContainer.-top { | ||||
|     top: auto; | ||||
|     bottom: calc(100% + 20px); | ||||
|   } | ||||
|    | ||||
|   .range-date-picker, | ||||
|   .range-date-picker * { | ||||
|     margin: 0; | ||||
|     padding: 0; | ||||
|     box-sizing: border-box; | ||||
|     direction: ltr; | ||||
|   } | ||||
|    | ||||
|   .range-date-picker, | ||||
|   .range-date-picker.-rtl * { | ||||
|     direction: rtl; | ||||
|   } | ||||
|    | ||||
|   .DatePicker__calendarArrow { | ||||
|     position: absolute; | ||||
|     width: 0; | ||||
|     height: 0; | ||||
|     top: calc(100% + 10px); | ||||
|     left: 0; | ||||
|     right: 0; | ||||
|     margin: 0 auto; | ||||
|     border-style: solid; | ||||
|     z-index: 10; | ||||
|     border-width: 0 10px 10px 10px; | ||||
|     border-color: transparent transparent #fff transparent; | ||||
|      | ||||
|   } | ||||
|    | ||||
|   .range-date-picker { | ||||
|     --cl-color-black: #444444; | ||||
|     --cl-color-disabled: #d4d4d4; | ||||
|     --cl-color-error: #ff2929; | ||||
|     font-family:'iranSans'; | ||||
|     font-size: 10px; | ||||
|     background: rgba(245, 245, 245, 0.53); | ||||
|     border:1px solid #DBDBDB; | ||||
|     box-shadow: 0 1em em rgba(0, 0, 0, 0.07); | ||||
|     border-radius: 1em; | ||||
|     position: relative; | ||||
|     user-select: none; | ||||
|     padding-top: 1.2em; | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     width: 40em; | ||||
|     z-index: 10; | ||||
|     max-width: 90vw; | ||||
|     min-height: 38.7em; | ||||
|   } | ||||
|    | ||||
|   .DatePicker .range-date-picker, | ||||
|   .DatePicker__calendarArrow { | ||||
|     transform: translateY(2.5em); | ||||
|     opacity: 0; | ||||
|     animation: fadeCalendar 0.3s forwards; | ||||
|      | ||||
|   } | ||||
|    | ||||
|   .DatePicker__calendarContainer.-top .range-date-picker { | ||||
|     transform: translateY(-2.5em); | ||||
|   } | ||||
|    | ||||
|   .range-date-picker.-noFocusOutline *:focus { | ||||
|     outline: none !important; | ||||
|   } | ||||
|    | ||||
|   .range-date-picker > :not(.Calendar__footer) button { | ||||
|     font-family: inherit; | ||||
|     background: transparent; | ||||
|     cursor: pointer; | ||||
|     -webkit-tap-highlight-color: transparent; | ||||
|     outline: none; | ||||
|   } | ||||
|    | ||||
|   .Calendar__header { | ||||
|     display: flex; | ||||
|     color: var(--cl-color-black); | ||||
|     padding: 2em 2.9em; | ||||
|     align-items: center; | ||||
|     overflow: hidden; | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthArrowWrapper { | ||||
|     line-height: 0; | ||||
|     font-size: 1em; | ||||
|     padding: 3px; | ||||
|     position: relative; | ||||
|     border: none; | ||||
|     z-index: 1; | ||||
|     opacity: 1; | ||||
|     transition: 0.2s; | ||||
|     background-color: #06BACE; | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthArrowWrapper:focus { | ||||
|     outline: 1px dashed rgba(0, 0, 0, 0.4); | ||||
|     outline-offset: 2px; | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthArrowWrapper:disabled, | ||||
|   .Calendar__monthArrowWrapper.-hidden { | ||||
|     opacity: 0; | ||||
|     pointer-events: none; | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthArrowWrapper.-left { | ||||
|     transform: rotate(180deg); | ||||
|   } | ||||
|   .range-date-picker.-rtl .Calendar__monthArrowWrapper.-left { | ||||
|     transform: rotate(-180deg); | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthArrowWrapper.-right { | ||||
|     transform: rotate(-0deg); | ||||
|      | ||||
|   } | ||||
|   .range-date-picker.-rtl .Calendar__monthArrowWrapper.-right { | ||||
|     transform: rotate(0deg); | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthArrowWrapper:active .Calendar__monthArrow { | ||||
|     transform: scale(0.7); | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthArrow { | ||||
|     border-radius: 7px; | ||||
|     padding: 12px; | ||||
|     transition: var(--animation-duration) transform; | ||||
|     pointer-events: none; | ||||
|     background-repeat: no-repeat; | ||||
|     display: block; | ||||
|     width: 0.7em; | ||||
|     height: 0.7em; | ||||
|     background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='15' height='15' viewBox='0 0 50 80' xml:space='preserve'%3E%3Cpolyline fill='none' stroke='%23FFFFFF' stroke-width='15' stroke-linecap='round' stroke-linejoin='round' points=' 0.375,0.375 45.63,38.087 0.375,75.8 '/%3E%3C/svg%3E");    background-size: 100% 100%; | ||||
|     background-color: #06BACE; | ||||
|     background-size: 10px; | ||||
|     background-position: center; | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthYearContainer { | ||||
|     flex: 1; | ||||
|     position: relative; | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthYear { | ||||
|     font-size: 2em; | ||||
|     font-weight: 500; | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|     position: absolute; | ||||
|     top: 0; | ||||
|     bottom: 0; | ||||
|     left: 50%; | ||||
|     will-change: transform, opacity; | ||||
|     backface-visibility: hidden; | ||||
|     transform: translateZ(0); | ||||
|     transition: var(--animation-duration); | ||||
|     line-height: 1; | ||||
|     color:#246E8A; | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthYear.-hiddenNext { | ||||
|     opacity: 0; | ||||
|     transform: translateX(50%); | ||||
|   } | ||||
|    | ||||
|   .range-date-picker.-rtl .Calendar__monthYear.-hiddenNext { | ||||
|     transform: translateX(-150%); | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthYear.-hiddenPrevious { | ||||
|     opacity: 0; | ||||
|     transform: translateX(-150%); | ||||
|   } | ||||
|    | ||||
|   .range-date-picker.-rtl .Calendar__monthYear.-hiddenPrevious { | ||||
|     transform: translateX(50%); | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthYear.-shown { | ||||
|     opacity: 1; | ||||
|     margin-top: auto; | ||||
|     margin-bottom: auto; | ||||
|     transform: translateX(-50%); | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthYear.-shownAnimated { | ||||
|     animation: var(--animation-duration) fadeTextToCenter forwards; | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthYear > * { | ||||
|     padding: 0.2em 0.5em; | ||||
|     border: 1px solid transparent; | ||||
|     transition: var(--animation-duration); | ||||
|     font-size: 1.05em; | ||||
|     display: flex; | ||||
|     justify-content: center; | ||||
|     align-items: center; | ||||
|     transform: translateX(0) scale(0.95); | ||||
|     will-change: transform; | ||||
|     border-radius: 5px; | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthYear:not(.-shown) > *, | ||||
|   .Calendar__monthYear > *.-hidden { | ||||
|     cursor: default; | ||||
|     pointer-events: none; | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthText { | ||||
|     margin-left: -0.3em; | ||||
|   } | ||||
|   .Calendar__yearText:last-child { | ||||
|     margin-right: -0.3em; | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthYear.-shown > *:hover, | ||||
|   .range-date-picker:not(.-noFocusOutline) .Calendar__monthYear.-shown > *:focus, | ||||
|   .Calendar__monthYear > *.-activeBackground { | ||||
|     background: #f5f5f5; | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthText:hover { | ||||
|     transform: translateX(-0.2em) scale(0.95); | ||||
|   } | ||||
|   .range-date-picker.-rtl .Calendar__monthText:hover { | ||||
|     transform: translateX(0.2em) scale(0.95); | ||||
|   } | ||||
|    | ||||
|   .Calendar__yearText:hover { | ||||
|     transform: translateX(0.2em) scale(0.95); | ||||
|   } | ||||
|   .range-date-picker.-rtl .Calendar__yearText:hover { | ||||
|     transform: translateX(-0.2em) scale(0.95); | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthYear .Calendar__yearText.-hidden { | ||||
|     transform: translateX(50%); | ||||
|     opacity: 0; | ||||
|   } | ||||
|    | ||||
|   .range-date-picker.-rtl .Calendar__monthYear .Calendar__yearText.-hidden { | ||||
|     transform: translateX(-50%); | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthYear .Calendar__monthText.-hidden { | ||||
|     transform: translateX(-50%); | ||||
|     opacity: 0; | ||||
|   } | ||||
|    | ||||
|   .range-date-picker.-rtl .Calendar__monthYear .Calendar__monthText.-hidden { | ||||
|     transform: translateX(50%); | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthYear:not(.-shown) > * { | ||||
|     pointer-events: none; | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthSelectorAnimationWrapper, | ||||
|   .Calendar__yearSelectorAnimationWrapper { | ||||
|     position: absolute; | ||||
|     width: 100%; | ||||
|     height: 80%; | ||||
|     bottom: 0; | ||||
|     display: flex; | ||||
|     justify-content: center; | ||||
|     align-items: center; | ||||
|     overflow: hidden; | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthSelectorWrapper { | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|     display: flex; | ||||
|     justify-content: center; | ||||
|     align-items: center; | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthSelector { | ||||
|     padding: 0 2.5em; | ||||
|     align-content: center; | ||||
|     padding-bottom: 2em; | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthSelector, | ||||
|   .Calendar__yearSelector { | ||||
|     display: flex; | ||||
|     flex-wrap: wrap; | ||||
|     position: relative; | ||||
|     z-index: 2; | ||||
|     background-color: #fff; | ||||
|     transform: translateY(-150%); | ||||
|     will-change: transform; | ||||
|     transition: 0.6s; | ||||
|     height: 100%; | ||||
|   } | ||||
|    | ||||
|   .Calendar__yearSelectorWrapper { | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|   } | ||||
|    | ||||
|   .Calendar__yearSelectorWrapper::after, | ||||
|   .Calendar__yearSelectorWrapper::before { | ||||
|     content: ''; | ||||
|     width: 100%; | ||||
|     height: 5em; | ||||
|     position: absolute; | ||||
|     left: 0; | ||||
|     opacity: 0; | ||||
|     transition: 0.4s; | ||||
|     transition-delay: 0.2s; | ||||
|   } | ||||
|    | ||||
|   .Calendar__yearSelectorWrapper::after { | ||||
|     background-image: linear-gradient(to bottom, #fff, #fff 10%, rgba(245, 245, 245, 0)); | ||||
|     top: -0.1em; | ||||
|   } | ||||
|    | ||||
|   .Calendar__yearSelectorWrapper::before { | ||||
|     background-image: linear-gradient(to top, #fff, #fff 10%, rgba(245, 245, 245, 0)); | ||||
|     bottom: 0; | ||||
|   } | ||||
|    | ||||
|   .Calendar__yearSelectorWrapper.-faded::after, | ||||
|   .Calendar__yearSelectorWrapper.-faded::before { | ||||
|     opacity: 1; | ||||
|     z-index: 3; | ||||
|   } | ||||
|    | ||||
|   .Calendar__yearSelector { | ||||
|     align-content: flex-start; | ||||
|     scrollbar-width: 0; | ||||
|     overflow: scroll; | ||||
|     position: relative; | ||||
|     width: 100%; | ||||
|     padding: 5em 2em; | ||||
|     -ms-overflow-style: none; | ||||
|   } | ||||
|    | ||||
|   .Calendar__yearSelector::-webkit-scrollbar { | ||||
|     display: none; | ||||
|   } | ||||
|    | ||||
|   .Calendar__yearSelectorItem { | ||||
|     width: 25%; | ||||
|     display: flex; | ||||
|     justify-content: center; | ||||
|   } | ||||
|    | ||||
|   .Calendar__yearSelectorItem:not(:nth-child(-n + 4)) { | ||||
|     margin-top: 1.5em; | ||||
|   } | ||||
|    | ||||
|   .Calendar__yearSelectorText { | ||||
|     border: none; | ||||
|     font-size: 1.4em; | ||||
|     min-width: 85%; | ||||
|     padding: 0.2em 0.5em; | ||||
|     border-radius: 5px; | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthSelector.-open, | ||||
|   .Calendar__yearSelector.-open { | ||||
|     transform: translateY(0); | ||||
|   } | ||||
|    | ||||
|   .Calendar__yearSelectorText:focus, | ||||
|   .Calendar__monthSelectorItemText:focus { | ||||
|     outline: 1px dashed rgba(0, 0, 0, 0.4); | ||||
|     outline-offset: 2px; | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthSelectorItem { | ||||
|     width: calc(100% / 3); | ||||
|     display: flex; | ||||
|     justify-content: center; | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthSelectorItem:not(:nth-child(-n + 3)) { | ||||
|     margin-top: 2em; | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthSelectorItemText { | ||||
|     border: none; | ||||
|     padding: 0.4em 0.4em; | ||||
|     border-radius: 8.5px; | ||||
|     font-size: 1.3em; | ||||
|     min-width: 70%; | ||||
|     transition: 0.3s; | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthSelectorItem:not(.-active) .Calendar__monthSelectorItemText:not(:disabled):hover, | ||||
|   .Calendar__yearSelectorItem:not(.-active) .Calendar__yearSelectorText:not(:disabled):hover { | ||||
|     background: #f5f5f5; | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthSelectorItemText:disabled, | ||||
|   .Calendar__yearSelectorText:disabled { | ||||
|     opacity: 0.5; | ||||
|     cursor: default; | ||||
|   } | ||||
|    | ||||
|   .Calendar__monthSelectorItem.-active .Calendar__monthSelectorItemText, | ||||
|   .Calendar__yearSelectorItem.-active .Calendar__yearSelectorText { | ||||
|     background-color: #006C94; | ||||
|     color: #fff; | ||||
|   } | ||||
|    | ||||
|   .Calendar__weekDays { | ||||
|     display: flex; | ||||
|     justify-content: space-between; | ||||
|     color: var(--cl-color-disabled); | ||||
|     font-size: 1.2em; | ||||
|     margin-bottom: 0.7em; | ||||
|     padding: 0 2.6em; | ||||
|     position: relative; | ||||
|   } | ||||
|    | ||||
|   .Calendar__weekDay { | ||||
|     display: block; | ||||
|     width: calc(100% / 7); | ||||
|      | ||||
|     text-align: center; | ||||
|     text-decoration: none; | ||||
|     border: 0px; | ||||
|     color: #06BACE; | ||||
|     margin:0.2em; | ||||
|     white-space: nowrap; | ||||
|   } | ||||
|   .Calendar__weekDay[title]{ | ||||
|     text-decoration: none; | ||||
|   } | ||||
|    | ||||
|   .Calendar__sectionWrapper { | ||||
|     position: relative; | ||||
|     min-height: 25.8em; | ||||
|     overflow: hidden; | ||||
|   } | ||||
|    | ||||
|   .Calendar__section { | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     padding: 0 3.2em; | ||||
|     position: absolute; | ||||
|     color: var(--cl-color-black); | ||||
|     top: 0; | ||||
|     padding-top: 0.5em; | ||||
|     left: 0; | ||||
|     width: 100%; | ||||
|     will-change: transform, opacity; | ||||
|     transform: translateZ(0); | ||||
|     backface-visibility: hidden; | ||||
|     transition: var(--animation-duration); | ||||
|   } | ||||
|    | ||||
|   .Calendar__section.-hiddenPrevious { | ||||
|     opacity: 0.5; | ||||
|     transform: translateX(-90%); | ||||
|   } | ||||
|    | ||||
|   .range-date-picker.-rtl .Calendar__section.-hiddenPrevious { | ||||
|     transform: translateX(90%); | ||||
|   } | ||||
|    | ||||
|   .Calendar__section.-hiddenNext { | ||||
|     opacity: 0.5; | ||||
|     transform: translateX(90%); | ||||
|   } | ||||
|    | ||||
|   .range-date-picker.-rtl .Calendar__section.-hiddenNext { | ||||
|     transform: translateX(-90%); | ||||
|   } | ||||
|    | ||||
|   .Calendar__section.-shown { | ||||
|     opacity: 1; | ||||
|     transform: translateX(0); | ||||
|   } | ||||
|    | ||||
|   .Calendar__section.-shownAnimated { | ||||
|     animation: var(--animation-duration) FadeContentToCenter forwards; | ||||
|   } | ||||
|    | ||||
|   .Calendar__weekRow { | ||||
|     display: flex; | ||||
|     width: 100%; | ||||
|   } | ||||
|    | ||||
|   .range-date-picker .Calendar__section .Calendar__weekRow .Calendar__day { | ||||
|     display: block; | ||||
|     width: calc(100% / 7); | ||||
|     text-align: center; | ||||
|     padding: calc(0.25em - 1px); | ||||
|     font-size: 1.6em; | ||||
|     transition: 0.2s; | ||||
|     border: 1px solid transparent; | ||||
|     color: rgba(0, 0, 0, 0.8); | ||||
|     display: flex; | ||||
|     justify-content: center; | ||||
|     align-items: center; | ||||
|     cursor: pointer; | ||||
|     border-radius: 7px; | ||||
|      | ||||
|     | ||||
|   } | ||||
|    | ||||
|   .Calendar__day:focus { | ||||
|     outline: 1px dashed rgba(0, 0, 0, 0.4); | ||||
|     outline-offset: 2px; | ||||
|   } | ||||
|    | ||||
|   .Calendar__day.-ltr { | ||||
|     min-height: 2.6em; | ||||
|     font-size: 1.45em; | ||||
|   } | ||||
|    | ||||
|   .Calendar__day.-rtl { | ||||
|     font-size: 1.55em; | ||||
|     height: 2.45em; | ||||
|   } | ||||
|    | ||||
|   .range-date-picker .Calendar__section .Calendar__weekRow .Calendar__day:not(.-blank):not(.-selectedStart):not(.-selectedEnd):not(.-selectedBetween):not(.-selected):hover { | ||||
|      | ||||
|     | ||||
|     color: #06BACE; | ||||
|     border:1px solid #06BACE; | ||||
|   } | ||||
|    | ||||
|   .Calendar__day.-selected, | ||||
|   .Calendar__day.-selectedStart, | ||||
|   .Calendar__day.-selectedEnd { | ||||
|     background: #006C94; | ||||
|     color: #fff; | ||||
|   } | ||||
|    | ||||
|   .Calendar__day.-ltr.-selectedStart { | ||||
|     border-radius: 0; | ||||
|     border-top-left-radius: 7px; | ||||
|     border-bottom-left-radius: 7px; | ||||
|   } | ||||
|    | ||||
|   .Calendar__day.-rtl.-selectedStart { | ||||
|     border-radius: 0; | ||||
|     border-top-right-radius: 7px; | ||||
|     border-bottom-right-radius: 7px; | ||||
|   } | ||||
|    | ||||
|   .Calendar__day.-selectedBetween { | ||||
|     background:rgba(0, 108, 148, 0.07); | ||||
|      | ||||
|     border-radius: 0; | ||||
|   } | ||||
|    | ||||
|   .Calendar__day.-ltr.-selectedEnd { | ||||
|     border-radius: 0; | ||||
|     border-top-right-radius: 7px; | ||||
|     border-bottom-right-radius: 7px; | ||||
|   } | ||||
|    | ||||
|   .Calendar__day.-rtl.-selectedEnd { | ||||
|     border-radius: 0; | ||||
|     border-top-left-radius: 7px; | ||||
|     border-bottom-left-radius: 7px; | ||||
|   } | ||||
|    | ||||
|   .Calendar__day.-weekend:not(.-selected):not(.-blank):not(.-selectedStart):not(.-selectedEnd):not(.-selectedBetween) { | ||||
|     color: var(--cl-color-error); | ||||
|   } | ||||
|    | ||||
|   .Calendar__day.-weekend.-today:not(.-selectedStart):not(.-selectedEnd):not(.-selectedBetween)::after { | ||||
|     background: var(--cl-color-error); | ||||
|   } | ||||
|    | ||||
|   .Calendar__day.-disabled { | ||||
|     color: var(--cl-color-disabled) !important; | ||||
|     background: transparent !important; | ||||
|     cursor: default !important; | ||||
|   } | ||||
|   .Calendar__day.-selected { | ||||
|     | ||||
|   } | ||||
|   .Calendar__day.-today:not(.-selectedStart):not(.-selectedEnd):not(.-selectedBetween) { | ||||
|     font-weight: 600; | ||||
|     color: var(--cl-color-black); | ||||
|     color: #000; | ||||
|     position: relative; | ||||
|   } | ||||
|    | ||||
|   .Calendar__day.-today:not(.-selectedStart):not(.-selectedEnd):not(.-selectedBetween)::after { | ||||
|     content: ''; | ||||
|     position: absolute; | ||||
|     bottom: 0.2em; | ||||
|     display: block; | ||||
|     width: 0.6em; | ||||
|     height: 1px; | ||||
|     background: #000; | ||||
|     left: 50%; | ||||
|     opacity: 0.5; | ||||
|     transform: translateX(-50%); | ||||
|     transition: 0.2s; | ||||
|   } | ||||
|    | ||||
|   .Calendar__day.-today:hover:not(.-selectedStart):not(.-selectedEnd):not(.-selectedBetween)::after { | ||||
|     opacity: 0; | ||||
|   } | ||||
|    | ||||
|   .Calendar__day.-blank { | ||||
|     color: transparent; | ||||
|     cursor: default; | ||||
|     pointer-events: none; | ||||
|   } | ||||
|    | ||||
|   .Calendar__footer { | ||||
|     position: relative; | ||||
|     z-index: 1; | ||||
|   } | ||||
| 
 | ||||
|   .range-date-picker .specialDay span{ | ||||
|     border-bottom: 4px dotted #444444; | ||||
|   } | ||||
|   .range-date-picker .holiday span{ | ||||
|     color:#ff2929; | ||||
|   } | ||||
|    | ||||
|   @keyframes fadeCalendar { | ||||
|     from { | ||||
|       opacity: 0; | ||||
|     } | ||||
|    | ||||
|     to { | ||||
|       opacity: 1; | ||||
|       transform: translateY(0); | ||||
|     } | ||||
|   } | ||||
|    | ||||
|   @keyframes fadeArrowFlipped { | ||||
|     from { | ||||
|       opacity: 0; | ||||
|     } | ||||
|    | ||||
|     to { | ||||
|       opacity: 1; | ||||
|       transform: translateY(0) rotate(180deg); | ||||
|     } | ||||
|   } | ||||
|    | ||||
|   @keyframes fadeTextToCenter { | ||||
|     to { | ||||
|       opacity: 1; | ||||
|       transform: translateX(-50%); | ||||
|     } | ||||
|   } | ||||
|    | ||||
|   @keyframes FadeContentToCenter { | ||||
|     to { | ||||
|       opacity: 1; | ||||
|       transform: translateX(0); | ||||
|     } | ||||
|   } | ||||
|    | ||||
					Loading…
					
					
				
		Reference in new issue