UNPKG

react-trip-date

Version:

Accessible, fully customizable date and range picker for React applications.

1 lines 96.9 kB
{"version":3,"file":"index.mjs","sources":["../src/libs/useClickOutside.ts","../src/components/DisplayYears.tsx","../src/assets/chevron-left.svg?react","../src/assets/chevron-right.svg?react","../src/components/Header.tsx","../src/constant/theme.ts","../src/constant/constants.ts","../src/libs/classNames.ts","../src/libs/dayjs-config.ts","../src/libs/getDayFormat.ts","../src/libs/handleDayKeyDown.ts","../src/libs/isMobile.ts","../src/rangePicker/Day.tsx","../src/components/TitleOfWeek.tsx","../src/datePicker/datePicker.style.tsx","../src/libs/createCalendar.ts","../src/libs/sliceDaysOfMonthToWeeks.ts","../src/rangePicker/Months.tsx","../src/components/DisplayMonths.tsx","../src/libs/mergeObjects.ts","../src/libs/dayjsLocalized.ts","../src/libs/useCalendarController.ts","../src/libs/useControlled.ts","../src/rangePicker/RangePicker.tsx","../src/datePicker/Day.tsx","../src/datePicker/Months.tsx","../src/datePicker/DatePicker.tsx","../src/calendar/Calendar.tsx"],"sourcesContent":["import { useEffect, useRef } from \"react\";\n\ntype Event = MouseEvent | TouchEvent;\n\nexport const useOnClickOutside = <T extends HTMLElement = HTMLElement>(\n handler: (event: Event) => void,\n) => {\n const ref = useRef<T>(null);\n\n useEffect(() => {\n const listener = (event: Event) => {\n const el = ref.current;\n if (!el || el.contains(event.target as Node)) {\n return;\n }\n handler(event);\n };\n\n document.addEventListener(\"mousedown\", listener);\n document.addEventListener(\"touchstart\", listener);\n\n return () => {\n document.removeEventListener(\"mousedown\", listener);\n document.removeEventListener(\"touchstart\", listener);\n };\n }, [handler]);\n\n return { ref };\n};\n","import dayjs, { Dayjs } from \"dayjs\";\nimport { Dispatch, SetStateAction } from \"react\";\nimport styled from \"styled-components\";\n\nimport { useOnClickOutside } from \"../libs/useClickOutside\";\n\ntype Props = {\n setSource: Dispatch<SetStateAction<Dayjs>>;\n setDisplayYears: Dispatch<SetStateAction<boolean>>;\n};\n\nconst YEAR_COUNT = 120;\n\nexport const DisplayYears = ({ setDisplayYears, setSource }: Props) => {\n const { ref } = useOnClickOutside<HTMLDivElement>(() =>\n setDisplayYears(false),\n );\n\n const years = [];\n for (let i = 0; i < YEAR_COUNT; i++) {\n const label = dayjs().subtract(i, \"year\").format(\"YYYY\");\n years.push(\n <button\n type=\"button\"\n key={label}\n onClick={() => {\n setSource(prev => prev.set(\"year\", Number(label)));\n setDisplayYears(open => !open);\n }}\n >\n {label}\n </button>,\n );\n }\n\n return (\n <Wrapper\n className=\"tp-calendar-year-select\"\n ref={ref}\n role=\"listbox\"\n aria-label=\"Choose year\"\n >\n {years}\n </Wrapper>\n );\n};\n\nconst Wrapper = styled.div`\n display: flex;\n flex-direction: column;\n position: absolute;\n top: 20px;\n left: 0;\n right: 0;\n margin: auto;\n width: 80px;\n height: 40vh;\n background: #fff;\n overflow: auto;\n padding: 10px 0px;\n border-radius: ${({ theme }) => theme.shape.borderRadius}px;\n z-index: 10;\n\n > button {\n display: block;\n margin: 0;\n padding: 6px 0;\n text-align: center;\n width: 100%;\n color: ${({ theme }) => theme.grey[900]};\n border: 0;\n background: transparent;\n font: inherit;\n cursor: pointer;\n\n &:hover,\n &:focus-visible {\n background-color: ${({ theme }) => theme.primary.light};\n outline: none;\n }\n }\n`;\n","import * as React from \"react\";\nconst SvgChevronLeft = (props) => /* @__PURE__ */ React.createElement(\"svg\", { xmlns: \"http://www.w3.org/2000/svg\", width: 4.876, height: 7.908, viewBox: \"0 0 4.876 7.908\", fill: \"currentColor\", ...props }, /* @__PURE__ */ React.createElement(\"path\", { id: \"ic_keyboard_arrow_down_24px_1_\", d: \"M241.923 1911.5l3.031 3.031 3.031-3.031.923.923-3.954 3.954-3.954-3.954z\", className: \"semi-dark\", transform: \"rotate(90 1078.688 837.689)\" }));\nexport default SvgChevronLeft;\n","import * as React from \"react\";\nconst SvgChevronRight = (props) => /* @__PURE__ */ React.createElement(\"svg\", { xmlns: \"http://www.w3.org/2000/svg\", width: 4.877, height: 7.91, viewBox: \"0 0 4.877 7.91\", fill: \"currentColor\", ...props }, /* @__PURE__ */ React.createElement(\"path\", { id: \"ic_keyboard_arrow_down_24px_1_\", d: \"M241.923 1911.5l3.031 3.031 3.031-3.031.923.923-3.954 3.954-3.954-3.954z\", transform: \"rotate(-90 -831.296 1080.205)\" }));\nexport default SvgChevronRight;\n","import { Dayjs } from \"dayjs\";\nimport { Dispatch, SetStateAction, useState } from \"react\";\nimport styled from \"styled-components\";\n\nimport { DisplayYears } from \"./DisplayYears\";\nimport ArrowLeft from \"../assets/chevron-left.svg?react\";\nimport ArrowRight from \"../assets/chevron-right.svg?react\";\nimport { InitialComponents } from \"../constant\";\n\ntype Props = {\n displayMonths: boolean;\n jalali: boolean;\n numberOfMonths: number;\n source: Dayjs;\n components?: InitialComponents;\n setDisplayMonths: Dispatch<SetStateAction<boolean>>;\n setSource: Dispatch<SetStateAction<Dayjs>>;\n};\n\nexport const Header = ({\n jalali,\n source,\n setSource,\n components,\n displayMonths,\n numberOfMonths,\n setDisplayMonths,\n}: Props) => {\n const [displayYears, setDisplayYears] = useState(false);\n\n const prev = () => {\n setSource(source.subtract(1, displayMonths ? \"year\" : \"month\"));\n };\n\n const next = () => {\n setSource(source.add(1, displayMonths ? \"year\" : \"month\"));\n };\n\n const prevLabel = displayMonths\n ? `Previous year, ${source.format(\"YYYY\")}`\n : `Previous month, ${source.format(\"MMMM YYYY\")}`;\n const nextLabel = displayMonths\n ? `Next year, ${source.format(\"YYYY\")}`\n : `Next month, ${source.format(\"MMMM YYYY\")}`;\n\n const renderTitles = () => {\n if (displayMonths) {\n return (\n <>\n <button\n type=\"button\"\n className=\"tp-calendar-header-title\"\n onClick={() => setDisplayYears(open => !open)}\n aria-label={`Choose year, ${source.format(\"YYYY\")}`}\n >\n {source.format(\n components?.header?.format ??\n (displayMonths ? \"YYYY\" : \"YYYY-MMMM\"),\n )}\n </button>\n {displayYears && (\n <DisplayYears\n setDisplayYears={setDisplayYears}\n setSource={setSource}\n />\n )}\n </>\n );\n }\n\n const titles = [];\n for (let i = 0; i < numberOfMonths; i++) {\n const monthSource = source.add(i, \"month\");\n titles.push(\n <button\n type=\"button\"\n key={i}\n className=\"tp-calendar-header-title\"\n onClick={() => setDisplayMonths(open => !open)}\n aria-label={`Choose month, ${monthSource.format(\"MMMM YYYY\")}`}\n >\n {monthSource.format(components?.header?.format ?? \"YYYY MMMM\")}\n </button>,\n );\n }\n return titles;\n };\n\n return (\n <Wrapper\n className=\"tp-calendar-header\"\n $numberOfMonths={numberOfMonths}\n $jalali={jalali}\n $displayMonths={displayMonths}\n >\n <button\n type=\"button\"\n aria-label={prevLabel}\n className={components?.header?.monthIcons?.left ? \"\" : \"action left\"}\n onClick={prev}\n >\n {components?.header?.monthIcons?.left &&\n !displayMonths &&\n components.header.monthIcons.left}\n {components?.header?.yearIcons?.left &&\n displayMonths &&\n components.header.yearIcons.left}\n {!components?.header?.monthIcons?.left && (\n <>\n <ArrowLeft />\n {displayMonths ? <ArrowLeft /> : null}\n </>\n )}\n </button>\n {renderTitles()}\n <button\n type=\"button\"\n aria-label={nextLabel}\n className={components?.header?.monthIcons?.right ? \"\" : \"action right\"}\n onClick={next}\n >\n {components?.header?.monthIcons?.right &&\n !displayMonths &&\n components.header.monthIcons.right}\n {components?.header?.yearIcons?.right &&\n displayMonths &&\n components.header.yearIcons.right}\n {!components?.header?.monthIcons?.right && (\n <>\n <ArrowRight />\n {displayMonths ? <ArrowRight /> : null}\n </>\n )}\n </button>\n </Wrapper>\n );\n};\n\ntype WrapperProps = {\n $jalali: boolean;\n $numberOfMonths: number;\n $displayMonths: boolean;\n};\n\nconst Wrapper = styled.div<WrapperProps>`\n display: flex;\n justify-content: space-between;\n align-items: center;\n height: 55px;\n position: relative;\n background-color: ${({ theme }) => theme.primary.main};\n flex-direction: ${({ $jalali }) => ($jalali ? \"row-reverse\" : \"row\")};\n border-radius: ${({ theme }) => theme.shape.borderRadius}px;\n\n .tp-calendar-header-title {\n color: #fff;\n text-align: center;\n cursor: pointer;\n border: 0;\n background: transparent;\n font: inherit;\n border-radius: ${({ theme }) => theme.shape.borderRadius}px;\n direction: ${({ $jalali }) => ($jalali ? \"ltr\" : \"rtl\")};\n width: ${({ $numberOfMonths, $displayMonths }) =>\n $displayMonths ? \"120px\" : `${100 / $numberOfMonths}% `};\n margin: auto;\n\n &:focus-visible {\n outline: 2px solid #fff;\n outline-offset: 2px;\n }\n }\n\n button.action {\n position: absolute;\n height: 55px;\n width: 55px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n border: 0;\n background: transparent;\n color: #fff;\n\n &:focus-visible {\n outline: 2px solid #fff;\n outline-offset: 2px;\n }\n\n &.right {\n transform: ${({ $jalali }) =>\n !$jalali ? \"rotate(0deg)\" : \"rotate(180deg)\"};\n ${({ $jalali }) => ($jalali ? \"left:0;\" : \"right: 0;\")}\n }\n &.left {\n transform: ${({ $jalali }) =>\n !$jalali ? \"rotate(0deg)\" : \"rotate(180deg)\"};\n ${({ $jalali }) => ($jalali ? \"right:0;\" : \"left: 0;\")}\n }\n }\n\n svg {\n width: 10px;\n height: 15px;\n color: ${({ theme }) => theme.background.default};\n }\n`;\n","export const theme = {\n primary: {\n light: \"#d0f4f0\",\n main: \"#13c8b5\",\n dark: \"#12baa9\",\n },\n grey: {\n 700: \"#707070\",\n 900: \"#1b1b1d\",\n },\n background: {\n default: \"#f5f5f5\",\n },\n text: {\n disabled: \"#BABABA\",\n },\n shape: {\n borderRadius: 4,\n },\n};\n\nexport interface Theme {\n primary: {\n light: string;\n main: string;\n dark: string;\n };\n grey: {\n 700: string;\n 900: string;\n };\n background: {\n default: string;\n };\n text: {\n disabled: string;\n };\n shape: {\n borderRadius: number;\n };\n}\n","export const FORMAT_DATE = \"YYYY-MM-DD\";\n","type ClassValue =\n | string\n | number\n | false\n | null\n | undefined\n | Record<string, unknown>;\n\nexport const classNames = (...args: ClassValue[]) => {\n const classes: Array<string | number> = [];\n for (const arg of args) {\n if (!arg) continue;\n const argType = typeof arg;\n if (argType === \"string\" || argType === \"number\") {\n classes.push(arg as string | number);\n } else if (argType === \"object\") {\n const obj = arg as Record<string, unknown> & { toString?: () => string };\n if (obj.toString && obj.toString !== Object.prototype.toString) {\n classes.push(obj.toString());\n } else {\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key) && obj[key]) {\n classes.push(key);\n }\n }\n }\n }\n }\n return classes.join(\" \");\n};\n","/**\n * dayjs is configured here once for the entire library.\n *\n * Two ordering constraints have to be respected, in this order:\n *\n * 1. Locale modules (`dayjs/locale/*`) must register themselves on\n * `dayjs.Ls` BEFORE `dayjs.extend(jalaliPlugin)` runs.\n * 2. Each locale module is imported via a default-binding so the\n * bundler keeps the module in the graph and its registration\n * side-effect actually executes; bare `import \"dayjs/locale/X\"`\n * forms get tree-shaken in production.\n *\n * Why the order matters: the jalali plugin's `extend` callback re-\n * registers the `fa` locale merged with its Persian-calendar\n * `jmonths` array. If a `dayjs/locale/fa.js` registration ran later,\n * it would overwrite the augmented entry and strip `jmonths`, and\n * `format(\"MMMM\")` on a Jalali date would crash with\n * \"Cannot read properties of undefined (reading '2')\".\n */\nimport jalaliPlugin from \"@zoomit/dayjs-jalali-plugin\";\nimport _dayjs from \"dayjs\";\nimport deLocale from \"dayjs/locale/de\";\nimport esLocale from \"dayjs/locale/es\";\nimport faLocale from \"dayjs/locale/fa\";\nimport frLocale from \"dayjs/locale/fr\";\nimport itLocale from \"dayjs/locale/it\";\nimport jaLocale from \"dayjs/locale/ja\";\nimport ruLocale from \"dayjs/locale/ru\";\nimport trLocale from \"dayjs/locale/tr\";\nimport zhLocale from \"dayjs/locale/zh\";\nimport isBetween from \"dayjs/plugin/isBetween\";\nimport isSameOrBefore from \"dayjs/plugin/isSameOrBefore\";\n\nconst REGISTERED_LOCALES = [\n deLocale,\n esLocale,\n faLocale,\n frLocale,\n itLocale,\n jaLocale,\n ruLocale,\n trLocale,\n zhLocale,\n];\nif (REGISTERED_LOCALES.length === 0) {\n throw new Error(\"react-trip-date: locale registration failed.\");\n}\n\n_dayjs.extend(jalaliPlugin);\n_dayjs.extend(isBetween);\n_dayjs.extend(isSameOrBefore);\n\nconst dayjs: (\n date?: _dayjs.ConfigType,\n format?:\n | string\n | {\n locale?: string;\n format?: string;\n utc?: boolean;\n jalali?: boolean;\n }\n | string[],\n strict?: boolean,\n) => _dayjs.Dayjs = _dayjs;\n\nexport { dayjs };\n","import { Dayjs } from \"dayjs\";\n\nimport { dayjs } from \"./dayjs-config\";\n\nexport const getDayFormat = (day: Dayjs, jalali?: boolean) => {\n return dayjs(day, { jalali: jalali, format: \"YYYY-MM-DD\" })\n .calendar(\"gregory\")\n .format(\"YYYY-MM-DD\");\n};\n","import { Dayjs } from \"dayjs\";\nimport { KeyboardEvent } from \"react\";\n\ntype Options = {\n day: Dayjs;\n source: Dayjs;\n setSource: (next: Dayjs) => void;\n setFocusedDate: (date: string) => void;\n requestFocus?: (date: string) => void;\n};\n\nconst FMT = \"YYYY-MM-DD\";\n\nexport function handleDayKeyDown(\n event: KeyboardEvent<HTMLElement>,\n { day, source, setSource, setFocusedDate, requestFocus }: Options,\n): boolean {\n let next: Dayjs | undefined;\n\n switch (event.key) {\n case \"ArrowLeft\":\n next = day.subtract(1, \"day\");\n break;\n case \"ArrowRight\":\n next = day.add(1, \"day\");\n break;\n case \"ArrowUp\":\n next = day.subtract(1, \"week\");\n break;\n case \"ArrowDown\":\n next = day.add(1, \"week\");\n break;\n case \"Home\":\n next = day.startOf(\"week\");\n break;\n case \"End\":\n next = day.endOf(\"week\");\n break;\n case \"PageUp\":\n next = event.shiftKey\n ? day.subtract(1, \"year\")\n : day.subtract(1, \"month\");\n break;\n case \"PageDown\":\n next = event.shiftKey ? day.add(1, \"year\") : day.add(1, \"month\");\n break;\n default:\n return false;\n }\n\n event.preventDefault();\n event.stopPropagation();\n\n const nextStr = next.format(FMT);\n requestFocus?.(nextStr);\n setFocusedDate(nextStr);\n\n if (next.month() !== source.month() || next.year() !== source.year()) {\n setSource(next.startOf(\"month\"));\n }\n\n return true;\n}\n","/* eslint-disable */\nexport const isMobile = () => {\n return (\n /(android|bb\\d+|meego).+mobile|avantgo|bada\\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(\n navigator.userAgent || navigator.vendor || (window as any).opera,\n ) ||\n /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\\-(n|u)|c55\\/|capi|ccwa|cdm\\-|cell|chtm|cldc|cmd\\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\\-s|devi|dica|dmob|do(c|p)o|ds(12|\\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\\-|_)|g1 u|g560|gene|gf\\-5|g\\-mo|go(\\.w|od)|gr(ad|un)|haie|hcit|hd\\-(m|p|t)|hei\\-|hi(pt|ta)|hp( i|ip)|hs\\-c|ht(c(\\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\\-(20|go|ma)|i230|iac( |\\-|\\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\\/)|klon|kpt |kwc\\-|kyo(c|k)|le(no|xi)|lg( g|\\/(k|l|u)|50|54|\\-[a-w])|libw|lynx|m1\\-w|m3ga|m50\\/|ma(te|ui|xo)|mc(01|21|ca)|m\\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\\-2|po(ck|rt|se)|prox|psio|pt\\-g|qa\\-a|qc(07|12|21|32|60|\\-[2-7]|i\\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\\-|oo|p\\-)|sdk\\/|se(c(\\-|0|1)|47|mc|nd|ri)|sgh\\-|shar|sie(\\-|m)|sk\\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\\-|v\\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\\-|tdg\\-|tel(i|m)|tim\\-|t\\-mo|to(pl|sh)|ts(70|m\\-|m3|m5)|tx\\-9|up(\\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\\-|your|zeto|zte\\-/i.test(\n (navigator.userAgent || navigator.vendor || (window as any).opera).substr(\n 0,\n 4,\n ),\n )\n );\n};\n","import { Dayjs } from \"dayjs\";\nimport {\n Dispatch,\n KeyboardEvent,\n MouseEvent,\n MutableRefObject,\n SetStateAction,\n useEffect,\n useRef,\n} from \"react\";\nimport styled from \"styled-components\";\n\nimport { FORMAT_DATE } from \"../constant\";\nimport {\n RangePickerComponents,\n RangePickerSelectedDays,\n} from \"./rangePicker.type\";\nimport { classNames } from \"../libs/classNames\";\nimport { dayjs } from \"../libs/dayjs-config\";\nimport { getDayFormat } from \"../libs/getDayFormat\";\nimport { handleDayKeyDown } from \"../libs/handleDayKeyDown\";\nimport { isMobile } from \"../libs/isMobile\";\n\ninterface Props {\n day: Dayjs;\n source: Dayjs;\n jalali: boolean;\n hoverDay?: string;\n disabled: boolean;\n numberOfMonth: number;\n disabledDays: string[];\n disabledBeforeToday: boolean;\n disabledBeforeDate?: string;\n disabledAfterDate?: string;\n allowDisabledDaysSpan: boolean;\n components?: RangePickerComponents;\n selectedDays?: RangePickerSelectedDays;\n setHoverDay: Dispatch<SetStateAction<string | undefined>>;\n setSelectedDays: Dispatch<SetStateAction<RangePickerSelectedDays>>;\n setSource: Dispatch<SetStateAction<Dayjs>>;\n dayClasses?: (day: Dayjs) => string[];\n focusedDate: string;\n setFocusedDate: (date: string) => void;\n focusRef: MutableRefObject<string | null>;\n requestFocus: (date: string) => void;\n}\n\nconst TODAY_GREG = () => dayjs().format(FORMAT_DATE);\n\nexport const Day = ({\n day,\n source,\n setSource,\n jalali,\n selectedDays,\n disabledDays,\n allowDisabledDaysSpan,\n hoverDay,\n disabled,\n components,\n setHoverDay,\n setSelectedDays,\n numberOfMonth,\n disabledBeforeToday,\n disabledBeforeDate,\n disabledAfterDate,\n dayClasses,\n focusedDate,\n setFocusedDate,\n focusRef,\n requestFocus,\n}: Props) => {\n const ref = useRef<HTMLButtonElement>(null);\n const dateFormat = getDayFormat(day, jalali);\n const isFocused = focusedDate === dateFormat;\n\n const effectiveBefore = (() => {\n if (!disabledBeforeToday) return disabledBeforeDate;\n const today = TODAY_GREG();\n return disabledBeforeDate && dayjs(disabledBeforeDate).isAfter(today)\n ? disabledBeforeDate\n : today;\n })();\n\n const isDisabledDate = (target: string): boolean => {\n if (disabledDays.includes(target)) return true;\n if (effectiveBefore && dayjs(target).isBefore(effectiveBefore)) return true;\n if (disabledAfterDate && dayjs(target).isAfter(disabledAfterDate))\n return true;\n return false;\n };\n\n useEffect(() => {\n if (\n isFocused &&\n ref.current &&\n focusRef.current === dateFormat &&\n document.activeElement !== ref.current\n ) {\n ref.current.focus({ preventScroll: true });\n focusRef.current = null;\n }\n }, [isFocused, dateFormat, focusRef]);\n\n const commit = (next: RangePickerSelectedDays) => {\n setSelectedDays(next);\n };\n\n const handleChangeState = (from: string, to: string) => {\n if (disabled) return;\n if (dayjs(from).isBefore(to)) {\n commit({ from, to });\n } else {\n commit({ from: to, to: from });\n }\n };\n\n const handleSelect = () => {\n if (disabled || isDisabledDate(dateFormat)) return;\n\n // First click — set the \"from\" date.\n if (!selectedDays?.from && !selectedDays?.to) {\n setHoverDay(\"\");\n commit({ from: dateFormat, to: \"\" });\n return;\n }\n\n // Second click — set the \"to\" date.\n if (selectedDays?.from && !selectedDays?.to) {\n const disables = disabledDays.filter(item =>\n dayjs(item).isBetween(selectedDays.from, dateFormat, null, \"[]\"),\n );\n if (disables.length && !allowDisabledDaysSpan) {\n const probe = hoverDay ?? dateFormat;\n const sorted = [...disables].sort((prev, next) =>\n dayjs(disables[0]).isBefore(probe)\n ? dayjs(prev).isSameOrBefore(next)\n ? -1\n : 1\n : dayjs(prev).isSameOrBefore(next)\n ? 1\n : -1,\n );\n const clamp = sorted[0];\n if (dayjs(clamp).isBefore(probe)) {\n handleChangeState(\n selectedDays.from,\n getDayFormat(dayjs(clamp).subtract(1, \"day\"), jalali),\n );\n } else {\n handleChangeState(\n selectedDays.from,\n getDayFormat(dayjs(clamp).add(1, \"day\"), jalali),\n );\n }\n } else {\n handleChangeState(selectedDays.from, dateFormat);\n }\n return;\n }\n\n // Third click — reset.\n setHoverDay(\"\");\n commit({ from: dateFormat, to: \"\" });\n };\n\n const handleClick = (event: MouseEvent<HTMLButtonElement>) => {\n event.preventDefault();\n if (disabled) return;\n setFocusedDate(dateFormat);\n handleSelect();\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLButtonElement>) => {\n if (\n handleDayKeyDown(event, {\n day,\n source,\n setSource,\n setFocusedDate,\n requestFocus,\n })\n ) {\n return;\n }\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n handleSelect();\n }\n };\n\n const hoverOnDay = () => {\n if (!selectedDays?.to && selectedDays?.from) {\n setHoverDay(dateFormat);\n }\n };\n\n const getEndDateForClasses = () => {\n if (!selectedDays) return \"\";\n if (selectedDays.to) return selectedDays.to;\n if (hoverDay) return hoverDay;\n return selectedDays.from;\n };\n\n const isRangeSelected = (): boolean => {\n if (!selectedDays || !selectedDays.from) return false;\n if (dateFormat === selectedDays.from) return true;\n const endDate = getEndDateForClasses();\n if (isDisabledDate(endDate)) return false;\n return dayjs(dateFormat).isBetween(selectedDays.from, endDate, null, \"[]\");\n };\n\n const isInactiveMonth =\n day.month() !== source.add(numberOfMonth, \"month\").month();\n const isToday =\n dayjs()\n .calendar(jalali ? \"jalali\" : \"gregory\")\n .format(FORMAT_DATE) === day.format(FORMAT_DATE);\n\n const disabledClass = isDisabledDate(dateFormat) && !isRangeSelected();\n const startDateClass =\n !!selectedDays &&\n dateFormat ===\n (dayjs(selectedDays.from).isSameOrBefore(getEndDateForClasses())\n ? selectedDays.from\n : selectedDays.to);\n const endDateClass =\n !!selectedDays &&\n dateFormat ===\n (dayjs(selectedDays.from).isSameOrBefore(getEndDateForClasses())\n ? selectedDays.to\n : selectedDays.from);\n\n const DayComponent = components?.days;\n const extraDayClasses = dayClasses ? dayClasses(day).join(\" \") : \"\";\n\n return (\n <Wrapper\n ref={ref}\n type=\"button\"\n data-test={day.format(FORMAT_DATE)}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n onMouseEnter={isMobile() ? undefined : hoverOnDay}\n tabIndex={isFocused ? 0 : -1}\n role=\"gridcell\"\n aria-selected={startDateClass || endDateClass || isRangeSelected()}\n aria-disabled={disabledClass || disabled || undefined}\n aria-current={isToday ? \"date\" : undefined}\n aria-label={day.format(\"dddd, MMMM D, YYYY\")}\n disabled={disabled}\n className={classNames(\n {\n inactive: isInactiveMonth,\n disabled: disabledClass,\n \"range-select\": isRangeSelected(),\n jalali,\n disable: disabled,\n same:\n !!selectedDays && dayjs(selectedDays.from).isSame(selectedDays.to),\n \"start-date\": startDateClass,\n \"end-date\": endDateClass,\n today: isToday,\n },\n extraDayClasses,\n \"tp-calendar-day\",\n )}\n >\n {DayComponent ? (\n <DayComponent day={day.format(FORMAT_DATE)} jalali={jalali} />\n ) : (\n day.format(\"DD\")\n )}\n </Wrapper>\n );\n};\n\nconst Wrapper = styled.button`\n border: 0;\n background: transparent;\n font: inherit;\n width: 40px;\n height: 40px;\n transition: all 0.15s ease-in-out;\n text-align: center;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-direction: column;\n margin-bottom: 5px;\n cursor: pointer;\n color: ${({ theme }) => theme.grey[900]};\n\n &:focus-visible {\n outline: 2px solid ${({ theme }) => theme.primary.dark};\n outline-offset: 2px;\n }\n\n &:hover {\n background-color: ${({ theme }) => theme.primary.light};\n color: ${({ theme }) => theme.grey[700]};\n &.disable {\n background-color: transparent;\n color: ${({ theme }) => theme.grey[900]};\n cursor: default;\n }\n }\n\n &.inactive {\n color: transparent;\n visibility: hidden;\n }\n\n &.today {\n border: 1px solid #e2e2e2;\n border-radius: 50%;\n }\n\n &.disabled {\n color: ${({ theme }) => theme.text.disabled};\n position: relative;\n cursor: not-allowed;\n\n &:hover {\n background-color: transparent;\n color: ${({ theme }) => theme.text.disabled};\n }\n\n &::after {\n position: absolute;\n content: \"\";\n width: 15px;\n height: 2px;\n background-color: ${({ theme }) => theme.text.disabled};\n transform: rotate(-20deg);\n }\n }\n\n &.range-select {\n background-color: ${({ theme }) => theme.primary.dark};\n color: #fff;\n border: 0;\n border-radius: 0;\n\n &:hover {\n background-color: ${({ theme }) => theme.primary.dark};\n color: #fff;\n }\n box-shadow: 0px 10px 30px -12px ${({ theme }) => theme.primary.dark};\n\n &.jalali {\n box-shadow: 0px 10px 30px -12px ${({ theme }) => theme.primary.dark};\n }\n\n &.end-date {\n border-top-right-radius: 25px;\n border-bottom-right-radius: 25px;\n\n &.jalali {\n border-top-left-radius: 25px;\n border-bottom-left-radius: 25px;\n border-top-right-radius: 0px;\n border-bottom-right-radius: 0px;\n }\n }\n\n &.start-date {\n border-top-left-radius: 25px;\n border-bottom-left-radius: 25px;\n\n &.jalali {\n border-top-left-radius: 0px;\n border-bottom-left-radius: 0px;\n border-top-right-radius: 25px;\n border-bottom-right-radius: 25px;\n }\n }\n }\n\n &.same {\n border-top-left-radius: 25px !important;\n border-bottom-left-radius: 25px !important;\n border-top-right-radius: 25px !important;\n border-bottom-right-radius: 25px !important;\n }\n`;\n","import { ElementType } from \"react\";\nimport styled from \"styled-components\";\n\ntype Props = {\n jalali: boolean;\n startOfWeek: number;\n components?: {\n titles?: string[];\n wrapper?: ElementType<{ jalali: boolean; startOfWeek: number }>;\n };\n};\n\nexport const TitleOfWeek = ({ jalali, startOfWeek, components }: Props) => {\n let titles = [\"Su\", \"Mo\", \"Tu\", \"We\", \"Th\", \"Fr\", \"Sa\"];\n if (components?.titles) titles = [...components.titles];\n else if (jalali) titles = [\"ش\", \"ی\", \"د\", \"س\", \"چ\", \"پ\", \"ج\"];\n\n if (!jalali) {\n let sow = startOfWeek % 7;\n if (sow > 0) while (sow--) titles.push(titles.shift()!);\n }\n\n if (components?.wrapper) {\n const WrapperC = components.wrapper;\n return <WrapperC jalali={jalali} startOfWeek={startOfWeek} />;\n }\n\n return (\n <Wrapper className=\"tp-calendar-week-titles\" $jalali={jalali} role=\"row\">\n {titles.map(item => (\n // eslint-disable-next-line jsx-a11y/no-noninteractive-element-to-interactive-role\n <p key={item} role=\"columnheader\" aria-label={item}>\n {item}\n </p>\n ))}\n </Wrapper>\n );\n};\n\nconst Wrapper = styled.div<{ $jalali: boolean }>`\n display: flex;\n text-align: center;\n justify-content: center;\n flex-direction: ${({ $jalali }) => ($jalali ? \"row-reverse\" : \"row\")};\n border-bottom: 1px solid #e2e2e2;\n margin: 30px 0 15px 0;\n p {\n width: 38px;\n margin-bottom: 15px;\n margin-left: 5px;\n }\n`;\n","import styled, { css } from \"styled-components\";\n\ntype DirectionalProps = {\n $jalali: boolean;\n};\n\ntype MonthProps = {\n $numberOfMonths: number;\n};\n\nexport const Wrapper = styled.div<DirectionalProps>`\n display: flex;\n justify-content: space-between;\n align-items: center;\n position: relative;\n flex-direction: ${({ $jalali }) => ($jalali ? \"row-reverse\" : \"row\")};\n`;\n\nexport const Month = styled.div<MonthProps>`\n height: 350px;\n display: flex;\n position: relative;\n align-items: center;\n flex-direction: column;\n width: ${({ $numberOfMonths }) => css`calc(${100 / $numberOfMonths}%)`};\n`;\n\nexport const Weeks = styled.div<DirectionalProps>`\n display: flex;\n align-items: center;\n position: relative;\n flex-direction: ${({ $jalali }) => ($jalali ? \"row-reverse\" : \"row\")};\n`;\n","import { Dayjs } from \"dayjs\";\n\nconst createCalendar = ({\n source,\n startOfWeek,\n}: CreateCalendarType): Dayjs[] => {\n const days: Dayjs[] = [];\n const daysInMonth = source.daysInMonth();\n const firstDay = source.startOf(\"month\").day();\n\n const sow = startOfWeek < 0 ? 0 : startOfWeek % 7;\n let shift = 0;\n if (firstDay > sow) shift = firstDay - sow;\n else if (sow > firstDay) shift = 7 - (sow - firstDay);\n\n let tmpDay = source.startOf(\"month\").subtract(shift, \"day\");\n let totalDays = daysInMonth + shift;\n const remaining = totalDays % 7;\n if (remaining > 0) totalDays += 7 - remaining;\n for (let i = 0; i < totalDays; i++) {\n days.push(tmpDay);\n tmpDay = tmpDay.add(1, \"day\");\n }\n return days;\n};\n\nexport { createCalendar };\n\ninterface CreateCalendarType {\n source: Dayjs;\n startOfWeek: number;\n}\n","import { Dayjs } from \"dayjs\";\n\nexport const sliceDaysOfMonthToWeeks = (days: Dayjs[], len: number) => {\n let i = 0;\n const weeks: Dayjs[][] = [];\n while (i * len < days.length) {\n weeks.push(days.slice(i * len, ++i * len));\n }\n return weeks;\n};\n","import { Dayjs } from \"dayjs\";\nimport { Dispatch, SetStateAction, useRef, useState } from \"react\";\n\nimport { Day } from \"./Day\";\nimport {\n RangePickerComponents,\n RangePickerSelectedDays,\n} from \"./rangePicker.type\";\nimport { TitleOfWeek } from \"../components/TitleOfWeek\";\nimport { Month, Weeks, Wrapper } from \"../datePicker/datePicker.style\";\nimport { createCalendar } from \"../libs/createCalendar\";\nimport { sliceDaysOfMonthToWeeks } from \"../libs/sliceDaysOfMonthToWeeks\";\n\ninterface Props {\n source: Dayjs;\n jalali: boolean;\n startOfWeek: number;\n hoverDay?: string;\n numberOfMonths: number;\n selectedDays?: RangePickerSelectedDays;\n disabledDays: string[];\n disabledBeforeToday: boolean;\n disabledBeforeDate?: string;\n disabledAfterDate?: string;\n disabled: boolean;\n allowDisabledDaysSpan: boolean;\n components?: RangePickerComponents;\n setSource: Dispatch<SetStateAction<Dayjs>>;\n setHoverDay: Dispatch<SetStateAction<string | undefined>>;\n setSelectedDays: Dispatch<SetStateAction<RangePickerSelectedDays>>;\n dayClasses?: (day: Dayjs) => string[];\n}\n\nconst FMT = \"YYYY-MM-DD\";\nconst monthKey = (source: Dayjs) => source.format(\"YYYY-MM\");\nconst weekKey = (firstDay: Dayjs) => firstDay.format(FMT);\n\nexport const Months = ({\n allowDisabledDaysSpan,\n numberOfMonths,\n startOfWeek,\n selectedDays,\n disabledDays,\n hoverDay,\n setHoverDay,\n components,\n setSelectedDays,\n disabledBeforeToday,\n disabledBeforeDate,\n disabledAfterDate,\n jalali,\n disabled,\n source: sourceProp,\n setSource,\n dayClasses,\n}: Props) => {\n const [focusedDate, setFocusedDate] = useState<string>(\n () => selectedDays?.from ?? sourceProp.format(FMT),\n );\n const focusRef = useRef<string | null>(null);\n const requestFocus = (date: string) => {\n focusRef.current = date;\n };\n\n const months = [];\n for (let i = 0; i < numberOfMonths; i++) {\n const monthSource = sourceProp.add(i, \"month\");\n const weeksDays: Dayjs[][] = sliceDaysOfMonthToWeeks(\n createCalendar({\n source: monthSource,\n startOfWeek: jalali ? 6 : startOfWeek,\n }),\n 7,\n );\n\n months.push(\n <Month\n className=\"tp-calendar-month\"\n $numberOfMonths={numberOfMonths || 1}\n key={monthKey(monthSource)}\n data-test={monthKey(monthSource)}\n role=\"grid\"\n aria-label={monthSource.format(\"MMMM YYYY\")}\n >\n <TitleOfWeek\n jalali={jalali}\n startOfWeek={startOfWeek}\n components={components?.titleOfWeek}\n />\n {weeksDays.map(week => (\n <Weeks\n className=\"tp-calendar-week\"\n $jalali={jalali}\n data-test={weekKey(week[0])}\n key={weekKey(week[0])}\n role=\"row\"\n >\n {week.map(day => (\n <Day\n day={day}\n jalali={jalali}\n numberOfMonth={i}\n hoverDay={hoverDay}\n setHoverDay={setHoverDay}\n components={components}\n source={sourceProp}\n setSource={setSource}\n disabled={disabled}\n disabledDays={disabledDays}\n key={day.format(FMT)}\n selectedDays={selectedDays}\n setSelectedDays={setSelectedDays}\n disabledBeforeToday={disabledBeforeToday}\n allowDisabledDaysSpan={allowDisabledDaysSpan}\n disabledBeforeDate={disabledBeforeDate}\n disabledAfterDate={disabledAfterDate}\n dayClasses={dayClasses}\n focusedDate={focusedDate}\n setFocusedDate={setFocusedDate}\n focusRef={focusRef}\n requestFocus={requestFocus}\n />\n ))}\n </Weeks>\n ))}\n </Month>,\n );\n }\n\n return (\n <Wrapper className=\"tp-calendar-months\" $jalali={jalali}>\n {months}\n </Wrapper>\n );\n};\n","import { Dayjs } from \"dayjs\";\nimport { Dispatch, SetStateAction } from \"react\";\nimport styled from \"styled-components\";\n\ntype Props = {\n source: Dayjs;\n jalali: boolean;\n setSource: Dispatch<SetStateAction<Dayjs>>;\n setDisplayMonths: Dispatch<SetStateAction<boolean>>;\n};\n\nexport const DisplayMonths = ({\n source,\n setSource,\n setDisplayMonths,\n jalali,\n}: Props) => {\n const selectMonth = (next: Dayjs) => {\n setSource(next.month(+next.format(\"M\") - 1));\n setDisplayMonths(false);\n };\n\n // Anchor the day at 1 before changing month — otherwise dayjs rolls a\n // day-of-month like 31 into the next month when the target month is\n // shorter, producing duplicates / skips when the source falls late in\n // its month.\n const firstOfMonth = source.date(1);\n const months: Dayjs[] = [];\n for (let i = 0; i < 12; i++) {\n months[i] = firstOfMonth.month(i);\n }\n\n return (\n <Wrapper\n className=\"tp-calendar-month-select\"\n $jalali={jalali}\n role=\"listbox\"\n aria-label=\"Choose month\"\n >\n {months.map((item, index) => (\n <div\n key={index}\n onClick={() => selectMonth(item)}\n onKeyDown={event => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n selectMonth(item);\n }\n }}\n role=\"option\"\n aria-selected={source.month() === item.month()}\n tabIndex={0}\n data-testid=\"month-item-to-select\"\n >\n <p>{item.format(\"MMMM\")}</p>\n </div>\n ))}\n </Wrapper>\n );\n};\n\nconst Wrapper = styled.div<{ $jalali: boolean }>`\n margin-top: 15px;\n display: flex;\n flex: 1 1;\n height: 350px;\n flex-wrap: wrap;\n flex-direction: ${({ $jalali }) => ($jalali ? \"row-reverse\" : \"row\")};\n > div {\n flex-basis: calc(25% - 4px);\n justify-content: center;\n text-align: center;\n display: flex;\n align-items: center;\n cursor: pointer;\n border: 2px solid #fff;\n background-color: ${({ theme }) => theme.primary.main};\n border-radius: ${({ theme }) => theme.shape.borderRadius}px;\n\n &:focus-visible {\n outline: 2px solid ${({ theme }) => theme.primary.dark};\n outline-offset: 2px;\n }\n\n p {\n color: #fff;\n }\n }\n`;\n","type AnyRecord = Record<string, unknown>;\n\nconst isPlainObject = (value: unknown): value is AnyRecord =>\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n Object.getPrototypeOf(value) === Object.prototype;\n\n/**\n * Recursively merge plain objects without mutating any source. Arrays are\n * concatenated and deduped. Anything that is not a plain object is replaced.\n */\nexport const deepMerge = (\n target: AnyRecord,\n ...sources: ReadonlyArray<AnyRecord | undefined>\n): AnyRecord => {\n let result: AnyRecord = isPlainObject(target) ? { ...target } : {};\n\n for (const source of sources) {\n if (!isPlainObject(source)) continue;\n const next: AnyRecord = { ...result };\n for (const key of Object.keys(source)) {\n const srcValue = source[key];\n const tgtValue = next[key];\n if (isPlainObject(srcValue)) {\n next[key] = deepMerge(\n isPlainObject(tgtValue) ? tgtValue : {},\n srcValue,\n );\n } else if (Array.isArray(srcValue) && Array.isArray(tgtValue)) {\n next[key] = Array.from(new Set([...tgtValue, ...srcValue]));\n } else {\n next[key] = srcValue;\n }\n }\n result = next;\n }\n\n return result;\n};\n","import { Dayjs } from \"dayjs\";\n\nimport { dayjs } from \"./dayjs-config\";\n\nexport const dayjsLocalized = (\n jalali: boolean = false,\n date?: string,\n locale: string = \"en\",\n): Dayjs => {\n return dayjs(date)\n .calendar(jalali ? \"jalali\" : \"gregory\")\n .locale(jalali ? \"fa\" : locale || \"en\");\n};\n","import { Dayjs } from \"dayjs\";\nimport {\n Dispatch,\n RefObject,\n SetStateAction,\n useEffect,\n useRef,\n useState,\n} from \"react\";\n\nimport { dayjsLocalized } from \"./dayjsLocalized\";\nimport { getDayFormat } from \"./getDayFormat\";\n\nconst SINGLE_MONTH_BREAKPOINT = 580;\nconst MONTH_WIDTH = 320;\n\nexport type DateWindow = { start: string; end: string };\n\nexport interface UseCalendarControllerOptions {\n autoResponsive: boolean;\n initialMonthAndYear: string | undefined;\n jalali: boolean;\n locale: string | undefined;\n numberOfMonthsProp: number;\n onRangeDateInScreen?: (window: DateWindow) => void;\n initialFocusDate?: string;\n}\n\nexport interface CalendarController {\n ref: RefObject<HTMLDivElement>;\n source: Dayjs;\n setSource: Dispatch<SetStateAction<Dayjs>>;\n numberOfMonths: number;\n displayMonths: boolean;\n setDisplayMonths: Dispatch<SetStateAction<boolean>>;\n}\n\nexport function useCalendarController({\n autoResponsive,\n initialMonthAndYear,\n jalali,\n locale,\n numberOfMonthsProp,\n onRangeDateInScreen,\n initialFocusDate,\n}: UseCalendarControllerOptions): CalendarController {\n const ref = useRef<HTMLDivElement>(null);\n\n const [source, setSource] = useState<Dayjs>(() =>\n dayjsLocalized(jalali, initialMonthAndYear ?? initialFocusDate, locale),\n );\n const [displayMonths, setDisplayMonths] = useState(false);\n const [numberOfMonths, setNumberOfMonths] = useState(numberOfMonthsProp);\n\n useEffect(() => {\n if (initialMonthAndYear) {\n setSource(dayjsLocalized(jalali, initialMonthAndYear, locale));\n }\n }, [initialMonthAndYear, jalali, locale]);\n\n useEffect(() => {\n if (!autoResponsive) {\n setNumberOfMonths(numberOfMonthsProp);\n return;\n }\n\n if (typeof window === \"undefined\") return;\n\n const element = ref.current;\n if (!element) return;\n\n const apply = (width: number) => {\n if (width < SINGLE_MONTH_BREAKPOINT) {\n setNumberOfMonths(1);\n } else {\n setNumberOfMonths(Math.max(1, Math.floor(width / MONTH_WIDTH)));\n }\n };\n\n apply(element.clientWidth);\n\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(entries => {\n for (const entry of entries) {\n apply(entry.contentRect.width);\n }\n });\n observer.observe(element);\n return () => observer.disconnect();\n }, [autoResponsive, numberOfMonthsProp]);\n\n const onRangeDateInScreenRef = useRef(onRangeDateInScreen);\n onRangeDateInScreenRef.current = onRangeDateInScreen;\n\n useEffect(() => {\n if (!onRangeDateInScreenRef.current) return;\n const months = Math.max(0, numberOfMonths - 1);\n let endDate = source.add(months, \"month\");\n endDate = endDate.date(endDate.daysInMonth());\n const startDate = source.date(1);\n onRangeDateInScreenRef.current({\n start: getDayFormat(startDate, jalali),\n end: getDayFormat(endDate, jalali),\n });\n }, [jalali, numberOfMonths, source]);\n\n return {\n ref,\n source,\n setSource,\n numberOfMonths,\n displayMonths,\n setDisplayMonths,\n };\n}\n","import { Dispatch, SetStateAction, useCallback, useRef, useState } from \"react\";\n\n// `process` may be undefined in browser-only consumers that don't replace\n// `process.env.NODE_ENV` at bundle time. Resolve once at module load.\nconst IS_DEV =\n typeof process !== \"undefined\" &&\n typeof process.env !== \"undefined\" &&\n process.env.NODE_ENV !== \"production\";\n\n/**\n * Controlled/uncontrolled state helper. If `controlled` is defined the value\n * is sourced from the parent each render and `onChange` is invoked when the\n * setter is called. Otherwise internal state is used.\n *\n * The controlled/uncontrolled mode is locked in on mount; switching modes\n * after that is unsupported and will warn in development.\n */\nexport function useControlled<T>(\n controlled: T | undefined,\n defaultValue: T,\n onChange?: (value: T) => void,\n): [T, Dispatch<SetStateAction<T>>] {\n const isControlled = controlled !== undefined;\n const { current: wasControlled } = useRef(isControlled);\n\n if (IS_DEV && isControlled !== wasControlled) {\n // eslint-disable-next-line no-console\n console.warn(\n \"[react-trip-date] A controlled prop was switched between controlled and uncontrolled. Pick one for the lifetime of the component.\",\n );\n }\n\n const [internal, setInternal] = useState<T>(defaultValue);\n const value = isControlled ? (controlled as T) : internal;\n\n const onChangeRef = useRef(onChange);\n onChangeRef.current = onChange;\n\n const setValue: Dispatch<SetStateAction<T>> = useCallback(\n next => {\n if (!wasControlled) {\n setInternal(prev => {\n const resolved =\n typeof next === \"function\" ? (next as (p: T) => T)(prev) : next;\n onChangeRef.current?.(resolved);\n return resolved;\n });\n } else {\n const resolved =\n typeof next === \"function\"\n ? (next as (p: T) => T)(controlled as T)\n : next;\n onChangeRef.current?.(resolved);\n }\n },\n [wasControlled, controlled],\n );\n\n return [value, setValue];\n}\n","import { useState } from \"react\";\nimport { DefaultTheme, ThemeProvider } from \"styled-components\";\n\nimport { Header } from \"../components\";\nimport { Months } from \"./Months\";\nimport { DisplayMonths } from \"../components/DisplayMonths\";\nimport { theme } from \"../constant\";\nimport { RangePickerProps, RangePickerSelectedDays } from \"./rangePicker.type\";\nimport { deepMerge } from \"../libs/mergeObjects\";\nimport { useCalendarController } from \"../libs/useCalendarController\";\nimport { useControlled } from \"../libs/useControlled\";\n\nconst EMPTY_RANGE: RangePickerSelectedDays = { from: \"\", to: \"\" };\n\nexport const RangePicker = ({\n autoResponsive = true,\n allowDisabledDaysSpan = false,\n components,\n disabled = false,\n dayClasses,\n disabledAfterDate,\n disabledBeforeToday = false,\n disabledBeforeDate,\n disabledDays = [],\n initialMonthAndYear,\n jalali = false,\n locale,\n numberOfMonths: numberOfMonthsProp = 1,\n onChange,\n onRangeDateInScreen,\n selectedDays: selectedDaysProp,\n startOfWeek = 1,\n theme: themeProp,\n}: RangePickerProps) => {\n const [selectedDays, setSelectedDays] =\n useControlled<RangePickerSelectedDays>(\n selectedDaysProp,\n selectedDaysProp ?? EMPTY_RANGE,\n onChange,\n );\n const [hoverDay, setHoverDay] = useState<string>();\n\n const initialFocusDate =\n !initialMonthAndYear && selectedDays?.from ? selectedDays.from : undefined;\n\n const {\n ref,\n source,\n setSource,\n numberOfMonths,\n displayMonths,\n setDisplayMonths,\n } = useCalendarController({\n autoResponsive,\n initialMonthAndYear,\n jalali,\n locale,\n numberOfMonthsProp,\n onRangeDateInScreen,\n initialFocusDate,\n });\n\n const mergedTheme = themeProp\n ? (deepMerge(theme, themeProp) as unknown as DefaultTheme)\n : (theme as DefaultTheme);\n\n return (\n <div\n className=\"tp-calendar\"\n ref={ref}\n role=\"group\"\n aria-label=\"Date range picker\"\n >\n <ThemeProvider theme={mergedTheme}>\n <Header\n jalali={jalali}\n source={source}\n setSource={setSource}\n components={components}\n displayMonths={displayMonths}\n numberOfMonths={numberOfMonths}\n setDisplayMonths={setDisplayMonths}\n />\n {displayMonths ? (\n <DisplayMonths\n jalali={jalali}\n setDisplayMonths={setDisplayMonths}\n setSource={setSource}\n source={source}\n />\n ) : (\n <Months\n source={source}\n jalali={jalali}\n disabled={disabled}\n setSource={setSource}\n hoverDay={hoverDay}\n components={components}\n setHoverDay={setHoverDay}\n startOfWeek={startOfWeek}\n disabledDays={disabledDays}\n selectedDays={selectedDays}\n numberOfMonths={numberOfMonths}\n setSelectedDays={setSelectedDays}\n disabledBeforeToday={disabledBeforeToday}\n disabledBeforeDate={disabledBeforeDate}\n disabledAfterDate={disabledAfterDate}\n allowDisabledDaysSpan={allowDisabledDaysSpan}\n dayClasses={dayClasses}\n />\n )}\n </ThemeProvider>\n </div>\n );\n};\n","import { Dayjs } from \"dayjs\";\nimport {\n Dispatch,\n KeyboardEvent,\n MouseEvent,\n MutableRefObject,\n SetStateAction,\n useEffect,\n useRef,\n} from \"react\";\nimport styled from \"styled-components\";\n\nimport { FORMAT_DATE } from \"../constant\";\nimport { DatePickerComponents } from \"./datePicker.type\";\nimport { classNames } from \"../libs/classNames\";\nimport { dayjs } from \"../libs/dayjs-config\";\nimport { getDayFormat } from \"../libs/getDayFormat\";\nimport { handleDayKeyDown } from \"../libs/handleDayKeyDown\";\n\ntype Props = {\n day: Dayjs;\n source: Dayjs;\n jalali: boolean;\n disabled: boolean;\n numberOfMonth: number;\n selectedDays: string[];\n disabledDays: string[];\n numberOfSelectableDays: number;\n disabledBeforeToda