{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "date-picker",
  "title": "Date Picker",
  "type": "registry:ui",
  "description": "Form date control: trigger + popover + Calendar/RangeCalendar + optional time. Single/multiple/range, week/month/quarter/year pickers, presets, confirm mode, min/max and disabled dates. For an always-visible grid, use Calendar instead.",
  "categories": [
    "date-time"
  ],
  "files": [
    {
      "path": "packages/elements-lit/src/components/date-picker/date-picker-utils.ts",
      "content": "export type DatePickerType = 'single' | 'multiple' | 'range'\nexport type DatePickerLayout = 'default' | 'month-and-year' | 'month-only' | 'year-only'\nexport type DatePickerPicker = 'day' | 'week' | 'month' | 'quarter' | 'year'\nexport type DatePickerStatus = 'error' | 'warning'\nexport type DatePickerSize = 'small' | 'middle' | 'large'\nexport type DatePickerPlacement =\n  | 'top'\n  | 'bottom'\n  | 'left'\n  | 'right'\n  | 'topLeft'\n  | 'topRight'\n  | 'bottomLeft'\n  | 'bottomRight'\n\nexport type FormatValue = 'short' | 'medium' | 'long' | 'full' | Intl.DateTimeFormatOptions\n\nexport type SingleValue = string | Date | null\nexport type MultipleValue = (string | Date)[] | null\nexport type RangeValue = { start: string | Date; end?: string | Date } | null\n\nexport interface DatePickerPreset {\n  label: string\n  value: SingleValue | MultipleValue | RangeValue\n  category?: string\n}\n\nexport interface DisabledTimeResult {\n  disabledHours?: () => number[]\n  disabledMinutes?: (selectedHour: number) => number[]\n  disabledSeconds?: (selectedHour: number, selectedMinute: number) => number[]\n}\n\nexport type TimeShape = { h: number; m: number; s: number }\n\nexport type InternalSingle = Date | undefined\nexport type InternalMultiple = Date[]\nexport type InternalRange = { start?: Date; end?: Date }\n\nexport function coerceDate(v: string | Date | null | undefined): Date | null {\n  if (!v) return null\n  if (v instanceof Date) return Number.isNaN(v.getTime()) ? null : v\n  const m = /^(\\d{4})-(\\d{2})-(\\d{2})/.exec(v)\n  if (m) {\n    const d = new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]))\n    return Number.isNaN(d.getTime()) ? null : d\n  }\n  const dt = /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2})(?::(\\d{2}))?/.exec(v)\n  if (dt) {\n    const d = new Date(\n      Number(dt[1]),\n      Number(dt[2]) - 1,\n      Number(dt[3]),\n      Number(dt[4]),\n      Number(dt[5]),\n      dt[6] ? Number(dt[6]) : 0,\n    )\n    return Number.isNaN(d.getTime()) ? null : d\n  }\n  const d = new Date(v)\n  return Number.isNaN(d.getTime()) ? null : d\n}\n\nexport function toISODate(d: Date): string {\n  const y = d.getFullYear()\n  const mo = String(d.getMonth() + 1).padStart(2, '0')\n  const da = String(d.getDate()).padStart(2, '0')\n  return `${y}-${mo}-${da}`\n}\n\nexport function toISODateTime(d: Date, showSeconds: boolean): string {\n  const date = toISODate(d)\n  const h = String(d.getHours()).padStart(2, '0')\n  const m = String(d.getMinutes()).padStart(2, '0')\n  if (showSeconds) {\n    const s = String(d.getSeconds()).padStart(2, '0')\n    return `${date}T${h}:${m}:${s}`\n  }\n  return `${date}T${h}:${m}`\n}\n\nexport function stripTime(d: Date): Date {\n  return new Date(d.getFullYear(), d.getMonth(), d.getDate())\n}\n\nexport function parseTimeShape(value: string, fallback: TimeShape): TimeShape {\n  const m = /^(\\d{1,2}):(\\d{2})(?::(\\d{2}))?$/.exec(value)\n  if (!m) return fallback\n  return { h: Number(m[1]), m: Number(m[2]), s: m[3] ? Number(m[3]) : 0 }\n}\n\nexport function withTime(d: Date, t: TimeShape): Date {\n  return new Date(d.getFullYear(), d.getMonth(), d.getDate(), t.h, t.m, t.s)\n}\n\nexport function weekStart(d: Date, weekStartsOn: number): Date {\n  const dayOfWeek = d.getDay()\n  const offset = (dayOfWeek - weekStartsOn + 7) % 7\n  const out = new Date(d)\n  out.setDate(out.getDate() - offset)\n  return stripTime(out)\n}\n\nexport function weekNumber(d: Date): number {\n  const jsDate = new Date(d)\n  const target = new Date(jsDate.valueOf())\n  const dayNr = (jsDate.getDay() + 6) % 7\n  target.setDate(target.getDate() - dayNr + 3)\n  const firstThursday = target.valueOf()\n  target.setMonth(0, 1)\n  if (target.getDay() !== 4) {\n    target.setMonth(0, 1 + ((4 - target.getDay() + 7) % 7))\n  }\n  return 1 + Math.ceil((firstThursday - target.valueOf()) / 604800000)\n}\n\nexport function defaultRangePresets(): DatePickerPreset[] {\n  const t = stripTime(new Date())\n  const yesterday = new Date(t)\n  yesterday.setDate(yesterday.getDate() - 1)\n  const last7 = new Date(t)\n  last7.setDate(last7.getDate() - 6)\n  const last30 = new Date(t)\n  last30.setDate(last30.getDate() - 29)\n  const monthStart = new Date(t.getFullYear(), t.getMonth(), 1)\n  const lastMonthEnd = new Date(t.getFullYear(), t.getMonth(), 0)\n  const lastMonthStart = new Date(lastMonthEnd.getFullYear(), lastMonthEnd.getMonth(), 1)\n  const ytd = new Date(t.getFullYear(), 0, 1)\n  return [\n    { label: 'Today', value: { start: t, end: t } },\n    { label: 'Yesterday', value: { start: yesterday, end: yesterday } },\n    { label: 'Last 7 days', value: { start: last7, end: t } },\n    { label: 'Last 30 days', value: { start: last30, end: t } },\n    { label: 'This month', value: { start: monthStart, end: t } },\n    { label: 'Last month', value: { start: lastMonthStart, end: lastMonthEnd } },\n    { label: 'Year to date', value: { start: ytd, end: t } },\n  ]\n}\n\nexport function coerceShape(\n  type: DatePickerType,\n  v: SingleValue | MultipleValue | RangeValue | undefined,\n): InternalSingle | InternalMultiple | InternalRange | undefined {\n  if (type === 'multiple') {\n    if (!Array.isArray(v)) return undefined\n    return v.map(coerceDate).filter((x): x is Date => x != null)\n  }\n  if (type === 'range') {\n    if (!v || Array.isArray(v) || typeof v === 'string' || !('start' in (v as object))) return undefined\n    const r = v as { start: string | Date; end?: string | Date }\n    const start = coerceDate(r.start)\n    const end = r.end ? coerceDate(r.end) : undefined\n    if (!start) return undefined\n    return end ? { start, end } : { start }\n  }\n  return coerceDate(v as SingleValue) ?? undefined\n}\n\nexport function fmtDate(d: Date, locale: string, format: FormatValue) {\n  const opts: Intl.DateTimeFormatOptions = typeof format === 'object' ? format : { dateStyle: format }\n  return new Intl.DateTimeFormat(locale, opts).format(d)\n}\n\nexport function fmtMonth(d: Date, locale: string) {\n  return new Intl.DateTimeFormat(locale, { month: 'long', year: 'numeric' }).format(d)\n}\n\nexport function fmtTime(d: Date, showSeconds: boolean, use24Hour: boolean) {\n  const opts: Intl.DateTimeFormatOptions = showSeconds\n    ? { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: !use24Hour }\n    : { hour: '2-digit', minute: '2-digit', hour12: !use24Hour }\n  return new Intl.DateTimeFormat(undefined, opts).format(d)\n}\n",
      "type": "registry:file",
      "target": "src/uip/components/date-picker/date-picker-utils.ts"
    },
    {
      "path": "packages/elements-lit/src/components/date-picker/date-picker.ts",
      "content": "import { LitElement, css, html, nothing } from 'lit'\nimport { Calendar as CalendarIcon, ChevronLeft, ChevronRight, X } from 'lucide'\nimport { cn } from '../../lib/utils'\nimport { icon } from '../../lib/icon'\nimport { tailwind } from '../../lib/styles'\nimport { ThemeController } from '../../lib/theme'\nimport { autoUpdate, computePosition } from '../../lib/position'\nimport {\n  coerceDate,\n  coerceShape,\n  defaultRangePresets,\n  fmtDate,\n  fmtMonth,\n  fmtTime,\n  stripTime,\n  toISODate,\n  toISODateTime,\n  weekStart,\n  type DatePickerLayout,\n  type DatePickerPicker,\n  type DatePickerPlacement,\n  type DatePickerPreset,\n  type DatePickerSize,\n  type DatePickerStatus,\n  type DatePickerType,\n  type DisabledTimeResult,\n  type FormatValue,\n  type InternalMultiple,\n  type InternalRange,\n  type InternalSingle,\n  type MultipleValue,\n  type RangeValue,\n  type SingleValue,\n} from './date-picker-utils'\n\nexport type {\n  DatePickerType,\n  DatePickerLayout,\n  DatePickerPicker,\n  DatePickerStatus,\n  DatePickerSize,\n  DatePickerPlacement,\n  FormatValue,\n  SingleValue,\n  MultipleValue,\n  RangeValue,\n  DatePickerPreset,\n  DisabledTimeResult,\n} from './date-picker-utils'\n\n/**\n * <uip-date-picker> — Rich date/range/time picker with presets, validation states,\n * custom formatting, granularity pickers (day, week, month, quarter, year), and popover.\n */\nexport class UipDatePicker extends LitElement {\n  static styles = [\n    tailwind,\n    css`\n      :host {\n        display: inline-block;\n        position: relative;\n        width: 100%;\n      }\n    `,\n  ]\n\n  static properties = {\n    value: { attribute: false },\n    defaultValue: { attribute: false },\n    type: { type: String },\n    placeholder: { type: String },\n    disabled: { type: Boolean },\n    readOnly: { type: Boolean, attribute: 'read-only' },\n    clearable: { type: Boolean },\n    format: { attribute: false },\n    dateFormat: { attribute: false },\n    locale: { type: String },\n    numberOfMonths: { type: Number, attribute: 'number-of-months' },\n    weekStartsOn: { type: Number, attribute: 'week-starts-on' },\n    minValue: { attribute: 'min-value' },\n    maxValue: { attribute: 'max-value' },\n    layout: { type: String },\n    picker: { type: String },\n    showCurrentDate: { type: Boolean, attribute: 'show-current-date' },\n    needConfirm: { type: Boolean, attribute: 'need-confirm' },\n    status: { type: String },\n    size: { type: String },\n    placement: { type: String },\n    separator: { type: String },\n    showTime: { type: Boolean, attribute: 'show-time' },\n    use24Hour: { type: Boolean, attribute: 'use-24-hour' },\n    showSeconds: { type: Boolean, attribute: 'show-seconds' },\n    presets: { type: Array, attribute: false },\n    disabledDate: { attribute: false },\n    disabledTime: { attribute: false },\n    renderCell: { attribute: false },\n    open: { state: true },\n    viewMonth: { state: true },\n    internalSelected: { state: true },\n    tempHour: { state: true },\n    tempMinute: { state: true },\n    tempSecond: { state: true },\n  }\n\n  value?: SingleValue | MultipleValue | RangeValue\n  defaultValue?: SingleValue | MultipleValue | RangeValue\n  type: DatePickerType = 'single'\n  placeholder?: string\n  disabled = false\n  readOnly = false\n  clearable = true\n  format: FormatValue = 'medium'\n  dateFormat?: FormatValue\n  locale = 'en-US'\n  numberOfMonths = 1\n  weekStartsOn: 0 | 1 = 0\n  minValue?: string | Date\n  maxValue?: string | Date\n  layout: DatePickerLayout = 'default'\n  picker: DatePickerPicker = 'day'\n  showCurrentDate = false\n  needConfirm = false\n  status?: DatePickerStatus\n  size: DatePickerSize = 'middle'\n  placement: DatePickerPlacement = 'bottomLeft'\n  separator = '—'\n  showTime = false\n  use24Hour = false\n  showSeconds = false\n  presets?: DatePickerPreset[]\n  disabledDate?: (date: Date) => boolean\n  disabledTime?: () => DisabledTimeResult\n  renderCell?: (date: Date) => unknown\n\n  open = false\n  viewMonth: Date = stripTime(new Date())\n  internalSelected: Date | Date[] | { start?: Date; end?: Date } | null = null\n  tempHour = 12\n  tempMinute = 0\n  tempSecond = 0\n\n  private cleanupAutoUpdate: (() => void) | null = null\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n    this.setAttribute('data-slot', 'date-picker')\n    const initial = this.value ?? this.defaultValue\n    if (initial !== undefined) {\n      this.initValue(initial)\n    }\n  }\n\n  disconnectedCallback() {\n    super.disconnectedCallback()\n    this.cleanupPositioning()\n  }\n\n  updated(changedProperties: Map<string, unknown>) {\n    if (changedProperties.has('value')) {\n      if (this.value !== undefined) {\n        this.initValue(this.value)\n      }\n    }\n    if (changedProperties.has('open')) {\n      if (this.open) {\n        this.setupPositioning()\n      } else {\n        this.cleanupPositioning()\n      }\n      this.dispatchEvent(\n        new CustomEvent('open-change', {\n          detail: { open: this.open },\n          bubbles: true,\n          composed: true,\n        }),\n      )\n    }\n  }\n\n  private initValue(val: SingleValue | MultipleValue | RangeValue) {\n    if (this.type === 'multiple') {\n      if (Array.isArray(val)) {\n        this.internalSelected = val.map((x) => coerceDate(x)).filter((d): d is Date => d !== null)\n      }\n    } else if (this.type === 'range') {\n      if (val && typeof val === 'object' && 'start' in val) {\n        const start = coerceDate((val as any).start) ?? undefined\n        const end = coerceDate((val as any).end) ?? undefined\n        this.internalSelected = { start, end }\n        if (start) this.viewMonth = new Date(start.getFullYear(), start.getMonth(), 1)\n      }\n    } else {\n      const d = coerceDate(val as SingleValue)\n      this.internalSelected = d\n      if (d) {\n        this.viewMonth = new Date(d.getFullYear(), d.getMonth(), 1)\n        this.tempHour = d.getHours()\n        this.tempMinute = d.getMinutes()\n        this.tempSecond = d.getSeconds()\n      }\n    }\n  }\n\n  private setupPositioning() {\n    const trigger = this.renderRoot.querySelector('[part=trigger]') as HTMLElement | null\n    const popover = this.renderRoot.querySelector('[part=popover]') as HTMLElement | null\n    if (!trigger || !popover) return\n\n    const side = this.placement.startsWith('top') ? 'top' : this.placement.startsWith('left') ? 'left' : this.placement.startsWith('right') ? 'right' : 'bottom'\n    const align = this.placement.endsWith('Left') || this.placement.endsWith('Start') ? 'start' : this.placement.endsWith('Right') || this.placement.endsWith('End') ? 'end' : 'center'\n\n    this.cleanupPositioning()\n    this.cleanupAutoUpdate = autoUpdate(trigger, popover, () => {\n      const pos = computePosition(trigger, popover, { side, align, sideOffset: 4 })\n      Object.assign(popover.style, pos.style)\n    })\n  }\n\n  private cleanupPositioning() {\n    if (this.cleanupAutoUpdate) {\n      this.cleanupAutoUpdate()\n      this.cleanupAutoUpdate = null\n    }\n  }\n\n  private toggleOpen() {\n    if (this.disabled || this.readOnly) return\n    this.open = !this.open\n  }\n\n  private closePopover() {\n    this.open = false\n  }\n\n  private selectDate(d: Date) {\n    if (this.isDateDisabled(d)) return\n\n    if (this.picker === 'week') {\n      const start = weekStart(d, this.weekStartsOn)\n      d = start\n    } else if (this.picker === 'month') {\n      d = new Date(d.getFullYear(), d.getMonth(), 1)\n    } else if (this.picker === 'quarter') {\n      const q = Math.floor(d.getMonth() / 3)\n      d = new Date(d.getFullYear(), q * 3, 1)\n    } else if (this.picker === 'year') {\n      d = new Date(d.getFullYear(), 0, 1)\n    }\n\n    if (this.showTime) {\n      d = new Date(d.getFullYear(), d.getMonth(), d.getDate(), this.tempHour, this.tempMinute, this.tempSecond)\n    }\n\n    if (this.type === 'multiple') {\n      const list = Array.isArray(this.internalSelected) ? [...this.internalSelected] : []\n      const idx = list.findIndex((item) => stripTime(item).getTime() === stripTime(d).getTime())\n      if (idx >= 0) list.splice(idx, 1)\n      else list.push(d)\n      this.internalSelected = list\n      if (!this.needConfirm) this.emitValue(list.map((item) => toISODate(item)))\n    } else if (this.type === 'range') {\n      const r = (this.internalSelected as { start?: Date; end?: Date } | null) ?? {}\n      if (!r.start || (r.start && r.end)) {\n        this.internalSelected = { start: d, end: undefined }\n      } else {\n        const start = r.start\n        if (d < start) {\n          this.internalSelected = { start: d, end: start }\n        } else {\n          this.internalSelected = { start, end: d }\n        }\n        if (!this.needConfirm) {\n          const finalRange = this.internalSelected as { start: Date; end: Date }\n          this.emitValue({\n            start: this.showTime ? toISODateTime(finalRange.start, this.showSeconds) : toISODate(finalRange.start),\n            end: this.showTime ? toISODateTime(finalRange.end, this.showSeconds) : toISODate(finalRange.end),\n          })\n          this.closePopover()\n        }\n      }\n    } else {\n      this.internalSelected = d\n      if (!this.needConfirm) {\n        this.emitValue(this.showTime ? toISODateTime(d, this.showSeconds) : toISODate(d))\n        this.closePopover()\n      }\n    }\n    this.requestUpdate()\n  }\n\n  private confirmSelection() {\n    if (this.type === 'range') {\n      const r = this.internalSelected as { start?: Date; end?: Date } | null\n      if (r?.start && r?.end) {\n        this.emitValue({\n          start: this.showTime ? toISODateTime(r.start, this.showSeconds) : toISODate(r.start),\n          end: this.showTime ? toISODateTime(r.end, this.showSeconds) : toISODate(r.end),\n        })\n      }\n    } else if (this.type === 'multiple') {\n      const list = Array.isArray(this.internalSelected) ? this.internalSelected : []\n      this.emitValue(list.map((item) => toISODate(item)))\n    } else if (this.internalSelected instanceof Date) {\n      this.emitValue(\n        this.showTime\n          ? toISODateTime(this.internalSelected, this.showSeconds)\n          : toISODate(this.internalSelected),\n      )\n    }\n    this.closePopover()\n  }\n\n  private emitValue(val: any) {\n    this.value = val\n    this.dispatchEvent(\n      new CustomEvent('value-change', {\n        detail: { value: val },\n        bubbles: true,\n        composed: true,\n      }),\n    )\n    this.dispatchEvent(\n      new CustomEvent('change', {\n        detail: { value: val },\n        bubbles: true,\n        composed: true,\n      }),\n    )\n  }\n\n  private isDateDisabled(d: Date): boolean {\n    if (this.minValue) {\n      const min = coerceDate(this.minValue)\n      if (min && stripTime(d) < stripTime(min)) return true\n    }\n    if (this.maxValue) {\n      const max = coerceDate(this.maxValue)\n      if (max && stripTime(d) > stripTime(max)) return true\n    }\n    if (this.disabledDate && this.disabledDate(d)) return true\n    return false\n  }\n\n  private handleClear(e: Event) {\n    e.stopPropagation()\n    this.internalSelected = null\n    this.emitValue(null)\n  }\n\n  private applyPreset(preset: DatePickerPreset) {\n    this.initValue(preset.value)\n    if (!this.needConfirm) {\n      const v = preset.value\n      this.emitValue(v)\n      this.closePopover()\n    }\n    this.requestUpdate()\n  }\n\n  private renderCalendarDays(baseMonth: Date) {\n    const year = baseMonth.getFullYear()\n    const month = baseMonth.getMonth()\n    const firstDay = new Date(year, month, 1)\n    const lastDay = new Date(year, month + 1, 0)\n\n    const offset = (firstDay.getDay() - this.weekStartsOn + 7) % 7\n    const days: Date[] = []\n\n    // Previous month filler\n    for (let i = offset; i > 0; i--) {\n      days.push(new Date(year, month, 1 - i))\n    }\n    // Current month days\n    for (let i = 1; i <= lastDay.getDate(); i++) {\n      days.push(new Date(year, month, i))\n    }\n    // Trailing days\n    const totalCells = Math.ceil(days.length / 7) * 7\n    while (days.length < totalCells) {\n      days.push(new Date(year, month + 1, days.length - totalCells + 1))\n    }\n\n    const todayTime = stripTime(new Date()).getTime()\n\n    return html`\n      <div class=\"space-y-2\">\n        <!-- Month Header -->\n        <div class=\"flex items-center justify-between px-1\">\n          <button\n            type=\"button\"\n            class=\"hover:bg-accent text-muted-foreground hover:text-foreground inline-flex size-7 items-center justify-center rounded-md transition-colors\"\n            @click=${() => (this.viewMonth = new Date(year, month - 1, 1))}\n          >\n            ${icon(ChevronLeft, 'chevron-left', 'size-4')}\n          </button>\n          <span class=\"text-sm font-medium\">${fmtMonth(baseMonth, this.locale)}</span>\n          <button\n            type=\"button\"\n            class=\"hover:bg-accent text-muted-foreground hover:text-foreground inline-flex size-7 items-center justify-center rounded-md transition-colors\"\n            @click=${() => (this.viewMonth = new Date(year, month + 1, 1))}\n          >\n            ${icon(ChevronRight, 'chevron-right', 'size-4')}\n          </button>\n        </div>\n\n        <!-- Weekdays -->\n        <div class=\"grid grid-cols-7 text-center text-xs text-muted-foreground\">\n          ${['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']\n            .slice(this.weekStartsOn)\n            .concat(['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'].slice(0, this.weekStartsOn))\n            .map((wd) => html`<span class=\"py-1\">${wd}</span>`)}\n        </div>\n\n        <!-- Day Cells Grid -->\n        <div class=\"grid grid-cols-7 gap-1 text-xs\">\n          ${days.map((d) => {\n            const isOutside = d.getMonth() !== month\n            const isToday = stripTime(d).getTime() === todayTime\n            const isDisabled = this.isDateDisabled(d)\n\n            let isSelected = false\n            let isInRange = false\n\n            if (this.type === 'multiple' && Array.isArray(this.internalSelected)) {\n              isSelected = this.internalSelected.some(\n                (item) => stripTime(item).getTime() === stripTime(d).getTime(),\n              )\n            } else if (this.type === 'range' && this.internalSelected && 'start' in this.internalSelected) {\n              const r = this.internalSelected as { start?: Date; end?: Date }\n              if (r.start && stripTime(r.start).getTime() === stripTime(d).getTime()) isSelected = true\n              if (r.end && stripTime(r.end).getTime() === stripTime(d).getTime()) isSelected = true\n              if (r.start && r.end && d > r.start && d < r.end) isInRange = true\n            } else if (this.internalSelected instanceof Date) {\n              isSelected = stripTime(this.internalSelected).getTime() === stripTime(d).getTime()\n            }\n\n            return html`\n              <button\n                type=\"button\"\n                class=${cn(\n                  'relative flex size-8 items-center justify-center rounded-md transition-colors',\n                  isOutside && 'text-muted-foreground/40',\n                  isToday && !isSelected && 'border border-primary text-primary font-medium',\n                  isInRange && 'bg-primary/10 text-primary rounded-none',\n                  isSelected && 'bg-primary text-primary-foreground font-semibold shadow-xs',\n                  !isSelected && !isInRange && !isDisabled && 'hover:bg-accent hover:text-accent-foreground',\n                  isDisabled && 'cursor-not-allowed opacity-40',\n                )}\n                ?disabled=${isDisabled}\n                @click=${() => this.selectDate(d)}\n              >\n                ${this.renderCell ? this.renderCell(d) : d.getDate()}\n              </button>\n            `\n          })}\n        </div>\n      </div>\n    `\n  }\n\n  render() {\n    const effectiveFormat = this.dateFormat ?? this.format\n\n    const triggerLabel = (() => {\n      if (!this.internalSelected) return this.placeholder ?? (this.type === 'range' ? 'Select date range' : 'Pick a date')\n      if (this.type === 'multiple' && Array.isArray(this.internalSelected)) {\n        return this.internalSelected.length\n          ? `${this.internalSelected.length} dates selected`\n          : this.placeholder ?? 'Select dates'\n      }\n      if (this.type === 'range' && 'start' in this.internalSelected) {\n        const r = this.internalSelected as { start?: Date; end?: Date }\n        if (!r.start) return this.placeholder ?? 'Select date range'\n        const s = fmtDate(r.start, this.locale, effectiveFormat)\n        const e = r.end ? fmtDate(r.end, this.locale, effectiveFormat) : ''\n        return e ? `${s} ${this.separator} ${e}` : s\n      }\n      if (this.internalSelected instanceof Date) {\n        const dt = fmtDate(this.internalSelected, this.locale, effectiveFormat)\n        if (this.showTime) {\n          const tm = fmtTime(this.internalSelected, this.showSeconds, this.use24Hour)\n          return `${dt} ${tm}`\n        }\n        return dt\n      }\n      return this.placeholder ?? 'Pick a date'\n    })()\n\n    const sizeClass =\n      this.size === 'small'\n        ? 'h-8 px-2.5 text-xs'\n        : this.size === 'large'\n          ? 'h-10 px-3.5 text-base'\n          : 'h-9 px-3 text-sm'\n\n    const statusClass =\n      this.status === 'error'\n        ? 'border-destructive text-destructive focus-visible:ring-destructive'\n        : this.status === 'warning'\n          ? 'border-amber-500 text-amber-600 focus-visible:ring-amber-500'\n          : 'border-input hover:border-accent-foreground/50'\n\n    const activePresets = this.presets ?? (this.type === 'range' ? defaultRangePresets() : undefined)\n\n    return html`\n      <div part=\"base\" class=\"relative inline-block w-full\">\n        <!-- Trigger button -->\n        <button\n          type=\"button\"\n          part=\"trigger\"\n          class=${cn(\n            'bg-background focus-visible:ring-ring flex w-full items-center justify-between rounded-md border text-left font-normal transition-colors focus-visible:ring-2 focus-visible:outline-none select-none',\n            sizeClass,\n            statusClass,\n            !this.internalSelected && 'text-muted-foreground',\n            this.disabled && 'cursor-not-allowed opacity-50 pointer-events-none',\n          )}\n          aria-expanded=${this.open ? 'true' : 'false'}\n          aria-haspopup=\"dialog\"\n          @click=${this.toggleOpen}\n        >\n          <div class=\"flex items-center gap-2 truncate\">\n            ${icon(CalendarIcon, 'calendar', 'text-muted-foreground size-4 shrink-0')}\n            <span class=\"truncate\">${triggerLabel}</span>\n          </div>\n\n          <div class=\"flex items-center gap-1\">\n            ${this.clearable && this.internalSelected\n              ? html`\n                  <span\n                    role=\"button\"\n                    class=\"hover:bg-accent text-muted-foreground hover:text-foreground inline-flex size-5 items-center justify-center rounded p-0.5\"\n                    aria-label=\"Clear date\"\n                    @click=${this.handleClear}\n                  >\n                    ${icon(X, 'x', 'size-3.5')}\n                  </span>\n                `\n              : nothing}\n          </div>\n        </button>\n\n        <!-- Popover Content -->\n        ${this.open\n          ? html`\n              <div\n                part=\"popover\"\n                class=\"border-border bg-popover text-popover-foreground absolute z-50 mt-1 flex flex-col rounded-lg border p-3 shadow-lg outline-none animate-in fade-in-0 zoom-in-95\"\n              >\n                <div class=\"flex gap-3\">\n                  <!-- Optional Presets Sidebar -->\n                  ${activePresets && activePresets.length > 0\n                    ? html`\n                        <div class=\"border-border flex w-28 flex-col gap-1 border-r pr-3 text-xs\">\n                          ${activePresets.map(\n                            (p) => html`\n                              <button\n                                type=\"button\"\n                                class=\"hover:bg-accent hover:text-accent-foreground rounded px-2 py-1 text-left transition-colors\"\n                                @click=${() => this.applyPreset(p)}\n                              >\n                                ${p.label}\n                              </button>\n                            `,\n                          )}\n                        </div>\n                      `\n                    : nothing}\n\n                  <!-- Month Calendar(s) -->\n                  <div class=\"flex gap-4\">\n                    ${this.renderCalendarDays(this.viewMonth)}\n                    ${this.numberOfMonths > 1\n                      ? this.renderCalendarDays(\n                          new Date(this.viewMonth.getFullYear(), this.viewMonth.getMonth() + 1, 1),\n                        )\n                      : nothing}\n                  </div>\n                </div>\n\n                <!-- Footer (Time, Today shortcut, OK button) -->\n                ${this.showTime || this.showCurrentDate || this.needConfirm\n                  ? html`\n                      <div class=\"border-border mt-3 flex items-center justify-between border-t pt-2 text-xs\">\n                        <div>\n                          ${this.showCurrentDate\n                            ? html`\n                                <button\n                                  type=\"button\"\n                                  class=\"text-primary hover:underline font-medium\"\n                                  @click=${() => this.selectDate(stripTime(new Date()))}\n                                >\n                                  Today\n                                </button>\n                              `\n                            : nothing}\n                        </div>\n                        <div class=\"flex items-center gap-2\">\n                          ${this.needConfirm\n                            ? html`\n                                <button\n                                  type=\"button\"\n                                  class=\"bg-primary text-primary-foreground hover:bg-primary/90 focus-visible:ring-ring rounded-md px-3 py-1 font-medium shadow-xs transition-colors focus-visible:ring-2 focus-visible:outline-none\"\n                                  @click=${this.confirmSelection}\n                                >\n                                  OK\n                                </button>\n                              `\n                            : nothing}\n                        </div>\n                      </div>\n                    `\n                  : nothing}\n              </div>\n            `\n          : nothing}\n      </div>\n    `\n  }\n}\n\ncustomElements.get('uip-date-picker') || customElements.define('uip-date-picker', UipDatePicker)\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'uip-date-picker': UipDatePicker\n  }\n}\n",
      "type": "registry:file",
      "target": "src/uip/components/date-picker/date-picker.ts"
    }
  ],
  "dependencies": [
    "lit",
    "lucide"
  ],
  "registryDependencies": [
    "https://uipkge.dev/r/lit/lit-core.json"
  ]
}
