{"version":3,"file":"date-picker.cjs","names":[],"sources":["../../src/core/date-picker.ts"],"sourcesContent":["import { format, parse, Temporal } from '@vielzeug/tempo';\n\n// ── Public ISO helpers (single source of truth for both components) ───────────\n\n/**\n * Parses an ISO 8601 date string (`yyyy-MM-dd`) into a `Temporal.PlainDate`.\n * Returns `null` for any invalid / empty input — never throws.\n */\nexport function parseIso(iso: string | undefined | null): Temporal.PlainDate | null {\n  if (!iso) return null;\n\n  try {\n    return parse(iso, { as: 'plainDate' });\n  } catch {\n    return null;\n  }\n}\n\n/**\n * Serialises a `Temporal.PlainDate` (or `null`) to an ISO 8601 string (`yyyy-MM-dd`).\n * Returns `null` when the input is `null`.\n */\nexport function toIsoString(date: Temporal.PlainDate | null): string | null {\n  return date ? date.toString() : null;\n}\n\n/**\n * Formats a `Temporal.PlainDate` for display in a given locale.\n * e.g. \"15 Jun 2025\" (default medium pattern).\n */\nexport function formatDisplayDate(date: Temporal.PlainDate, locale: string): string {\n  return format(date, { intl: { day: 'numeric', month: 'short', year: 'numeric' }, locale, timeZone: 'UTC' });\n}\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type DatePickerView = 'day' | 'month' | 'year';\n\nexport type DateCell = {\n  /** Day-of-month number (1–31) */\n  day: number;\n  isDisabled: boolean;\n  /** ISO date string yyyy-MM-dd — stable key for rendering */\n  iso: string;\n  isOutsideMonth: boolean;\n  isSelected: boolean;\n  isToday: boolean;\n  plain: Temporal.PlainDate;\n};\n\nexport type MonthCell = {\n  isDisabled: boolean;\n  isSelected: boolean;\n  label: string;\n  /** 1-based month number (Temporal convention: 1 = January … 12 = December) */\n  month: number;\n  shortLabel: string;\n};\n\nexport type YearCell = {\n  isDisabled: boolean;\n  isSelected: boolean;\n  year: number;\n};\n\nexport type DatePickerControlOptions = {\n  /** Locale for day/month names, defaults to `navigator.language` or `'en'` */\n  locale?: string;\n  /** Maximum selectable date (inclusive) */\n  max?: Temporal.PlainDate | null;\n  /** Minimum selectable date (inclusive) */\n  min?: Temporal.PlainDate | null;\n  /** Called when a date is committed by the user */\n  onChange: (date: Temporal.PlainDate | null) => void;\n  /** Currently selected date */\n  value?: Temporal.PlainDate | null;\n  /**\n   * Which days of the week to disable (0 = Sunday … 6 = Saturday).\n   * @example [0, 6] disables weekends\n   */\n  weekendDays?: number[];\n};\n\nexport type DatePickerControl = {\n  /** Ordered day cells for the visible month grid (includes leading/trailing days) */\n  dayCells(): DateCell[];\n  /** Month currently shown in the header (1-indexed, Temporal convention) */\n  displayMonth(): number;\n  /** Year currently shown in the header */\n  displayYear(): number;\n  /** Jump to a specific display month/year without selecting */\n  goTo(year: number, month: number): void;\n  /** All 12 month cells */\n  monthCells(): MonthCell[];\n  /** Move display month forward by one */\n  nextMonth(): void;\n  /** Move display year forward by one */\n  nextYear(): void;\n  /** Move display month backward by one */\n  prevMonth(): void;\n  /** Move display year backward by one */\n  prevYear(): void;\n  /** Select a date. Pass null to clear. */\n  select(date: Temporal.PlainDate | null): void;\n  /** The currently selected date, or null */\n  selected(): Temporal.PlainDate | null;\n  /** Switch the calendar view */\n  setView(view: DatePickerView): void;\n  /** Currently rendered calendar view */\n  view(): DatePickerView;\n  /** Short week-day labels in locale order e.g. [\"Su\",\"Mo\",…] */\n  weekdayLabels(): string[];\n  /** Year cells for the visible decade window */\n  yearCells(): YearCell[];\n};\n\n// ── Internal helpers ──────────────────────────────────────────────────────────\n\n/**\n * Returns today as a `Temporal.PlainDate` in the ISO calendar.\n * @internal\n */\nfunction todayPlain(): Temporal.PlainDate {\n  return Temporal.Now.plainDateISO();\n}\n\n/**\n * Converts a `Temporal.PlainDate.dayOfWeek` value (1=Mon…7=Sun) to the\n * 0-based weekday index (0=Sun…6=Sat) used in the `weekendDays` prop.\n * @internal\n */\nfunction temporalDowToIndex(dow: number): number {\n  // Temporal: 1=Mon, 2=Tue, …, 7=Sun  →  0=Sun, 1=Mon, …, 6=Sat\n  return dow % 7;\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * Pure, framework-agnostic date-picker state machine backed by `Temporal.PlainDate`.\n *\n * All state is held in plain mutable variables — suitable for wrapping in any\n * reactive layer (ore `signal`, Vue ref, etc.). The factory returns a stable\n * handle object; callers are responsible for reactivity.\n *\n * Options with getter-based live bindings (e.g. `get min() { ... }`) are read\n * on every call so the control always reflects the latest reactive state.\n *\n * @example\n * ```ts\n * const ctrl = createDatePickerControl({\n *   value: Temporal.PlainDate.from('2025-06-15'),\n *   locale: 'en-US',\n *   onChange: (date) => console.log(date?.toString()),\n * });\n *\n * ctrl.nextMonth();\n * ctrl.select(Temporal.PlainDate.from('2025-07-04'));\n * ```\n */\nexport function createDatePickerControl(options: DatePickerControlOptions): DatePickerControl {\n  // ── Mutable state ─────────────────────────────────────────────────────────\n\n  let _selected: Temporal.PlainDate | null = options.value ?? null;\n  let _view: DatePickerView = 'day';\n\n  const initial = _selected ?? todayPlain();\n\n  let _displayYear = initial.year;\n  let _displayMonth = initial.month; // 1-indexed (Temporal convention)\n\n  // ── Live option accessors (always read from options for reactive compat) ───\n\n  function locale(): string {\n    return options.locale ?? (typeof navigator !== 'undefined' ? navigator.language : 'en');\n  }\n\n  function weekendDays(): number[] {\n    return options.weekendDays ?? [];\n  }\n\n  // ── Range / disabled helpers ───────────────────────────────────────────────\n\n  function isOutOfRange(date: Temporal.PlainDate): boolean {\n    if (options.min && Temporal.PlainDate.compare(date, options.min) < 0) return true;\n\n    if (options.max && Temporal.PlainDate.compare(date, options.max) > 0) return true;\n\n    return false;\n  }\n\n  function isDayDisabled(date: Temporal.PlainDate): boolean {\n    if (isOutOfRange(date)) return true;\n\n    if (weekendDays().includes(temporalDowToIndex(date.dayOfWeek))) return true;\n\n    return false;\n  }\n\n  // ── Grid builders ─────────────────────────────────────────────────────────\n\n  function buildDayCells(): DateCell[] {\n    const todayDate = todayPlain();\n    const firstOfMonth = Temporal.PlainDate.from({ day: 1, month: _displayMonth, year: _displayYear });\n\n    // Sunday-start grid: Temporal dayOfWeek is 1=Mon…7=Sun, convert to 0=Sun offset\n    const startOffset = temporalDowToIndex(firstOfMonth.dayOfWeek);\n    const cells: DateCell[] = [];\n\n    // Leading days from previous month\n    for (let i = startOffset - 1; i >= 0; i--) {\n      const d = firstOfMonth.subtract({ days: i + 1 });\n\n      cells.push({\n        day: d.day,\n        isDisabled: isDayDisabled(d),\n        isOutsideMonth: true,\n        iso: d.toString(),\n        isSelected: _selected !== null && Temporal.PlainDate.compare(d, _selected) === 0,\n        isToday: Temporal.PlainDate.compare(d, todayDate) === 0,\n        plain: d,\n      });\n    }\n\n    // Days in current month\n    const daysInMonth = firstOfMonth.daysInMonth;\n\n    for (let day = 1; day <= daysInMonth; day++) {\n      const d = Temporal.PlainDate.from({ day, month: _displayMonth, year: _displayYear });\n\n      cells.push({\n        day,\n        isDisabled: isDayDisabled(d),\n        isOutsideMonth: false,\n        iso: d.toString(),\n        isSelected: _selected !== null && Temporal.PlainDate.compare(d, _selected) === 0,\n        isToday: Temporal.PlainDate.compare(d, todayDate) === 0,\n        plain: d,\n      });\n    }\n\n    // Trailing days to fill last row (always end at multiple of 7)\n    const remaining = (7 - (cells.length % 7)) % 7;\n    const firstOfNext = firstOfMonth.add({ months: 1 });\n\n    for (let i = 0; i < remaining; i++) {\n      const d = firstOfNext.add({ days: i });\n\n      cells.push({\n        day: d.day,\n        isDisabled: isDayDisabled(d),\n        isOutsideMonth: true,\n        iso: d.toString(),\n        isSelected: _selected !== null && Temporal.PlainDate.compare(d, _selected) === 0,\n        isToday: Temporal.PlainDate.compare(d, todayDate) === 0,\n        plain: d,\n      });\n    }\n\n    return cells;\n  }\n\n  function buildMonthCells(): MonthCell[] {\n    const cells: MonthCell[] = [];\n    const loc = locale();\n\n    for (let m = 1; m <= 12; m++) {\n      const plain = Temporal.PlainDate.from({ day: 1, month: m, year: _displayYear });\n      const lastOfMonth = plain.with({ day: plain.daysInMonth });\n\n      const isDisabled =\n        (options.min !== null && options.min !== undefined\n          ? Temporal.PlainDate.compare(lastOfMonth, options.min) < 0\n          : false) ||\n        (options.max !== null && options.max !== undefined\n          ? Temporal.PlainDate.compare(plain, options.max) > 0\n          : false);\n\n      cells.push({\n        isDisabled,\n        isSelected: _selected !== null && _selected.year === _displayYear && _selected.month === m,\n        label: format(plain, { intl: { month: 'long' }, locale: loc, timeZone: 'UTC' }),\n        month: m,\n        shortLabel: format(plain, { intl: { month: 'short' }, locale: loc, timeZone: 'UTC' }),\n      });\n    }\n\n    return cells;\n  }\n\n  function buildYearCells(): YearCell[] {\n    const decadeStart = Math.floor(_displayYear / 10) * 10;\n    const cells: YearCell[] = [];\n\n    for (let y = decadeStart; y < decadeStart + 12; y++) {\n      const firstOfYear = Temporal.PlainDate.from({ day: 1, month: 1, year: y });\n      const lastOfYear = Temporal.PlainDate.from({ day: 31, month: 12, year: y });\n\n      const isDisabled =\n        (options.min !== null && options.min !== undefined\n          ? Temporal.PlainDate.compare(lastOfYear, options.min) < 0\n          : false) ||\n        (options.max !== null && options.max !== undefined\n          ? Temporal.PlainDate.compare(firstOfYear, options.max) > 0\n          : false);\n\n      cells.push({\n        isDisabled,\n        isSelected: _selected !== null && _selected.year === y,\n        year: y,\n      });\n    }\n\n    return cells;\n  }\n\n  function buildWeekdayLabels(): string[] {\n    // 2024-01-07 is a Sunday — use it as the Sunday anchor\n    const sunday = parse('2024-01-07', { as: 'plainDate' });\n    const loc = locale();\n\n    return Array.from({ length: 7 }, (_, i) =>\n      format(sunday.add({ days: i }), { intl: { weekday: 'short' }, locale: loc, timeZone: 'UTC' }),\n    );\n  }\n\n  // ── Handle ────────────────────────────────────────────────────────────────\n\n  return {\n    dayCells: buildDayCells,\n    displayMonth: () => _displayMonth,\n    displayYear: () => _displayYear,\n\n    goTo(year: number, month: number): void {\n      if (!Number.isFinite(year)) return;\n\n      _displayYear = year;\n      _displayMonth = Math.max(1, Math.min(12, month));\n    },\n\n    monthCells: buildMonthCells,\n\n    nextMonth(): void {\n      if (_displayMonth === 12) {\n        _displayMonth = 1;\n        _displayYear++;\n      } else {\n        _displayMonth++;\n      }\n    },\n\n    nextYear(): void {\n      _displayYear++;\n    },\n\n    prevMonth(): void {\n      if (_displayMonth === 1) {\n        _displayMonth = 12;\n        _displayYear--;\n      } else {\n        _displayMonth--;\n      }\n    },\n\n    prevYear(): void {\n      _displayYear--;\n    },\n\n    select(date: Temporal.PlainDate | null): void {\n      if (date !== null && isDayDisabled(date)) return;\n\n      _selected = date;\n\n      if (_selected) {\n        _displayYear = _selected.year;\n        _displayMonth = _selected.month;\n      }\n\n      options.onChange(_selected);\n    },\n\n    selected: () => _selected,\n\n    setView(view: DatePickerView): void {\n      _view = view;\n    },\n\n    view: () => _view,\n\n    weekdayLabels: buildWeekdayLabels,\n\n    yearCells: buildYearCells,\n  };\n}\n"],"mappings":"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"}