{"version":3,"file":"CalendarPanel.mjs","names":["datePickerMessages"],"sources":["../../../src/base-ui/DatePicker/CalendarPanel.tsx"],"sourcesContent":["'use client';\n\nimport { cx } from 'antd-style';\nimport dayjs from 'dayjs';\nimport { ChevronLeft, ChevronRight } from 'lucide-react';\nimport { type KeyboardEvent, memo, useEffect, useRef, useState } from 'react';\n\nimport { panelStyles } from '@/base-ui/panelStyles';\nimport datePickerMessages from '@/i18n/resources/en/datePicker';\nimport { useTranslation } from '@/i18n/useTranslation';\nimport Icon from '@/Icon';\n\nimport {\n  buildMonthGrid,\n  type CalendarMode,\n  type DateBounds,\n  getWeekdayLabels,\n  getWeekStart,\n  isDayDisabled,\n  isMonthDisabled,\n  isSameDay,\n  isYearDisabled,\n  moveDayFocus,\n  orderRange,\n} from './calendar';\nimport { styles } from './style';\n\nexport interface CalendarPanelProps {\n  bounds: DateBounds;\n  hovered?: Date | null;\n  mode: CalendarMode;\n  month: Date;\n  onHover?: (date: Date | null) => void;\n  onMonthChange: (month: Date) => void;\n  onSelect: (date: Date) => void;\n  range?: [Date | null, Date | null];\n  selected?: Date | null;\n  showNav?: boolean;\n}\n\nconst MONTHS = Array.from({ length: 12 }, (_, index) => index);\n\nconst CalendarPanel = memo<CalendarPanelProps>(\n  ({\n    bounds,\n    hovered,\n    mode,\n    month,\n    onHover,\n    onMonthChange,\n    onSelect,\n    range,\n    selected,\n    showNav = true,\n  }) => {\n    const { t } = useTranslation(datePickerMessages);\n    const [view, setView] = useState<CalendarMode>(mode);\n    const [focused, setFocused] = useState<Date>(selected ?? month);\n    const gridRef = useRef<HTMLDivElement>(null);\n    const weekStart = getWeekStart();\n    const today = new Date();\n    const current = dayjs(month);\n    const decadeStart = Math.floor(current.year() / 12) * 12;\n    const days = buildMonthGrid(month, weekStart);\n    const focusDay =\n      dayjs(focused).isSame(month, 'month') && !isDayDisabled(focused, bounds)\n        ? focused\n        : (days.find((day) => dayjs(day).isSame(month, 'month') && !isDayDisabled(day, bounds)) ??\n          current.startOf('month').toDate());\n    const keyboardMoveRef = useRef(false);\n\n    useEffect(() => {\n      if (!keyboardMoveRef.current) return;\n      keyboardMoveRef.current = false;\n      gridRef.current?.querySelector<HTMLButtonElement>('[data-focus-target]')?.focus();\n    }, [focused, month]);\n\n    const step = (direction: 1 | -1) => {\n      const unit = view === 'date' ? 'month' : 'year';\n      const amount = view === 'year' ? 12 * direction : direction;\n      onMonthChange(current.add(amount, unit).toDate());\n    };\n\n    const drillUp = () => setView(view === 'date' ? 'month' : 'year');\n\n    const handleDayKey = (event: KeyboardEvent<HTMLButtonElement>) => {\n      let next = moveDayFocus(focusDay, event.key, weekStart);\n      if (!next) return;\n      event.preventDefault();\n      for (let step = 0; next && isDayDisabled(next, bounds) && step < 366; step++) {\n        next = moveDayFocus(next, event.key, weekStart);\n      }\n      if (!next || isDayDisabled(next, bounds)) return;\n      keyboardMoveRef.current = true;\n      setFocused(next);\n      if (!dayjs(next).isSame(month, 'month')) onMonthChange(next);\n    };\n\n    const band = (() => {\n      if (!range?.[0]) return null;\n      const end = range[1] ?? hovered;\n      return end ? orderRange(range[0], end) : null;\n    })();\n\n    const renderDays = () => {\n      return (\n        <div\n          aria-label={current.format('MMMM YYYY')}\n          className={styles.grid}\n          ref={gridRef}\n          role=\"grid\"\n          onMouseLeave={() => onHover?.(null)}\n        >\n          {getWeekdayLabels(weekStart).map((label) => (\n            <span className={cx(styles.weekday, panelStyles.label)} key={label} role=\"columnheader\">\n              {label}\n            </span>\n          ))}\n          {days.map((date) => {\n            const outside = !dayjs(date).isSame(month, 'month');\n            const isStart = !outside && !!band && isSameDay(date, band[0]);\n            const isEnd = !outside && !!band && isSameDay(date, band[1]);\n            const inBand =\n              !outside &&\n              !!band &&\n              dayjs(date).isAfter(band[0], 'day') &&\n              dayjs(date).isBefore(band[1], 'day');\n            const pressed = !outside && (isSameDay(date, selected) || isStart || isEnd);\n            const focusTarget = isSameDay(date, focusDay) && !outside;\n\n            return (\n              <span\n                aria-selected={pressed}\n                key={date.toISOString()}\n                role=\"gridcell\"\n                className={cx(\n                  styles.cell,\n                  inBand && styles.band,\n                  isStart && !isEnd && styles.bandStart,\n                  isEnd && !isStart && styles.bandEnd,\n                )}\n              >\n                <button\n                  aria-label={dayjs(date).format('MMMM D, YYYY')}\n                  aria-pressed={pressed}\n                  className={styles.day}\n                  data-focus-target={focusTarget ? '' : undefined}\n                  data-outside={outside ? '' : undefined}\n                  data-today={isSameDay(date, today) ? '' : undefined}\n                  disabled={isDayDisabled(date, bounds)}\n                  tabIndex={focusTarget ? 0 : -1}\n                  type=\"button\"\n                  onClick={() => onSelect(dayjs(date).startOf('day').toDate())}\n                  onKeyDown={handleDayKey}\n                  onMouseEnter={() => onHover?.(date)}\n                >\n                  {date.getDate()}\n                </button>\n              </span>\n            );\n          })}\n        </div>\n      );\n    };\n\n    const renderMonths = () => (\n      <div className={styles.tiles}>\n        {MONTHS.map((index) => {\n          const date = current.month(index).startOf('month').toDate();\n          return (\n            <button\n              aria-pressed={!!selected && dayjs(selected).isSame(date, 'month')}\n              className={styles.tile}\n              data-today={dayjs(today).isSame(date, 'month') ? '' : undefined}\n              disabled={isMonthDisabled(date, bounds)}\n              key={index}\n              type=\"button\"\n              onClick={() => {\n                if (mode === 'month') return onSelect(date);\n                onMonthChange(date);\n                setView('date');\n              }}\n            >\n              {dayjs(date).format('MMM')}\n            </button>\n          );\n        })}\n      </div>\n    );\n\n    const renderYears = () => (\n      <div className={styles.tiles}>\n        {MONTHS.map((index) => {\n          const date = dayjs(new Date(decadeStart + index, 0, 1)).toDate();\n          return (\n            <button\n              aria-pressed={!!selected && dayjs(selected).isSame(date, 'year')}\n              className={styles.tile}\n              data-today={dayjs(today).isSame(date, 'year') ? '' : undefined}\n              disabled={isYearDisabled(date, bounds)}\n              key={index}\n              type=\"button\"\n              onClick={() => {\n                if (mode === 'year') return onSelect(date);\n                onMonthChange(current.year(decadeStart + index).toDate());\n                setView('month');\n              }}\n            >\n              {decadeStart + index}\n            </button>\n          );\n        })}\n      </div>\n    );\n\n    const title = (() => {\n      if (view === 'date')\n        return (\n          <>\n            {current.format('MMMM')}{' '}\n            <span className={panelStyles.titleMuted}>{current.format('YYYY')}</span>\n          </>\n        );\n      if (view === 'month') return current.format('YYYY');\n      return `${decadeStart} – ${decadeStart + 11}`;\n    })();\n\n    return (\n      <div className={styles.calendar}>\n        <div className={styles.header}>\n          <button\n            className={panelStyles.title}\n            disabled={view === 'year'}\n            type=\"button\"\n            onClick={drillUp}\n          >\n            {title}\n          </button>\n          {showNav && (\n            <div className={styles.navGroup}>\n              <button\n                aria-label={t('datePicker.previous')}\n                className={panelStyles.nav}\n                type=\"button\"\n                onClick={() => step(-1)}\n              >\n                <Icon icon={ChevronLeft} size={14} />\n              </button>\n              <button\n                aria-label={t('datePicker.next')}\n                className={panelStyles.nav}\n                type=\"button\"\n                onClick={() => step(1)}\n              >\n                <Icon icon={ChevronRight} size={14} />\n              </button>\n            </div>\n          )}\n        </div>\n        {view === 'date' && renderDays()}\n        {view === 'month' && renderMonths()}\n        {view === 'year' && renderYears()}\n      </div>\n    );\n  },\n);\n\nCalendarPanel.displayName = 'CalendarPanel';\n\nexport default CalendarPanel;\n"],"mappings":"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"}