UNPKG

@zag-js/date-picker

Version:

Core logic for the date-picker widget implemented as a state machine

167 lines (166 loc) • 5.42 kB
// src/date-picker.utils.ts import { DateFormatter } from "@internationalized/date"; import { memo } from "@zag-js/core"; import { getDecadeRange } from "@zag-js/date-utils"; import { clampValue, match } from "@zag-js/utils"; function adjustStartAndEndDate(value) { const [startDate, endDate] = value; let result; if (!startDate || !endDate) result = value; else result = startDate.compare(endDate) <= 0 ? value : [endDate, startDate]; return result; } function isDateWithinRange(date, value) { const [startDate, endDate] = value; if (!startDate || !endDate) return false; return startDate.compare(date) <= 0 && endDate.compare(date) >= 0; } function sortDates(values) { return values.slice().filter((date) => date != null).sort((a, b) => a.compare(b)); } function getRoleDescription(view) { return match(view, { year: "calendar decade", month: "calendar year", day: "calendar month" }); } var PLACEHOLDERS = { day: "dd", month: "mm", year: "yyyy" }; function getInputPlaceholder(locale) { return new DateFormatter(locale).formatToParts(/* @__PURE__ */ new Date()).map((item) => PLACEHOLDERS[item.type] ?? item.value).join(""); } var isValidDate = (value) => { return !Number.isNaN(value.day) && !Number.isNaN(value.month) && !Number.isNaN(value.year); }; var defaultTranslations = { dayCell(state) { if (state.unavailable) return `Not available. ${state.valueText}`; if (state.firstInRange) return `Starting range from ${state.valueText}`; if (state.lastInRange) return `Range ending at ${state.valueText}`; if (state.selected) return `Selected date. ${state.valueText}`; return `Choose ${state.valueText}`; }, trigger(open) { return open ? "Close calendar" : "Open calendar"; }, viewTrigger(view) { return match(view, { year: "Switch to month view", month: "Switch to day view", day: "Switch to year view" }); }, presetTrigger(value) { const [start = "", end = ""] = value; return `select ${start} to ${end}`; }, prevTrigger(view) { return match(view, { year: "Switch to previous decade", month: "Switch to previous year", day: "Switch to previous month" }); }, nextTrigger(view) { return match(view, { year: "Switch to next decade", month: "Switch to next year", day: "Switch to next month" }); }, // TODO: Revisit this placeholder() { return { day: "dd", month: "mm", year: "yyyy" }; }, content: "calendar", monthSelect: "Select month", yearSelect: "Select year", clearTrigger: "Clear selected dates", weekColumnHeader: "Wk", weekNumberCell(weekNumber) { return `Week ${weekNumber}`; } }; function viewToNumber(view, fallback) { if (!view) return fallback || 0; return view === "day" ? 0 : view === "month" ? 1 : 2; } function viewNumberToView(viewNumber) { return viewNumber === 0 ? "day" : viewNumber === 1 ? "month" : "year"; } function clampView(view, minView, maxView) { return viewNumberToView( clampValue(viewToNumber(view, 0), viewToNumber(minView, 0), viewToNumber(maxView, 2)) ); } function isAboveMinView(view, minView) { return viewToNumber(view, 0) > viewToNumber(minView, 0); } function isBelowMinView(view, minView) { return viewToNumber(view, 0) < viewToNumber(minView, 0); } function getNextView(view, minView, maxView) { const nextViewNumber = viewToNumber(view, 0) + 1; return clampView(viewNumberToView(nextViewNumber), minView, maxView); } function getPreviousView(view, minView, maxView) { const prevViewNumber = viewToNumber(view, 0) - 1; return clampView(viewNumberToView(prevViewNumber), minView, maxView); } var views = ["day", "month", "year"]; function eachView(cb) { views.forEach((view) => cb(view)); } var getVisibleRangeText = memo( (opts) => [opts.view, opts.startValue.toString(), opts.endValue.toString(), opts.locale], ([view], opts) => { const { startValue, endValue, locale, timeZone, selectionMode } = opts; if (view === "year") { const years = getDecadeRange(startValue.year, { strict: true }); const start2 = years.at(0).toString(); const end2 = years.at(-1).toString(); return { start: start2, end: end2, formatted: `${start2} - ${end2}` }; } if (view === "month") { const formatter2 = new DateFormatter(locale, { year: "numeric", timeZone, calendar: startValue.calendar.identifier }); const start2 = formatter2.format(startValue.toDate(timeZone)); const end2 = formatter2.format(endValue.toDate(timeZone)); const formatted2 = selectionMode === "range" ? `${start2} - ${end2}` : start2; return { start: start2, end: end2, formatted: formatted2 }; } const formatter = new DateFormatter(locale, { month: "long", year: "numeric", timeZone, calendar: startValue.calendar.identifier }); const start = formatter.format(startValue.toDate(timeZone)); const end = formatter.format(endValue.toDate(timeZone)); const formatted = selectionMode === "range" ? `${start} - ${end}` : start; return { start, end, formatted }; } ); export { adjustStartAndEndDate, clampView, defaultTranslations, eachView, getInputPlaceholder, getNextView, getPreviousView, getRoleDescription, getVisibleRangeText, isAboveMinView, isBelowMinView, isDateWithinRange, isValidDate, sortDates };