{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "calendar",
  "title": "Calendar",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-angular/components/calendar/calendar.component.ts",
      "content": "import { Component, ViewEncapsulation, ChangeDetectionStrategy } from '@angular/core'\nimport { cn } from '@/lib/utils'\nimport { buttonVariants } from '@/ui/button/button.component'\nimport {\n  DAY_PICKER_TEMPLATE,\n  UiDayContentDirective,\n  UiDayPickerBase,\n  narrowWeekday,\n  type DayPickerClassNames,\n  type DayPickerFormatters,\n} from './day-picker'\n\n// Keyframes the React Calendar injects into <head> (copied verbatim).\nconst CAL_STYLE_CONTENT = `\n@keyframes calendar-day-pop {\n  0% { transform: scale(0.86); }\n  70% { transform: scale(1.06); }\n  100% { transform: scale(1); }\n}\n@keyframes calendar-month-in {\n  from { opacity: 0; transform: translateY(4px); }\n  to { opacity: 1; transform: translateY(0); }\n}\n@media (prefers-reduced-motion: reduce) {\n  [data-slot='calendar'] [class*='animate-\\\\[calendar-'] {\n    animation: none !important;\n  }\n}\n`\n\n/** The React Calendar's `classNames` map (react-day-picker keys), before consumer overrides. */\nexport function calendarClassNames(classNames?: DayPickerClassNames): DayPickerClassNames {\n  return {\n    months: 'relative flex flex-col gap-y-4 sm:flex-row sm:gap-x-4 sm:gap-y-0',\n    month: cn(\n      'flex flex-1 flex-col gap-4 motion-safe:animate-[calendar-month-in_220ms_cubic-bezier(0.22,1,0.36,1)_both]',\n      classNames?.['month'],\n    ),\n    month_caption: 'flex items-center justify-center',\n    caption_label: 'text-sm font-medium',\n    nav: 'absolute inset-x-0 top-0 flex items-center justify-between gap-1',\n    button_previous: cn(\n      buttonVariants({ variant: 'outline' }),\n      'size-9 bg-transparent p-0 opacity-70 hover:opacity-100 focus-visible:opacity-100',\n    ),\n    button_next: cn(\n      buttonVariants({ variant: 'outline' }),\n      'size-9 bg-transparent p-0 opacity-70 hover:opacity-100 focus-visible:opacity-100',\n    ),\n    month_grid: 'w-full border-collapse space-y-1',\n    weekdays: 'flex',\n    weekday: 'text-muted-foreground flex-1 rounded-md text-xs font-normal',\n    week: 'mt-2 flex w-full',\n    day: cn(\n      'relative flex-1 p-0 text-center text-sm focus-within:relative focus-within:z-20',\n      '[&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-range-end)]:rounded-r-md',\n      '[&:has([aria-selected].day-outside)]:bg-accent/50 first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md',\n    ),\n    day_button: cn(\n      buttonVariants({ variant: 'ghost' }),\n      'size-9 cursor-pointer p-0 font-normal transition-[color,background-color,transform,box-shadow] duration-150 aria-selected:opacity-100',\n    ),\n    selected:\n      'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground shadow-sm motion-safe:animate-[calendar-day-pop_220ms_cubic-bezier(0.22,1.4,0.36,1)_both] [&>button]:bg-primary [&>button]:text-primary-foreground [&>button:hover]:bg-primary [&>button:hover]:text-primary-foreground',\n    today: '[&:not([aria-selected])]:bg-accent [&:not([aria-selected])]:text-accent-foreground',\n    outside: 'day-outside text-muted-foreground aria-selected:text-muted-foreground',\n    disabled: 'text-muted-foreground opacity-50',\n    range_start:\n      'day-range-start rounded-l-md motion-safe:animate-[calendar-day-pop_220ms_cubic-bezier(0.22,1.4,0.36,1)_both]',\n    range_end:\n      'day-range-end rounded-r-md motion-safe:animate-[calendar-day-pop_220ms_cubic-bezier(0.22,1.4,0.36,1)_both]',\n    range_middle: 'aria-selected:bg-accent aria-selected:text-accent-foreground transition-colors duration-200',\n    hidden: 'invisible',\n    ...classNames,\n  }\n}\n\n/**\n * Angular port of the UIPKGE React Calendar (react-day-picker v10 + the registry's\n * classNames map). Same props with React names -- `mode`, `selected` + `(select)`,\n * `disabled` matchers, `numberOfMonths`, `captionLayout`, `locale`, `weekStartsOn`,\n * `startMonth` / `endMonth`, `fixedWeeks`, `showOutsideDays` (default true) -- the same\n * DOM and class strings, the same selection / navigation / keyboard behaviour, and the\n * same entrance keyframes. Pair it with a Popover or use it inline.\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-calendar, [ui-calendar]',\n  standalone: true,\n  imports: [UiDayContentDirective],\n  encapsulation: ViewEncapsulation.None,\n  styles: [CAL_STYLE_CONTENT],\n  host: {\n    '[class]': 'hostClass',\n    '[attr.lang]': 'lang ?? localeCode',\n    '[attr.dir]': 'dir ?? null',\n    '[attr.data-mode]': 'mode ?? null',\n    '[attr.data-required]': 'required ? \"true\" : null',\n    '[attr.data-multiple-months]': 'dataMultipleMonths',\n    '[attr.data-uipkge]': '\"\"',\n    '[attr.data-slot]': 'slot',\n  },\n  template: DAY_PICKER_TEMPLATE,\n})\nexport class UiCalendarComponent extends UiDayPickerBase {\n  protected readonly slot = 'calendar'\n\n  constructor() {\n    super()\n    // React Calendar defaults showOutsideDays to true.\n    this.showOutsideDays = true\n  }\n\n  get hostClass(): string {\n    // Custom-element host: display utility first; the rest is rdp's root class + cn('p-3', className).\n    return cn('block', [this.cls['root'], cn('p-3', this.className)].join(' '))\n  }\n\n  protected componentClassNames(): DayPickerClassNames {\n    return calendarClassNames(this.classNames)\n  }\n\n  protected override componentFormatters(): Partial<DayPickerFormatters> {\n    return { formatWeekdayName: narrowWeekday }\n  }\n}\n",
      "type": "registry:ui",
      "target": "~/src/app/components/ui/calendar/calendar.component.ts"
    },
    {
      "path": "packages/registry-angular/components/calendar/day-picker.ts",
      "content": "import {\n  ChangeDetectorRef,\n  Directive,\n  ElementRef,\n  EventEmitter,\n  Input,\n  OnChanges,\n  OnDestroy,\n  Output,\n  TemplateRef,\n  ViewContainerRef,\n  booleanAttribute,\n  inject,\n  type EmbeddedViewRef,\n} from '@angular/core'\n\n/**\n * Angular port of the react-day-picker v10 engine the React registry's Calendar and\n * RangeCalendar wrap. Same DOM (root > months > nav + month > caption + grid), same\n * `classNames` keys and rdp-* defaults, same modifier order on each cell, same\n * data-* / aria attributes, same selection rules (single / multiple / range with\n * min / max / required / resetOnSelect / excludeDisabled), same navigation bounds\n * (startMonth / endMonth / captionLayout dropdowns) and the same roving-focus keyboard\n * model (arrows, Shift+arrows, PageUp/Down, Home/End). Native `Date` only.\n */\n\nexport type DateRange = { from: Date | undefined; to?: Date | undefined }\nexport type DateBefore = { before: Date }\nexport type DateAfter = { after: Date }\nexport type DateInterval = { before: Date; after: Date }\nexport type DayOfWeek = { dayOfWeek: number | number[] }\n/** react-day-picker Matcher: which days a modifier (disabled, hidden, custom) applies to. */\nexport type Matcher =\n  boolean | ((date: Date) => boolean) | Date | Date[] | DateRange | DateBefore | DateAfter | DateInterval | DayOfWeek\n\nexport type DayPickerMode = 'single' | 'multiple' | 'range'\nexport type CaptionLayout = 'label' | 'dropdown' | 'dropdown-months' | 'dropdown-years'\nexport type WeekStartsOn = 0 | 1 | 2 | 3 | 4 | 5 | 6\nexport type DayPickerSelected = Date | Date[] | DateRange | undefined\n\n/** Day modifiers (flags + selection states + custom `modifiers` keys). */\nexport type Modifiers = Record<string, boolean>\n\nexport interface DayPickerLabels {\n  labelNav: () => string\n  labelPrevious: (month?: Date) => string\n  labelNext: (month?: Date) => string\n  labelMonthDropdown: () => string\n  labelYearDropdown: () => string\n  labelGrid: (month: Date, locale: string) => string\n  labelGridcell: (date: Date, modifiers: Modifiers, locale: string) => string\n  labelDayButton: (date: Date, modifiers: Modifiers, locale: string) => string\n  labelWeekday: (date: Date, locale: string) => string\n}\n\nexport interface DayPickerFormatters {\n  formatCaption: (month: Date, locale: string) => string\n  formatDay: (date: Date, locale: string) => string\n  formatWeekdayName: (weekday: Date, locale: string) => string\n  formatMonthDropdown: (month: Date, locale: string) => string\n  formatYearDropdown: (year: Date, locale: string) => string\n}\n\n/** `locale` accepts a BCP 47 tag or a `{ code, labels? }` object (React passes date-fns locales). */\nexport type DayPickerLocale = string | { code: string; labels?: Partial<DayPickerLabels> }\n\n/** Context for the `dayContent` template (React `components.DayButton` children). */\nexport interface DayContentContext {\n  $implicit: Date\n  date: Date\n  modifiers: Modifiers\n  label: string\n}\n\nexport interface DayEvent<E extends Event = Event> {\n  date: Date\n  modifiers: Modifiers\n  event: E\n}\n\nexport type DayPickerClassNames = Record<string, string>\n\nconst UI_KEYS = [\n  'root',\n  'chevron',\n  'day',\n  'day_button',\n  'caption_label',\n  'dropdowns',\n  'dropdown',\n  'dropdown_root',\n  'footer',\n  'month_grid',\n  'month_caption',\n  'months_dropdown',\n  'month',\n  'months',\n  'nav',\n  'button_next',\n  'button_previous',\n  'week',\n  'weeks',\n  'weekday',\n  'weekdays',\n  'week_number',\n  'week_number_header',\n  'years_dropdown',\n]\nconst DAY_FLAGS = ['focused', 'disabled', 'hidden', 'outside', 'today']\nconst SELECTION_STATES = ['range_end', 'range_middle', 'range_start', 'selected']\n\nexport function defaultClassNames(): DayPickerClassNames {\n  const out: DayPickerClassNames = {}\n  for (const k of [...UI_KEYS, ...DAY_FLAGS, ...SELECTION_STATES]) out[k] = `rdp-${k}`\n  return out\n}\n\n// ---- native Date helpers (date-fns semantics, local time) ----\n\nexport const startOfDay = (d: Date) => new Date(d.getFullYear(), d.getMonth(), d.getDate())\nexport const startOfMonth = (d: Date) => new Date(d.getFullYear(), d.getMonth(), 1)\nconst endOfMonth = (d: Date) => new Date(d.getFullYear(), d.getMonth() + 1, 0, 23, 59, 59, 999)\nconst startOfYear = (d: Date) => new Date(d.getFullYear(), 0, 1)\nconst endOfYear = (d: Date) => new Date(d.getFullYear(), 11, 31, 23, 59, 59, 999)\nexport const addDays = (d: Date, n: number) => new Date(d.getFullYear(), d.getMonth(), d.getDate() + n)\nexport function addMonths(d: Date, n: number): Date {\n  const target = new Date(d.getFullYear(), d.getMonth() + n, 1, d.getHours(), d.getMinutes(), d.getSeconds())\n  const last = new Date(target.getFullYear(), target.getMonth() + 1, 0).getDate()\n  target.setDate(Math.min(d.getDate(), last))\n  return target\n}\nconst addYears = (d: Date, n: number) => addMonths(d, n * 12)\nfunction startOfWeek(d: Date, ws: number): Date {\n  const day = d.getDay()\n  const diff = (day < ws ? 7 : 0) + day - ws\n  return new Date(d.getFullYear(), d.getMonth(), d.getDate() - diff)\n}\nfunction endOfWeek(d: Date, ws: number): Date {\n  const day = d.getDay()\n  const diff = (day < ws ? -7 : 0) + 6 - (day - ws)\n  return new Date(d.getFullYear(), d.getMonth(), d.getDate() + diff, 23, 59, 59, 999)\n}\nexport function differenceInCalendarDays(a: Date, b: Date): number {\n  return Math.round(\n    (Date.UTC(a.getFullYear(), a.getMonth(), a.getDate()) - Date.UTC(b.getFullYear(), b.getMonth(), b.getDate())) /\n      864e5,\n  )\n}\nconst differenceInCalendarMonths = (a: Date, b: Date) =>\n  (a.getFullYear() - b.getFullYear()) * 12 + a.getMonth() - b.getMonth()\nexport const isSameDay = (a: Date, b: Date) =>\n  a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate()\nconst isSameMonth = (a: Date, b: Date) => a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth()\nconst pad = (n: number) => String(n).padStart(2, '0')\nconst isoDate = (d: Date) => `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`\nconst monthId = (d: Date) => `${d.getFullYear()}-${pad(d.getMonth() + 1)}`\nconst isDate = (v: unknown): v is Date => v instanceof Date && !Number.isNaN(v.getTime())\n\nexport function rangeIncludesDate(range: DateRange, date: Date, excludeEnds = false): boolean {\n  let { from, to } = range\n  if (from && to) {\n    if (differenceInCalendarDays(to, from) < 0) [from, to] = [to, from]\n    const min = excludeEnds ? 1 : 0\n    return differenceInCalendarDays(date, from) >= min && differenceInCalendarDays(to, date) >= min\n  }\n  if (!excludeEnds && to) return isSameDay(to, date)\n  if (!excludeEnds && from) return isSameDay(from, date)\n  return false\n}\n\n/** react-day-picker `dateMatchModifiers`. */\nexport function dateMatchModifiers(date: Date, matchers: Matcher | Matcher[]): boolean {\n  const list: Matcher[] = Array.isArray(matchers) ? matchers : [matchers]\n  return list.some((m) => {\n    if (typeof m === 'boolean') return m\n    if (isDate(m)) return isSameDay(date, m)\n    if (Array.isArray(m)) return m.some((d) => isSameDay(date, d))\n    if (typeof m === 'function') return m(date)\n    if (typeof m !== 'object' || m === null) return false\n    if ('from' in m) return rangeIncludesDate(m as DateRange, date, false)\n    if ('dayOfWeek' in m) {\n      const dow = (m as DayOfWeek).dayOfWeek\n      return Array.isArray(dow) ? dow.includes(date.getDay()) : dow === date.getDay()\n    }\n    const hasBefore = 'before' in m\n    const hasAfter = 'after' in m\n    if (hasBefore && hasAfter) {\n      const i = m as DateInterval\n      const isDayBefore = differenceInCalendarDays(i.before, date) > 0\n      const isDayAfter = differenceInCalendarDays(i.after, date) < 0\n      return i.before > i.after ? isDayAfter && isDayBefore : isDayBefore || isDayAfter\n    }\n    if (hasAfter) return differenceInCalendarDays(date, (m as DateAfter).after) > 0\n    if (hasBefore) return differenceInCalendarDays((m as DateBefore).before, date) > 0\n    return false\n  })\n}\n\n/** react-day-picker `addToRange`. */\nexport function addToRange(date: Date, initial: DateRange | undefined, min = 0, max = 0, required = false) {\n  const { from, to } = initial || ({} as DateRange)\n  let range: DateRange | undefined\n  if (!from && !to) range = { from: date, to: min > 0 ? undefined : date }\n  else if (from && !to) {\n    if (isSameDay(from, date)) {\n      if (min === 0) range = { from, to: date }\n      else if (required) range = { from, to: undefined }\n      else range = undefined\n    } else if (date < from) range = { from: date, to: from }\n    else range = { from, to: date }\n  } else if (from && to) {\n    if (isSameDay(from, date) && isSameDay(to, date)) range = required ? { from, to } : undefined\n    else if (isSameDay(from, date)) range = { from, to: min > 0 ? undefined : date }\n    else if (isSameDay(to, date)) range = { from: date, to: min > 0 ? undefined : date }\n    else if (date < from) range = { from: date, to }\n    else range = { from, to: date }\n  }\n  if (range?.from && range.to) {\n    const diff = differenceInCalendarDays(range.to, range.from)\n    if (max > 0 && diff > max) range = { from: date, to: undefined }\n    else if (min > 1 && diff < min) range = { from: date, to: undefined }\n  }\n  return range\n}\n\n// ---- locale formatting (date-fns patterns rdp uses, via Intl) ----\n\nconst fmtCache = new Map<string, Intl.DateTimeFormat>()\nfunction fmt(locale: string, opts: Intl.DateTimeFormatOptions): Intl.DateTimeFormat {\n  const key = locale + JSON.stringify(opts)\n  let f = fmtCache.get(key)\n  if (!f) {\n    f = new Intl.DateTimeFormat(locale, opts)\n    fmtCache.set(key, f)\n  }\n  return f\n}\n/** Locales whose month/year caption reads year-first (DateLib.yearFirstLocales). */\nconst YEAR_FIRST = new Set([\n  'eu',\n  'hu',\n  'ja',\n  'ja-Hira',\n  'ja-JP',\n  'ko',\n  'ko-KR',\n  'lt',\n  'lt-LT',\n  'lv',\n  'lv-LV',\n  'mn',\n  'mn-MN',\n  'zh',\n  'zh-CN',\n  'zh-HK',\n  'zh-TW',\n])\nconst isEnglish = (locale: string) => /^en\\b/i.test(locale)\nconst ordinal = (n: number) => {\n  const s = n % 100\n  if (s >= 11 && s <= 13) return `${n}th`\n  const r = n % 10\n  return `${n}${r === 1 ? 'st' : r === 2 ? 'nd' : r === 3 ? 'rd' : 'th'}`\n}\n/** date-fns `PPPP` (en: \"Wednesday, September 23rd, 2026\"). */\nfunction formatFullDate(d: Date, locale: string): string {\n  if (!isEnglish(locale)) return fmt(locale, { dateStyle: 'full' }).format(d)\n  const weekday = fmt(locale, { weekday: 'long' }).format(d)\n  const month = fmt(locale, { month: 'long' }).format(d)\n  return `${weekday}, ${month} ${ordinal(d.getDate())}, ${d.getFullYear()}`\n}\nfunction formatMonthYear(d: Date, locale: string): string {\n  if (YEAR_FIRST.has(locale)) return fmt(locale, { month: 'long', year: 'numeric' }).format(d)\n  return `${fmt(locale, { month: 'long' }).format(d)} ${d.getFullYear()}`\n}\n\nexport const defaultFormatters: DayPickerFormatters = {\n  formatCaption: formatMonthYear,\n  formatDay: (d) => String(d.getDate()),\n  formatWeekdayName: (d, locale) => fmt(locale, { weekday: 'short' }).format(d).slice(0, 2),\n  formatMonthDropdown: (d, locale) => fmt(locale, { month: 'long' }).format(d),\n  formatYearDropdown: (d) => String(d.getFullYear()),\n}\n\nconst EN_LABELS: DayPickerLabels = {\n  labelNav: () => 'Navigation bar',\n  labelPrevious: () => 'Go to the Previous Month',\n  labelNext: () => 'Go to the Next Month',\n  labelMonthDropdown: () => 'Choose the Month',\n  labelYearDropdown: () => 'Choose the Year',\n  labelGrid: (d, locale) => formatMonthYear(d, locale),\n  labelGridcell: (d, m, locale) => `${m['today'] ? 'Today, ' : ''}${formatFullDate(d, locale)}`,\n  labelDayButton: (d, m, locale) =>\n    `${m['today'] ? 'Today, ' : ''}${formatFullDate(d, locale)}${m['selected'] ? ', selected' : ''}`,\n  labelWeekday: (d, locale) => fmt(locale, { weekday: 'long' }).format(d),\n}\nconst JA_LABELS: Partial<DayPickerLabels> = {\n  labelNav: () => 'ナビゲーションバー',\n  labelPrevious: () => '前の月へ',\n  labelNext: () => '次の月へ',\n  labelMonthDropdown: () => '月を選択',\n  labelYearDropdown: () => '年を選択',\n  labelGridcell: (d, m, locale) => `${m['today'] ? '今日、' : ''}${formatFullDate(d, locale)}`,\n  labelDayButton: (d, m, locale) =>\n    `${m['today'] ? '今日、' : ''}${formatFullDate(d, locale)}${m['selected'] ? '、選択済み' : ''}`,\n}\n\ninterface CalDay {\n  date: Date\n  displayMonth: Date\n  outside: boolean\n}\nconst sameCalDay = (a: CalDay | undefined, b: CalDay) =>\n  !!a && isSameDay(a.date, b.date) && isSameMonth(a.displayMonth, b.displayMonth)\n\nexport interface DayVM {\n  key: string\n  date: Date\n  iso: string\n  dataMonth: string | null\n  cellClass: string\n  modifiers: Modifiers\n  label: string\n  cellLabel: string | null\n  buttonLabel: string\n  tabIndex: number\n  buttonDisabled: boolean\n  buttonAriaDisabled: boolean\n  ctx: DayContentContext\n  day: CalDay\n}\nexport interface DropdownVM {\n  kind: 'month' | 'year'\n  select: boolean\n  label: string\n  ariaLabel: string\n  options: { value: number; label: string; disabled: boolean }[]\n  value: number\n}\nexport interface MonthVM {\n  date: Date\n  offset: number\n  caption: string\n  gridLabel: string\n  weeks: { key: number; days: DayVM[] }[]\n  controls: DropdownVM[]\n}\nexport interface DayPickerVM {\n  months: MonthVM[]\n  weekdays: { key: number; label: string; name: string }[]\n  previousMonth?: Date\n  nextMonth?: Date\n  labelNav: string\n  labelPrevious: string\n  labelNext: string\n}\n\n/** Renders the `dayContent` template with its context inside the day button. */\n@Directive({ selector: '[uiDayContent]', standalone: true })\nexport class UiDayContentDirective implements OnChanges, OnDestroy {\n  @Input('uiDayContent') template: TemplateRef<DayContentContext> | null = null\n  @Input('uiDayContentContext') context!: DayContentContext\n  private readonly vcr = inject(ViewContainerRef)\n  private ref?: EmbeddedViewRef<DayContentContext>\n  private tpl: TemplateRef<DayContentContext> | null = null\n\n  ngOnChanges(): void {\n    if (this.tpl !== this.template) {\n      this.vcr.clear()\n      this.ref = this.template ? this.vcr.createEmbeddedView(this.template, this.context) : undefined\n      this.tpl = this.template\n    } else if (this.ref) {\n      Object.assign(this.ref.context, this.context)\n      this.ref.markForCheck()\n    }\n  }\n\n  ngOnDestroy(): void {\n    this.vcr.clear()\n  }\n}\n\nconst CHEVRON_LEFT = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-chevron-left size-4 rdp-chevron\" aria-hidden=\"true\" [attr.disabled]=\"vm.previousMonth ? null : ''\"><path d=\"m15 18-6-6 6-6\" /></svg>`\nconst CHEVRON_RIGHT = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-chevron-right size-4 rdp-chevron\" aria-hidden=\"true\" [attr.disabled]=\"vm.nextMonth ? null : ''\"><path d=\"m9 18 6-6-6-6\" /></svg>`\n\n/** Shared template (Calendar + RangeCalendar), 1:1 with react-day-picker's DayPicker render. */\nexport const DAY_PICKER_TEMPLATE = `\n  @let vm = view;\n  <div [class]=\"cls['months']\">\n    @if (!hideNavigation) {\n      <nav [class]=\"cls['nav']\" [attr.aria-label]=\"vm.labelNav\">\n        <button\n          type=\"button\"\n          [class]=\"cls['button_previous']\"\n          [attr.tabindex]=\"vm.previousMonth ? null : -1\"\n          [attr.aria-disabled]=\"vm.previousMonth ? null : 'true'\"\n          [attr.aria-label]=\"vm.labelPrevious\"\n          (click)=\"onPrevious()\"\n        >${CHEVRON_LEFT}</button>\n        <button\n          type=\"button\"\n          [class]=\"cls['button_next']\"\n          [attr.tabindex]=\"vm.nextMonth ? null : -1\"\n          [attr.aria-disabled]=\"vm.nextMonth ? null : 'true'\"\n          [attr.aria-label]=\"vm.labelNext\"\n          (click)=\"onNext()\"\n        >${CHEVRON_RIGHT}</button>\n      </nav>\n    }\n    @for (m of vm.months; track m.offset) {\n      <div [class]=\"cls['month']\">\n        <div [class]=\"cls['month_caption']\">\n          @if (isDropdownLayout) {\n            <div [class]=\"cls['dropdowns']\">\n              @for (c of m.controls; track c.kind) {\n                @if (c.select) {\n                  <span data-disabled=\"false\" [class]=\"cls['dropdown_root']\">\n                    <select\n                      [class]=\"cls['dropdown'] + ' ' + cls[c.kind === 'month' ? 'months_dropdown' : 'years_dropdown']\"\n                      [attr.aria-label]=\"c.ariaLabel\"\n                      [disabled]=\"disableNavigation\"\n                      (change)=\"onDropdown(c.kind, m, $event)\"\n                    >\n                      @for (o of c.options; track o.value) {\n                        <option [value]=\"o.value\" [disabled]=\"o.disabled\" [selected]=\"o.value === c.value\">{{ o.label }}</option>\n                      }\n                    </select>\n                    <span [class]=\"cls['caption_label']\" aria-hidden=\"true\">{{ c.label }}<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-chevron-right size-4 rdp-chevron\" aria-hidden=\"true\"><path d=\"m9 18 6-6-6-6\" /></svg></span>\n                  </span>\n                } @else {\n                  <span>{{ c.label }}</span>\n                }\n              }\n              <span role=\"status\" aria-live=\"polite\" class=\"sr-only\">{{ m.caption }}</span>\n            </div>\n          } @else {\n            <span [class]=\"cls['caption_label']\" role=\"status\" aria-live=\"polite\">{{ m.caption }}</span>\n          }\n        </div>\n        <table role=\"grid\" [attr.aria-multiselectable]=\"mode === 'multiple' || mode === 'range'\" [attr.aria-label]=\"m.gridLabel || null\" [class]=\"cls['month_grid']\">\n          @if (!hideWeekdays) {\n            <thead aria-hidden=\"true\">\n              <tr [class]=\"cls['weekdays']\">\n                @for (w of vm.weekdays; track w.key) {\n                  <th [attr.aria-label]=\"w.label\" [class]=\"cls['weekday']\" scope=\"col\">{{ w.name }}</th>\n                }\n              </tr>\n            </thead>\n          }\n          <tbody [class]=\"cls['weeks']\">\n            @for (week of m.weeks; track week.key) {\n              <tr [class]=\"cls['week']\">\n                @for (d of week.days; track d.key) {\n                  <td\n                    [class]=\"d.cellClass\"\n                    role=\"gridcell\"\n                    [attr.aria-selected]=\"d.modifiers['selected'] || null\"\n                    [attr.aria-label]=\"d.cellLabel\"\n                    [attr.data-day]=\"d.iso\"\n                    [attr.data-month]=\"d.dataMonth\"\n                    [attr.data-selected]=\"d.modifiers['selected'] || null\"\n                    [attr.data-disabled]=\"d.modifiers['disabled'] || null\"\n                    [attr.data-hidden]=\"d.modifiers['hidden'] || null\"\n                    [attr.data-outside]=\"d.modifiers['outside'] || null\"\n                    [attr.data-focused]=\"d.modifiers['focused'] || null\"\n                    [attr.data-today]=\"d.modifiers['today'] || null\"\n                  >@if (!d.modifiers['hidden'] && interactive) {<button\n                        [class]=\"cls['day_button']\"\n                        type=\"button\"\n                        [attr.tabindex]=\"d.tabIndex\"\n                        [disabled]=\"d.buttonDisabled\"\n                        [attr.aria-disabled]=\"d.buttonAriaDisabled || null\"\n                        [attr.aria-label]=\"d.buttonLabel\"\n                        (click)=\"onDayClick(d, $event)\"\n                        (focus)=\"onDayFocus(d, $event)\"\n                        (blur)=\"onDayBlur(d, $event)\"\n                        (keydown)=\"onDayKeyDown(d, $event)\"\n                        (mouseenter)=\"onDayMouseEnter(d, $event)\"\n                        (mouseleave)=\"onDayMouseLeave(d, $event)\"\n                      >@if (dayContent) {<ng-container [uiDayContent]=\"dayContent\" [uiDayContentContext]=\"d.ctx\" />} @else {{{ d.label }}}</button>} @else if (!d.modifiers['hidden']) {{{ d.label }}}</td>\n                }\n              </tr>\n            }\n          </tbody>\n        </table>\n      </div>\n    }\n  </div>\n  @if (footer) {\n    <div [class]=\"cls['footer']\" role=\"status\" aria-live=\"polite\">{{ footer }}</div>\n  }\n`\n\n/**\n * Shared react-day-picker state machine. Calendar and RangeCalendar extend it with their\n * class maps; inputs mirror DayPicker props (React names), outputs mirror its callbacks.\n */\n@Directive()\nexport abstract class UiDayPickerBase implements OnChanges {\n  private readonly cdr = inject(ChangeDetectorRef)\n  protected readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement\n\n  @Input() mode?: DayPickerMode\n  /** Selected value: Date (single), Date[] (multiple) or { from, to } (range). Controlled when `select` is bound. */\n  @Input() selected?: DayPickerSelected\n  @Input({ transform: booleanAttribute }) required = false\n  /** Minimum days (range) or dates (multiple) to select. */\n  @Input() min?: number\n  /** Maximum days (range) or dates (multiple) to select. */\n  @Input() max?: number\n  @Input({ transform: booleanAttribute }) excludeDisabled = false\n  @Input({ transform: booleanAttribute }) resetOnSelect = false\n  @Input() disabled?: Matcher | Matcher[]\n  @Input() hidden?: Matcher | Matcher[]\n  @Input() modifiers?: Record<string, Matcher | Matcher[]>\n  @Input() modifiersClassNames?: Record<string, string>\n  /** Controlled first displayed month (pair with `monthChange`). */\n  @Input() month?: Date\n  @Input() defaultMonth?: Date\n  @Input() startMonth?: Date\n  @Input() endMonth?: Date\n  @Input() numberOfMonths = 1\n  @Input({ transform: booleanAttribute }) pagedNavigation = false\n  @Input({ transform: booleanAttribute }) reverseMonths = false\n  @Input({ transform: booleanAttribute }) disableNavigation = false\n  @Input({ transform: booleanAttribute }) hideNavigation = false\n  @Input({ transform: booleanAttribute }) hideWeekdays = false\n  @Input() captionLayout: CaptionLayout = 'label'\n  @Input({ transform: booleanAttribute }) reverseYears = false\n  @Input({ transform: booleanAttribute }) fixedWeeks = false\n  @Input({ transform: booleanAttribute }) showOutsideDays = false\n  @Input() weekStartsOn?: WeekStartsOn\n  @Input() locale?: DayPickerLocale\n  @Input() today?: Date\n  @Input({ transform: booleanAttribute }) autoFocus = false\n  @Input() dir?: 'ltr' | 'rtl'\n  @Input() lang?: string\n  @Input() footer?: string\n  @Input() classNames?: DayPickerClassNames\n  @Input() formatters?: Partial<DayPickerFormatters>\n  @Input() labels?: Partial<DayPickerLabels>\n  /** Day button content (React `components.DayButton` children). Context: `$implicit` = the date. */\n  @Input() dayContent?: TemplateRef<DayContentContext> | null\n  @Input('class') className?: string\n\n  /** React `onSelect`: the new selection (the value React passes first). */\n  @Output() readonly select = new EventEmitter<DayPickerSelected>()\n  @Output() readonly monthChange = new EventEmitter<Date>()\n  @Output() readonly prevClick = new EventEmitter<Date>()\n  @Output() readonly nextClick = new EventEmitter<Date>()\n  @Output() readonly dayClick = new EventEmitter<DayEvent<MouseEvent>>()\n  @Output() readonly dayFocus = new EventEmitter<DayEvent<FocusEvent>>()\n  @Output() readonly dayBlur = new EventEmitter<DayEvent<FocusEvent>>()\n  @Output() readonly dayKeyDown = new EventEmitter<DayEvent<KeyboardEvent>>()\n  @Output() readonly dayMouseEnter = new EventEmitter<DayEvent<MouseEvent>>()\n  @Output() readonly dayMouseLeave = new EventEmitter<DayEvent<MouseEvent>>()\n\n  /** Uncontrolled first month (null until first render). */\n  private internalMonth: Date | null = null\n  private internalSelected: DayPickerSelected = undefined\n  private initialized = false\n  private focusedDay?: CalDay\n  private lastFocused?: CalDay\n  private cache?: DayPickerVM\n  private days: CalDay[] = []\n\n  /** Component-level class map (Calendar / RangeCalendar), merged under `classNames`. */\n  protected abstract componentClassNames(): DayPickerClassNames\n  /** Component-level formatter overrides (both wrappers emit narrow weekday names). */\n  protected componentFormatters(): Partial<DayPickerFormatters> {\n    return {}\n  }\n  protected abstract readonly slot: string\n\n  private clsCache?: DayPickerClassNames\n\n  get cls(): DayPickerClassNames {\n    return (this.clsCache ??= { ...defaultClassNames(), ...this.componentClassNames() })\n  }\n\n  get localeCode(): string {\n    const l = this.locale\n    if (!l) return 'en-US'\n    return typeof l === 'string' ? l : l.code\n  }\n\n  get ws(): number {\n    return this.weekStartsOn ?? 0\n  }\n\n  get isDropdownLayout(): boolean {\n    return this.captionLayout.startsWith('dropdown')\n  }\n\n  get interactive(): boolean {\n    return this.mode !== undefined || this.dayClick.observed\n  }\n\n  get isControlled(): boolean {\n    return this.select.observed\n  }\n\n  get currentSelected(): DayPickerSelected {\n    return this.isControlled ? this.selected : this.internalSelected\n  }\n\n  get todayDate(): Date {\n    return this.today ?? new Date()\n  }\n\n  get fmts(): DayPickerFormatters {\n    return { ...defaultFormatters, ...this.componentFormatters(), ...this.formatters }\n  }\n\n  get lbls(): DayPickerLabels {\n    const l = this.locale\n    const localeLabels = typeof l === 'object' ? l.labels : /^ja\\b/.test(this.localeCode) ? JA_LABELS : undefined\n    return { ...EN_LABELS, ...localeLabels, ...this.labels }\n  }\n\n  ngOnChanges(): void {\n    if (!this.initialized) {\n      this.initialized = true\n      this.internalSelected = this.selected\n      if (this.autoFocus) queueMicrotask(() => this.focusTarget())\n    }\n    this.invalidate()\n  }\n\n  /** Nav bounds: startMonth / endMonth, or ±100 years when a year dropdown is shown. */\n  get navMonths(): [Date | undefined, Date | undefined] {\n    const hasYearDropdown = this.captionLayout === 'dropdown' || this.captionLayout === 'dropdown-years'\n    let start = this.startMonth ? startOfMonth(this.startMonth) : undefined\n    let end = this.endMonth ? endOfMonth(this.endMonth) : undefined\n    if (!start && hasYearDropdown) start = startOfYear(addYears(this.todayDate, -100))\n    if (!end && hasYearDropdown) end = endOfYear(this.todayDate)\n    return [start && startOfDay(start), end && startOfDay(end)]\n  }\n\n  private initialMonth(navStart?: Date, navEnd?: Date): Date {\n    let initial = this.month || this.defaultMonth || this.todayDate\n    const n = this.numberOfMonths || 1\n    if (navEnd && differenceInCalendarMonths(navEnd, initial) < n - 1) initial = addMonths(navEnd, -1 * (n - 1))\n    if (navStart && differenceInCalendarMonths(initial, navStart) < 0) initial = navStart\n    return startOfMonth(initial)\n  }\n\n  get firstMonth(): Date {\n    const [navStart, navEnd] = this.navMonths\n    if (this.month) return this.initialMonth(navStart, navEnd)\n    this.internalMonth ??= this.initialMonth(navStart, navEnd)\n    return this.internalMonth\n  }\n\n  protected invalidate(): void {\n    this.cache = undefined\n    this.clsCache = undefined\n    this.cdr.markForCheck()\n  }\n\n  get view(): DayPickerVM {\n    return (this.cache ??= this.build())\n  }\n\n  // ---- selection ----\n\n  isSelected(date: Date): boolean {\n    const sel = this.currentSelected\n    if (!sel) return false\n    if (this.mode === 'single') return sel instanceof Date && isSameDay(sel, date)\n    if (this.mode === 'multiple') return Array.isArray(sel) && sel.some((d) => isSameDay(d, date))\n    if (this.mode === 'range')\n      return !(sel instanceof Date) && !Array.isArray(sel) && rangeIncludesDate(sel, date, false)\n    return false\n  }\n\n  private selectDate(date: Date): DayPickerSelected {\n    const sel = this.currentSelected\n    let next: DayPickerSelected\n    if (this.mode === 'single') {\n      const cur = sel instanceof Date ? sel : undefined\n      next = !this.required && cur && isSameDay(date, cur) ? undefined : date\n    } else if (this.mode === 'multiple') {\n      const cur = Array.isArray(sel) ? sel : undefined\n      if (this.isSelected(date)) {\n        if (cur?.length === this.min) return sel\n        if (this.required && cur?.length === 1) return sel\n        next = cur?.filter((d) => !isSameDay(d, date))\n      } else {\n        next = cur?.length === this.max ? [date] : [...(cur ?? []), date]\n      }\n    } else if (this.mode === 'range') {\n      const cur = sel && !(sel instanceof Date) && !Array.isArray(sel) ? sel : undefined\n      const full = !!cur?.from && !!cur?.to\n      const singleDayClick = !!cur?.from && !!cur?.to && isSameDay(cur.from, cur.to) && isSameDay(date, cur.from)\n      let range: DateRange | undefined\n      if (this.resetOnSelect && (full || !cur?.from))\n        range = !this.required && singleDayClick ? undefined : { from: date, to: undefined }\n      else range = addToRange(date, cur, this.min, this.max, this.required)\n      if (this.excludeDisabled && this.disabled && range?.from && range.to) {\n        for (let d = startOfDay(range.from); d <= range.to; d = addDays(d, 1)) {\n          if (dateMatchModifiers(d, this.disabled)) {\n            range = { from: date, to: undefined }\n            break\n          }\n        }\n      }\n      next = range\n    } else return sel\n    if (!this.isControlled) this.internalSelected = next\n    this.select.emit(next)\n    return next\n  }\n\n  // ---- navigation ----\n\n  goToMonth(date: Date): void {\n    if (this.disableNavigation) return\n    const [navStart, navEnd] = this.navMonths\n    let m = startOfMonth(date)\n    if (navStart && m < startOfMonth(navStart)) m = startOfMonth(navStart)\n    if (navEnd && m > startOfMonth(navEnd)) m = startOfMonth(navEnd)\n    this.internalMonth = m\n    this.monthChange.emit(m)\n    this.invalidate()\n  }\n\n  onPrevious(): void {\n    const prev = this.view.previousMonth\n    if (!prev) return\n    this.goToMonth(prev)\n    this.prevClick.emit(prev)\n  }\n\n  onNext(): void {\n    const next = this.view.nextMonth\n    if (!next) return\n    this.goToMonth(next)\n    this.nextClick.emit(next)\n  }\n\n  onDropdown(kind: 'month' | 'year', m: MonthVM, e: Event): void {\n    const v = Number((e.target as HTMLSelectElement).value)\n    const base = startOfMonth(m.date)\n    const target = kind === 'month' ? new Date(base.getFullYear(), v, 1) : new Date(v, base.getMonth(), 1)\n    this.goToMonth(addMonths(target, -m.offset))\n  }\n\n  // ---- day events ----\n\n  onDayClick(d: DayVM, e: MouseEvent): void {\n    e.preventDefault()\n    e.stopPropagation()\n    this.focusedDay = d.day\n    this.invalidate()\n    if (d.modifiers['disabled']) return\n    this.selectDate(d.date)\n    this.dayClick.emit({ date: d.date, modifiers: d.modifiers, event: e })\n  }\n\n  onDayFocus(d: DayVM, e: FocusEvent): void {\n    this.focusedDay = d.day\n    this.invalidate()\n    this.dayFocus.emit({ date: d.date, modifiers: d.modifiers, event: e })\n  }\n\n  onDayBlur(d: DayVM, e: FocusEvent): void {\n    this.lastFocused = this.focusedDay\n    this.focusedDay = undefined\n    this.invalidate()\n    this.dayBlur.emit({ date: d.date, modifiers: d.modifiers, event: e })\n  }\n\n  onDayMouseEnter(d: DayVM, e: MouseEvent): void {\n    this.dayMouseEnter.emit({ date: d.date, modifiers: d.modifiers, event: e })\n  }\n\n  onDayMouseLeave(d: DayVM, e: MouseEvent): void {\n    this.dayMouseLeave.emit({ date: d.date, modifiers: d.modifiers, event: e })\n  }\n\n  onDayKeyDown(d: DayVM, e: KeyboardEvent): void {\n    const rtl = this.dir === 'rtl'\n    const map: Record<string, [string, 'before' | 'after']> = {\n      ArrowLeft: [e.shiftKey ? 'month' : 'day', rtl ? 'after' : 'before'],\n      ArrowRight: [e.shiftKey ? 'month' : 'day', rtl ? 'before' : 'after'],\n      ArrowDown: [e.shiftKey ? 'year' : 'week', 'after'],\n      ArrowUp: [e.shiftKey ? 'year' : 'week', 'before'],\n      PageUp: [e.shiftKey ? 'year' : 'month', 'before'],\n      PageDown: [e.shiftKey ? 'year' : 'month', 'after'],\n      Home: ['startOfWeek', 'before'],\n      End: ['endOfWeek', 'after'],\n    }\n    const move = map[e.key]\n    if (move) {\n      e.preventDefault()\n      e.stopPropagation()\n      this.moveFocus(move[0], move[1])\n    }\n    this.dayKeyDown.emit({ date: d.date, modifiers: d.modifiers, event: e })\n  }\n\n  private moveFocus(moveBy: string, dir: 'before' | 'after'): void {\n    if (!this.focusedDay) return\n    const [navStart, navEnd] = this.navMonths\n    let ref = this.focusedDay.date\n    let next: Date | undefined\n    for (let attempt = 0; attempt <= 365; attempt++) {\n      const n = dir === 'after' ? 1 : -1\n      let d: Date\n      if (moveBy === 'day') d = addDays(ref, n)\n      else if (moveBy === 'week') d = addDays(ref, 7 * n)\n      else if (moveBy === 'month') d = addMonths(ref, n)\n      else if (moveBy === 'year') d = addYears(ref, n)\n      else if (moveBy === 'startOfWeek') d = startOfWeek(ref, this.ws)\n      else d = startOfDay(endOfWeek(ref, this.ws))\n      if (dir === 'before' && navStart && d < navStart) d = navStart\n      else if (dir === 'after' && navEnd && d > navEnd) d = navEnd\n      const blocked =\n        (this.disabled !== undefined && dateMatchModifiers(d, this.disabled)) ||\n        (this.hidden !== undefined && dateMatchModifiers(d, this.hidden))\n      if (!blocked) {\n        next = d\n        break\n      }\n      ref = d\n    }\n    if (!next) return\n    const target: CalDay = { date: next, displayMonth: next, outside: false }\n    const inCalendar = this.days.some((day) => sameCalDay(day, target))\n    if (this.disableNavigation && !inCalendar) return\n    if (!inCalendar) this.goToMonth(next)\n    this.focusedDay = target\n    this.invalidate()\n    this.cdr.detectChanges()\n    this.focusDomDay(target)\n  }\n\n  private focusDomDay(day: CalDay): void {\n    const btn = this.host.querySelector<HTMLButtonElement>(\n      `td[data-day=\"${isoDate(day.date)}\"]:not([data-outside]) > button`,\n    )\n    btn?.focus()\n  }\n\n  /** autoFocus: focus the focus-target day button (DayButton effect in React). */\n  private focusTarget(): void {\n    const btn = this.host.querySelector<HTMLButtonElement>('td > button[tabindex=\"0\"]')\n    btn?.focus()\n  }\n\n  // ---- view model ----\n\n  private build(): DayPickerVM {\n    const locale = this.localeCode\n    const ws = this.ws\n    const f = this.fmts\n    const l = this.lbls\n    const cls = this.cls\n    const today = this.todayDate\n    const [navStart, navEnd] = this.navMonths\n    const first = this.firstMonth\n    const n = this.numberOfMonths || 1\n\n    const displayMonths: Date[] = []\n    for (let i = 0; i < n; i++) {\n      const m = addMonths(first, i)\n      if (navEnd && m > navEnd) break\n      displayMonths.push(m)\n    }\n    const lastMonth = displayMonths[displayMonths.length - 1]!\n    const startWeekFirst = startOfWeek(first, ws)\n    const displayWeekEnd = endOfWeek(endOfMonth(lastMonth), ws)\n    const maxDate = this.endMonth ? endOfMonth(this.endMonth) : undefined\n    const constraintEnd = maxDate && endOfWeek(maxDate, ws)\n    const gridEnd = constraintEnd && displayWeekEnd > constraintEnd ? constraintEnd : displayWeekEnd\n    const nDays = differenceInCalendarDays(gridEnd, startWeekFirst)\n    const dates: Date[] = []\n    for (let i = 0; i <= nDays; i++) dates.push(addDays(startWeekFirst, i))\n    const extra = 42 * (differenceInCalendarMonths(lastMonth, first) + 1)\n    if (this.fixedWeeks && dates.length < extra) {\n      const add = extra - dates.length\n      for (let i = 0; i < add; i++) dates.push(addDays(dates[dates.length - 1]!, 1))\n    }\n\n    const monthDays = displayMonths.map((month) => {\n      const firstOfWeek = startOfWeek(month, ws)\n      const lastOfWeek = endOfWeek(endOfMonth(month), ws)\n      const md = dates.filter((d) => d >= firstOfWeek && d <= lastOfWeek)\n      if (this.fixedWeeks && md.length < 42) {\n        const add = 42 - md.length\n        const limit = addDays(lastOfWeek, add)\n        md.push(...dates.filter((d) => d > lastOfWeek && d <= limit))\n      }\n      return md.map<CalDay>((date) => ({ date, displayMonth: month, outside: !isSameMonth(date, month) }))\n    })\n    if (this.reverseMonths) {\n      displayMonths.reverse()\n      monthDays.reverse()\n    }\n    this.days = monthDays.flat()\n\n    const navStartDay = navStart && startOfMonth(navStart)\n    const navEndDay = navEnd && endOfMonth(navEnd)\n    const sel = this.currentSelected\n    const range = this.mode === 'range' && sel && !(sel instanceof Date) && !Array.isArray(sel) ? sel : undefined\n    const flags = (day: CalDay): Modifiers => {\n      const { date } = day\n      const hidden =\n        (this.hidden !== undefined && dateMatchModifiers(date, this.hidden)) ||\n        (!!navStartDay && date < navStartDay) ||\n        (!!navEndDay && date > navEndDay) ||\n        (!this.showOutsideDays && day.outside)\n      const mods: Modifiers = {\n        focused: false,\n        disabled: this.disabled !== undefined && dateMatchModifiers(date, this.disabled),\n        hidden,\n        outside: day.outside,\n        today: isSameDay(date, today),\n      }\n      for (const [name, matcher] of Object.entries(this.modifiers ?? {})) {\n        if (matcher !== undefined && dateMatchModifiers(date, matcher)) mods[name] = true\n      }\n      return mods\n    }\n\n    // Focus target: last focused > selected > today > first focusable (calculateFocusTarget).\n    let focusTarget: CalDay | undefined\n    let priority = -1\n    const focusable = (m: Modifiers) => !m['disabled'] && !m['hidden'] && !m['outside']\n    const flagsFor = new Map<CalDay, Modifiers>()\n    for (const day of this.days) {\n      const m = flags(day)\n      flagsFor.set(day, m)\n      if (!focusable(m)) continue\n      if (sameCalDay(this.lastFocused, day) && priority < 2) {\n        focusTarget = day\n        priority = 2\n      } else if (this.isSelected(day.date) && priority < 1) {\n        focusTarget = day\n        priority = 1\n      } else if (m['today'] && priority < 0) {\n        focusTarget = day\n        priority = 0\n      }\n    }\n    focusTarget ??= this.days.find((d) => focusable(flagsFor.get(d)!))\n\n    const dayVM = (day: CalDay): DayVM => {\n      const mods: Modifiers = { ...flagsFor.get(day)! }\n      mods['focused'] = !mods['hidden'] && sameCalDay(this.focusedDay, day)\n      mods['selected'] = this.isSelected(day.date) || !!mods['selected']\n      if (range) {\n        const { from, to } = range\n        mods['range_start'] = !!(from && to && isSameDay(day.date, from))\n        mods['range_end'] = !!(from && to && isSameDay(day.date, to))\n        mods['range_middle'] = rangeIncludesDate(range, day.date, true)\n      }\n      const classes = [cls['day']]\n      for (const [key, active] of Object.entries(mods)) {\n        if (active !== true) continue\n        const c =\n          this.modifiersClassNames?.[key] ??\n          (DAY_FLAGS.includes(key) || SELECTION_STATES.includes(key) ? cls[key] : undefined)\n        if (c) classes.push(c)\n      }\n      const label = f.formatDay(day.date, locale)\n      const iso = isoDate(day.date)\n      return {\n        key: `${iso}_${monthId(day.displayMonth)}`,\n        date: day.date,\n        iso,\n        dataMonth: day.outside ? monthId(day.date) : null,\n        cellClass: classes.join(' '),\n        modifiers: mods,\n        label,\n        cellLabel: !this.interactive && !mods['hidden'] ? l.labelGridcell(day.date, mods, locale) : null,\n        buttonLabel: l.labelDayButton(day.date, mods, locale),\n        tabIndex: sameCalDay(focusTarget, day) ? 0 : -1,\n        buttonDisabled: !mods['focused'] && !!mods['disabled'],\n        buttonAriaDisabled: !!mods['focused'] && !!mods['disabled'],\n        ctx: { $implicit: day.date, date: day.date, modifiers: mods, label },\n        day,\n      }\n    }\n\n    const yearFirst = YEAR_FIRST.has(locale)\n    const months: MonthVM[] = displayMonths.map((month, displayIndex) => {\n      const offset = this.reverseMonths ? displayMonths.length - 1 - displayIndex : displayIndex\n      const cal = monthDays[displayIndex]!\n      const weeks: MonthVM['weeks'] = []\n      for (let i = 0; i < cal.length; i += 7) weeks.push({ key: i, days: cal.slice(i, i + 7).map(dayVM) })\n      const controls: DropdownVM[] = []\n      if (this.isDropdownLayout) {\n        const monthSelect = this.captionLayout === 'dropdown' || this.captionLayout === 'dropdown-months'\n        const yearSelect = this.captionLayout === 'dropdown' || this.captionLayout === 'dropdown-years'\n        const monthOptions = Array.from({ length: 12 }, (_, i) => {\n          const m = new Date(month.getFullYear(), i, 1)\n          return {\n            value: i,\n            label: f.formatMonthDropdown(m, locale),\n            disabled: (!!navStart && m < startOfMonth(navStart)) || (!!navEnd && m > startOfMonth(navEnd)),\n          }\n        })\n        const yearOptions: DropdownVM['options'] = []\n        if (navStart && navEnd) {\n          for (let y = navStart.getFullYear(); y <= navEnd.getFullYear(); y++) {\n            yearOptions.push({ value: y, label: f.formatYearDropdown(new Date(y, 0, 1), locale), disabled: false })\n          }\n          if (this.reverseYears) yearOptions.reverse()\n        }\n        const monthCtl: DropdownVM = {\n          kind: 'month',\n          select: monthSelect,\n          label: f.formatMonthDropdown(month, locale),\n          ariaLabel: l.labelMonthDropdown(),\n          options: monthOptions,\n          value: month.getMonth(),\n        }\n        const yearCtl: DropdownVM = {\n          kind: 'year',\n          select: yearSelect,\n          label: yearSelect\n            ? (yearOptions.find((o) => o.value === month.getFullYear())?.label ?? '')\n            : f.formatYearDropdown(month, locale),\n          ariaLabel: l.labelYearDropdown(),\n          options: yearOptions,\n          value: month.getFullYear(),\n        }\n        controls.push(...(yearFirst ? [yearCtl, monthCtl] : [monthCtl, yearCtl]))\n      }\n      return {\n        date: month,\n        offset,\n        caption: f.formatCaption(month, locale),\n        gridLabel: l.labelGrid(month, locale),\n        weeks,\n        controls,\n      }\n    })\n\n    const weekStart = startOfWeek(today, ws)\n    const weekdays = Array.from({ length: 7 }, (_, i) => {\n      const d = addDays(weekStart, i)\n      return { key: d.getDay(), label: l.labelWeekday(d, locale), name: f.formatWeekdayName(d, locale) }\n    })\n\n    const offset = this.pagedNavigation ? n : 1\n    const firstStart = startOfMonth(first)\n    let previousMonth: Date | undefined\n    let nextMonth: Date | undefined\n    if (!this.disableNavigation) {\n      previousMonth =\n        !navStart || differenceInCalendarMonths(firstStart, navStart) > 0 ? addMonths(firstStart, -offset) : undefined\n      nextMonth = !navEnd || differenceInCalendarMonths(navEnd, first) >= n ? addMonths(firstStart, offset) : undefined\n    }\n\n    return {\n      months,\n      weekdays,\n      previousMonth,\n      nextMonth,\n      labelNav: l.labelNav(),\n      labelPrevious: l.labelPrevious(previousMonth),\n      labelNext: l.labelNext(nextMonth),\n    }\n  }\n\n  /** Host attributes (Root). */\n  get dataMultipleMonths(): string | null {\n    return this.numberOfMonths > 1 ? 'true' : null\n  }\n}\n\n/** Narrow weekday names, locale-aware (both registry wrappers override rdp's two-letter default). */\nexport const narrowWeekday = (weekday: Date, locale: string) => fmt(locale, { weekday: 'narrow' }).format(weekday)\n",
      "type": "registry:ui",
      "target": "~/src/app/components/ui/calendar/day-picker.ts"
    },
    {
      "path": "packages/registry-angular/components/calendar/index.ts",
      "content": "export { UiCalendarComponent, calendarClassNames } from './calendar.component'\nexport {\n  UiDayContentDirective,\n  type CaptionLayout,\n  type DateAfter,\n  type DateBefore,\n  type DateInterval,\n  type DateRange,\n  type DayContentContext,\n  type DayEvent,\n  type DayOfWeek,\n  type DayPickerClassNames,\n  type DayPickerFormatters,\n  type DayPickerLabels,\n  type DayPickerLocale,\n  type DayPickerMode,\n  type DayPickerSelected,\n  type Matcher,\n  type Modifiers,\n  type WeekStartsOn,\n} from './day-picker'\n",
      "type": "registry:ui",
      "target": "~/src/app/components/ui/calendar/index.ts"
    }
  ],
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/button.json"
  ],
  "description": "Single-month calendar grid for date selection (react-day-picker semantics, native Date). Use inline or in a custom popover. For form fields, use Date Picker. Supports min/max, disabled dates, multi-month, and locale.",
  "categories": [
    "date-time"
  ]
}