UNPKG

react-native-calendars-datepicker

Version:

Customizable date picker for React Native that supports Hijri calendar

549 lines (526 loc) 19.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.YEAR_PAGE_SIZE = exports.VALID_JALALI_LOCALES = exports.JALALI_MONTHS = exports.DATE_FORMAT = exports.CALENDAR_FORMAT = void 0; exports.areDatesOnSameDay = areDatesOnSameDay; exports.cn = cn; exports.dateToUnix = dateToUnix; exports.formatNumber = formatNumber; exports.getDayjs = exports.getDateYear = exports.getDateMonth = exports.getDate = void 0; exports.getDaysInHijriMonth = getDaysInHijriMonth; exports.getDaysInMonth = getDaysInMonth; exports.getEndOfDay = getEndOfDay; exports.getFirstDayOfMonth = getFirstDayOfMonth; exports.getParsedDate = exports.getMonthsArray = exports.getMonths = exports.getMonthName = exports.getMonthDays = exports.getJalaliMonths = exports.getFormatedDate = exports.getFormated = void 0; exports.getStartOfDay = getStartOfDay; exports.getYearRange = exports.getWeekdays = void 0; exports.isDateBetween = isDateBetween; exports.isDateDisabled = isDateDisabled; exports.isMonthDisabled = isMonthDisabled; exports.isValidJalaliLocale = void 0; exports.isYearDisabled = isYearDisabled; exports.removeTime = removeTime; exports.useDeepCompareMemo = useDeepCompareMemo; var _dayjs = _interopRequireDefault(require("dayjs")); var _clsx = require("clsx"); var _tailwindMerge = require("tailwind-merge"); var _react = require("react"); var _lodash = require("lodash"); var _numerals = require("./numerals"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } const CALENDAR_FORMAT = exports.CALENDAR_FORMAT = 'YYYY-MM-DD HH:mm'; const DATE_FORMAT = exports.DATE_FORMAT = 'YYYY-MM-DD'; const YEAR_PAGE_SIZE = exports.YEAR_PAGE_SIZE = 12; const VALID_JALALI_LOCALES = exports.VALID_JALALI_LOCALES = new Set(['fa', 'en']); const JALALI_MONTHS = exports.JALALI_MONTHS = { en: ['Farvardin', 'Ordibehesht', 'Khordad', 'Tir', 'Mordad', 'Shahrivar', 'Mehr', 'Aban', 'Azar', 'Dey', 'Bahman', 'Esfand'], fa: ['فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور', 'مهر', 'آبان', 'آذر', 'دی', 'بهمن', 'اسفند'] }; const isValidJalaliLocale = locale => VALID_JALALI_LOCALES.has(locale); exports.isValidJalaliLocale = isValidJalaliLocale; const getJalaliMonths = locale => JALALI_MONTHS[locale] || JALALI_MONTHS.en; exports.getJalaliMonths = getJalaliMonths; const getMonths = () => _dayjs.default.months(); exports.getMonths = getMonths; const getMonthName = month => _dayjs.default.months()[month]; /** * Converts a date to a dayjs object with optional Islamic calendar support. * * @param {DateType} date - The input date to convert (can be any value dayjs accepts) * @param {CalendarType} [calendar] - Optional calendar type to use ('islamic' for Hijri calendar) * @returns {Dayjs} A dayjs object in either the default or Islamic calendar system * * @example * // Get a standard dayjs object in the Geogorian calendar * const standard = getDayjs('2025-03-20'); * * @example * // Get a dayjs object of a date in the Hijri calendar * const hijri = getDayjs('2025-03-20', 'islamic'); */ exports.getMonthName = getMonthName; const getDayjs = (date, calendar) => { if (date === undefined) { date = new Date(); } if (calendar === 'islamic') { return (0, _dayjs.default)(date).toCalendarSystem('islamic'); } return (0, _dayjs.default)(date); }; /** * Get months array * * @returns months array */ exports.getDayjs = getDayjs; const getMonthsArray = ({ calendar, locale }) => { const monthNames = calendar === 'jalali' ? getJalaliMonths(locale) : _dayjs.default.months(); const monthShortNames = calendar === 'jalali' ? getJalaliMonths(locale) : _dayjs.default.monthsShort(); return monthNames.map((name, index) => ({ index, name: { full: name, short: monthShortNames[index] || '' }, isSelected: false })); }; /** * Get weekdays * * @param locale - locale * @param firstDayOfWeek - first day of week * @param format - format short, min or full * * @returns weekdays */ exports.getMonthsArray = getMonthsArray; const getWeekdays = (locale, firstDayOfWeek) => { _dayjs.default.locale(locale); const weekdayNames = _dayjs.default.weekdays(); const weekdayShortNames = _dayjs.default.weekdaysShort(); const weekdayMinNames = _dayjs.default.weekdaysMin(); let weekdays = weekdayNames.map((name, index) => ({ index, name: { full: name, short: weekdayShortNames[index] || '', min: weekdayMinNames[index] || '' } })); if (firstDayOfWeek > 0) { weekdays = [...weekdays.slice(firstDayOfWeek, weekdays.length), ...weekdays.slice(0, firstDayOfWeek)]; } return weekdays; }; exports.getWeekdays = getWeekdays; const getFormated = (date, calendar) => getDayjs(date, calendar).format(CALENDAR_FORMAT); exports.getFormated = getFormated; const getDateMonth = (date, calendar) => getDayjs(date, calendar).month(); exports.getDateMonth = getDateMonth; const getDateYear = (date, calendar) => getDayjs(date, calendar).year(); /** * Check if two dates are on the same day * * @param a - date to check * @param b - date to check * @param {CalendarType} [calendar] - Optional calendar type to use ('islamic' for Hijri calendar) * * @returns true if dates are on the same day, false otherwise */ exports.getDateYear = getDateYear; function areDatesOnSameDay(a, b, calendar) { if (!a || !b) { return false; } const date_a = getDayjs(a, calendar).format(DATE_FORMAT); const date_b = getDayjs(b, calendar).format(DATE_FORMAT); return date_a === date_b; } /** * Check if date is between two dates * * @param date - date to check * @param options - options * @param {CalendarType} [calendar] - Optional calendar type to use ('islamic' for Hijri calendar) * * @returns true if date is between two dates, false otherwise */ function isDateBetween(date, { startDate, endDate }, calendar) { if (!startDate || !endDate) { return false; } return getDayjs(date, calendar) <= endDate && getDayjs(date, calendar) >= startDate; } /** * Check if date is disabled * * @param date - date to check * @param options - options * * @returns true if date is disabled, false otherwise */ function isDateDisabled(date, { minDate, maxDate, enabledDates, disabledDates, calendar = 'gregory' }) { if (minDate && date.isBefore(getDayjs(minDate, calendar).startOf('day'))) { return true; } if (maxDate && date.isAfter(getDayjs(maxDate, calendar).endOf('day'))) { return true; } if (enabledDates) { if (Array.isArray(enabledDates)) { const isEnabled = enabledDates.some(enabledDate => areDatesOnSameDay(date, enabledDate, calendar)); return !isEnabled; } else if (enabledDates instanceof Function) { return !enabledDates(date); } } else if (disabledDates) { if (Array.isArray(disabledDates)) { const isDisabled = disabledDates.some(disabledDate => areDatesOnSameDay(date, disabledDate, calendar)); return isDisabled; } else if (disabledDates instanceof Function) { return disabledDates(date); } } return false; } /** * Check if year is disabled * * @param year - year to check * @param options - options * @param {CalendarType} [calendar] - Optional calendar type to use ('islamic' for Hijri calendar) * * @returns true if year is disabled, false otherwise */ function isYearDisabled(year, { minDate, maxDate }, calendar) { if (minDate && year < getDateYear(minDate, calendar)) return true; if (maxDate && year > getDateYear(maxDate, calendar)) return true; return false; } /** * Check if month is disabled * * @param month - month to check * @param date - date to check * @param options - options * @param {CalendarType} [calendar] - Optional calendar type to use ('islamic' for Hijri calendar) * * @returns true if month is disabled, false otherwise */ function isMonthDisabled(month, date, { minDate, maxDate }, calendar) { if (minDate && month < getDateMonth(minDate, calendar) && getDateYear(date, calendar) === getDateYear(minDate, calendar)) return true; if (maxDate && month > getDateMonth(maxDate, calendar) && getDateYear(date, calendar) === getDateYear(maxDate, calendar)) return true; return false; } /** * Get formated date * * @param date - date to get formated date from * @param format - format to get formated date from * @param {CalendarType} [calendar] - Optional calendar type to use ('islamic' for Hijri calendar) * * @returns formated date */ const getFormatedDate = (date, format, calendar) => getDayjs(date, calendar).format(format); /** * Get date * * @param date - date to get * @param {CalendarType} [calendar] - Optional calendar type to use ('islamic' for Hijri calendar) * * @returns date */ exports.getFormatedDate = getFormatedDate; const getDate = (date, calendar) => getDayjs(date, calendar); /** * Get year range * * @param year - year to get year range from * * @returns year range */ exports.getDate = getDate; const getYearRange = year => { const endYear = YEAR_PAGE_SIZE * Math.ceil(year / YEAR_PAGE_SIZE); let startYear = endYear === year ? endYear : endYear - YEAR_PAGE_SIZE; if (startYear < 0) { startYear = 0; } return Array.from({ length: YEAR_PAGE_SIZE }, (_, i) => startYear + i); }; /** * Determines the number of days in a specific month of the Islamic (Hijri) calendar. * * This function calculates the number of days in a given Hijri month by checking * the last day's representation in the Islamic calendar. Most Hijri months are * either 29 or 30 days long. * * @param {dayjs.Dayjs} date - A dayjs object representing a reference date in the Islamic calendar * @param {number} month - The month number (0-11) to check the length of * @returns {number} The number of days in the specified Hijri month (29 or 30) * * @example * // Get the number of days in the 9th month (Ramadan) of the current Hijri year * const daysInRamadan = getDaysInHijriMonth(dayjs().calendar('islamic'), 8); * * @example * // Determine days in a specific Hijri month * const specificMonth = getDaysInHijriMonth(dayjs('1445-04-01').toCalendarSystem('islamic'), 5); * * @note * - The function uses the Umm al-Qura calendar system (en-SA locale) * - Month indexing starts at 0 (0 = Muharram, 11 = Dhu al-Hijjah) * - Relies on locale-specific Islamic calendar conversion */ exports.getYearRange = getYearRange; function getDaysInHijriMonth(date, month) { const lastDay = date.month(month).date(30); const lastHijriDay = new Date(lastDay.toString()).toLocaleDateString('en-SA-u-ca-islamic-umalqura'); return lastHijriDay.split('/')[0] === '30' ? 30 : 29; } /** * Get days in month * * @param date - date to get days in month from * @param showOutsideDays - whether to show outside days * @param firstDayOfWeek - first day of week, number 0-6, 0 – Sunday, 6 – Saturday * @param {CalendarType} [calendar] - Optional calendar type to use ('islamic' for Hijri calendar) * * @returns days in month */ function getDaysInMonth(date, showOutsideDays, firstDayOfWeek, calendar) { const currentDate = getDayjs(date, calendar); let daysInCurrentMonth = currentDate.daysInMonth(); let prevMonthDays = currentDate.add(-1, 'month').daysInMonth(); if (calendar === 'islamic') { daysInCurrentMonth = getDaysInHijriMonth(currentDate, currentDate.month()); prevMonthDays = getDaysInHijriMonth(currentDate, currentDate.month() - 1); } const firstDay = currentDate.date(1 - firstDayOfWeek); const prevMonthOffset = firstDay.day() % 7; const daysInPrevMonth = showOutsideDays ? prevMonthOffset : 0; const monthDaysOffset = prevMonthOffset + daysInCurrentMonth; const daysInNextMonth = showOutsideDays ? monthDaysOffset > 35 ? 42 - monthDaysOffset : 35 - monthDaysOffset : 0; const fullDaysInMonth = daysInPrevMonth + daysInCurrentMonth + daysInNextMonth; return { prevMonthDays, prevMonthOffset, daysInCurrentMonth, daysInNextMonth, fullDaysInMonth }; } /** * Get first day of month * * @param date - date to get first day of month from * @param firstDayOfWeek - first day of week, number 0-6, 0 – Sunday, 6 – Saturday * @param {CalendarType} [calendar] - Optional calendar type to use ('islamic' for Hijri calendar) * * @returns first day of month */ function getFirstDayOfMonth(date, firstDayOfWeek, calendar) { const d = getDate(date, calendar); return d.date(1 - firstDayOfWeek).day(); } /** * Get start of day * * @param date - date to get start of day from * @param {CalendarType} [calendar] - Optional calendar type to use ('islamic' for Hijri calendar) * * @returns start of day */ function getStartOfDay(date, calendar) { return getDayjs(date, calendar).startOf('day'); } /** * Get end of day * * @param date - date to get end of day from * @param {CalendarType} [calendar] - Optional calendar type to use ('islamic' for Hijri calendar) * * @returns end of day */ function getEndOfDay(date, calendar) { return getDayjs(date, calendar).endOf('day'); } /** * Convert date to unix timestamp * * @param date - date to convert * @param {CalendarType} [calendar] - Optional calendar type to use ('islamic' for Hijri calendar) * * @returns unix timestamp */ function dateToUnix(date, calendar) { return getDayjs(date, calendar).unix(); } /** * Remove time from date * * @param date - date to remove time from * @param {CalendarType} [calendar] - Optional calendar type to use ('islamic' for Hijri calendar) * * @returns date with time removed */ function removeTime(date, timeZone, calendar) { return date ? getDayjs(_dayjs.default.tz(date, timeZone).startOf('day'), calendar) : undefined; } /** * Get detailed date object * * @param date Get detailed date object * @param {CalendarType} [calendar] - Optional calendar type to use ('islamic' for Hijri calendar) * * @returns parsed date object */ const getParsedDate = (date, calendar) => { const dayjsDate = getDayjs(date, calendar); return { year: dayjsDate.year(), month: dayjsDate.month(), hour: dayjsDate.hour(), hour12: parseInt(dayjsDate.format('hh')), minute: dayjsDate.minute(), period: dayjsDate.format('A') }; }; /** * Calculate month days array based on current date * * @param datetime - The current date that selected * @param showOutsideDays * @param minDate - min selectable date * @param maxDate - max selectable date * @param firstDayOfWeek - first day of week, number 0-6, 0 – Sunday, 6 – Saturday * @param enabledDates - array of enabled dates, or a function that returns true for a given date (takes precedence over disabledDates) * @param disabledDates - array of disabled dates, or a function that returns true for a given date * @param prevMonthDays - number of days in the previous month * @param prevMonthOffset - number of days to offset the previous month * @param daysInCurrentMonth - number of days in the current month * @param daysInNextMonth - number of days in the next month * @param {CalendarType} [calendar] - Optional calendar type to use ('islamic' for Hijri calendar) * * @returns days array based on current date */ exports.getParsedDate = getParsedDate; const getMonthDays = (datetime, showOutsideDays, minDate, maxDate, firstDayOfWeek, enabledDates, disabledDates, prevMonthDays, prevMonthOffset, daysInCurrentMonth, daysInNextMonth, numerals, calendar) => { const date = getDayjs(datetime, calendar); const prevDays = showOutsideDays ? Array.from({ length: prevMonthOffset }, (_, index) => { const number = index + (prevMonthDays - prevMonthOffset + 1); const thisDay = date.month(date.month() - 1).date(number); return generateCalendarDay(number, thisDay, minDate, maxDate, enabledDates, disabledDates, false, index + 1, firstDayOfWeek, numerals, calendar); }) : Array(prevMonthOffset).fill(null); const currentDays = Array.from({ length: daysInCurrentMonth }, (_, index) => { const day = index + 1; const thisDay = date.date(day); return generateCalendarDay(day, thisDay, minDate, maxDate, enabledDates, disabledDates, true, prevMonthOffset + day, firstDayOfWeek, numerals, calendar); }); const nextDays = Array.from({ length: daysInNextMonth }, (_, index) => { const day = index + 1; const thisDay = date.month(date.month() + 1).date(day); return generateCalendarDay(day, thisDay, minDate, maxDate, enabledDates, disabledDates, false, daysInCurrentMonth + prevMonthOffset + day, firstDayOfWeek, numerals, calendar); }); return [...prevDays, ...currentDays, ...nextDays]; }; /** * Generate day object for displaying inside day cell * * @param number - number of day * @param date - calculated date based on day, month, and year * @param minDate - min selectable date * @param maxDate - max selectable date * @param enabledDates - array of enabled dates, or a function that returns true for a given date (takes precedence over disabledDates) * @param disabledDates - array of disabled dates, or a function that returns true for a given date * @param isCurrentMonth - define the day is in the current month * @param dayOfMonth - number the day in the current month * @param firstDayOfWeek - first day of week, number 0-6, 0 – Sunday, 6 – Saturday * @param {CalendarType} [calendar] - Optional calendar type to use ('islamic' for Hijri calendar) * * @returns days object based on current date */ exports.getMonthDays = getMonthDays; const generateCalendarDay = (number, date, minDate, maxDate, enabledDates, disabledDates, isCurrentMonth, dayOfMonth, firstDayOfWeek, numerals, calendar) => { const startOfWeek = getDayjs(date, calendar).startOf('week').add(firstDayOfWeek, 'day'); return { text: formatNumber(number, numerals), number, date: date, isDisabled: isDateDisabled(date, { minDate, maxDate, enabledDates, disabledDates }), isCurrentMonth, dayOfMonth, isStartOfWeek: date.isSame(startOfWeek, 'day'), isEndOfWeek: date.day() === (firstDayOfWeek + 6) % 7 }; }; function cn(...inputs) { return (0, _tailwindMerge.twMerge)((0, _clsx.clsx)(inputs)); } /** * Deep compare memo * * @param value - value to compare * @param deps - dependencies * * @returns memoized value */ function useDeepCompareMemo(value, deps) { const ref = (0, _react.useRef)(); const depsRef = (0, _react.useRef)(); if (!depsRef.current || !deps.every((dep, i) => (0, _lodash.isEqual)(dep, depsRef.current[i]))) { ref.current = value; depsRef.current = deps; } return ref.current; } function getDigitMap(numerals) { const digitMap = {}; const numeralDigits = _numerals.numeralSystems[numerals]; for (let i = 0; i < 10; i++) { digitMap[i.toString()] = numeralDigits[i]; } return digitMap; } function replaceDigits(input, numerals) { const digitMap = getDigitMap(numerals); return input.replace(/\d/g, digit => digitMap[digit] || digit); } function formatNumber(value, numerals) { return replaceDigits(value.toString(), numerals); } //# sourceMappingURL=utils.js.map