{"name":"calendar","title":"Calendar","description":"An inline, always-open date field component that allows users to select a date.","installCommand":"bunx marko-ui add calendar -y","usageTags":"<Calendar>","importSnippet":"import Calendar from \"@/components/ui/calendar/calendar.marko\";","usageSnippet":"<let/selectedDates=[] as { year: number; month: number; day: number }[]/>\n<Calendar value:=selectedDates/>","tags":["Calendar"],"parts":[],"props":[{"name":"captionLayout","type":"'label' | 'dropdown'","required":false,"description":"Caption presentation. `\"label\"` (default) shows the visible range as a\nplain view-trigger button; `\"dropdown\"` swaps it for native month and year\n`<select>` controls. Component-owned — not forwarded to the machine."},{"name":"createCalendar","type":"((identifier: CalendarIdentifier) => Calendar) | undefined","required":false,"description":"A function that creates a Calendar object for a given calendar identifier.\nEnables non-Gregorian calendar support (Persian, Buddhist, Islamic, etc.)\nwithout bundling all calendars by default."},{"name":"defaultFocusedValue","type":"PlainDate","required":false,"description":"Initial visible-month focus, as a plain (optionally non-Gregorian)\ndate. Pins the SSR-rendered month instead of falling back to the\nbrowser's wall-clock \"today\" (`getTodayDate`), which would otherwise\nmake SSR and hydrated markup diverge near midnight or across\ntimezones."},{"name":"defaultOpen","type":"boolean | undefined","required":false,"description":"The initial open state of the date picker when rendered.\nUse when you don't need to control the open state of the date picker."},{"name":"defaultValue","type":"DateValue[] | undefined","required":false,"description":"The initial selected date(s) when rendered.\nUse when you don't need to control the selected date(s) of the date picker."},{"name":"defaultView","type":"DateView | undefined","required":false,"default":"day","description":"The default view of the calendar"},{"name":"dir","type":"'ltr' | 'rtl' | undefined","required":false,"default":"ltr","description":"The document's text/writing direction."},{"name":"disabled","type":"boolean | undefined","required":false,"description":"Whether the calendar is disabled."},{"name":"fixedWeeks","type":"boolean | undefined","required":false,"description":"Whether the calendar should have a fixed number of weeks.\nThis renders the calendar with 6 weeks instead of 5 or 6."},{"name":"focusedValue","type":"PlainDate","required":false,"description":"Controlled visible-month focus, as a plain (optionally non-Gregorian) date."},{"name":"format","type":"((date: DateValue, details: LocaleDetails) => string) | undefined","required":false,"description":"The format of the date to display in the input."},{"name":"getRootNode","type":"(() => ShadowRoot | Document | Node) | undefined","required":false,"description":"A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."},{"name":"ids","type":"ElementIds | undefined","required":false,"description":"The ids of the elements in the date picker. Useful for composition."},{"name":"invalid","type":"boolean | undefined","required":false,"description":"Whether the date picker is invalid"},{"name":"isDateUnavailable","type":"((date: DateValue, locale: string) => boolean) | undefined","required":false,"description":"Returns whether a date of the calendar is available."},{"name":"locale","type":"string | undefined","required":false,"default":"en-US","description":"The locale (BCP 47 language tag) to use when formatting the date."},{"name":"max","type":"{ year: number; month: number; day: number; }","required":false,"description":"Latest selectable date (inclusive); days after it render disabled."},{"name":"maxSelectedDates","type":"number | undefined","required":false,"description":"The maximum number of dates that can be selected.\nThis is only applicable when `selectionMode` is `multiple`."},{"name":"maxView","type":"DateView | undefined","required":false,"default":"year","description":"The maximum view of the calendar"},{"name":"min","type":"{ year: number; month: number; day: number; }","required":false,"description":"Earliest selectable date (inclusive); days before it render disabled."},{"name":"minView","type":"DateView | undefined","required":false,"default":"day","description":"The minimum view of the calendar"},{"name":"name","type":"string | undefined","required":false,"description":"The `name` attribute of the input element."},{"name":"numOfMonths","type":"number | undefined","required":false,"description":"The number of months to display."},{"name":"openOnClick","type":"boolean | undefined","required":false,"default":"false","description":"Whether to open the calendar when the input is clicked."},{"name":"outsideDaySelectable","type":"boolean | undefined","required":false,"default":"false","description":"Whether day outside the visible range can be selected."},{"name":"parse","type":"((value: string, details: LocaleDetails) => DateValue | undefined) | undefined","required":false,"description":"Function to parse the date from the input back to a DateValue."},{"name":"placeholder","type":"string | undefined","required":false,"description":"The placeholder text to display in the input."},{"name":"positioning","type":"PositioningOptions | undefined","required":false,"description":"The user provided options used to position the date picker content"},{"name":"readOnly","type":"boolean | undefined","required":false,"description":"Whether the calendar is read-only."},{"name":"required","type":"boolean | undefined","required":false,"description":"Whether the date picker is required"},{"name":"selectionMode","type":"SelectionMode | undefined","required":false,"default":"single","description":"The selection mode of the calendar.\n- `single` - only one date can be selected\n- `multiple` - multiple dates can be selected\n- `range` - a range of dates can be selected"},{"name":"showWeekNumbers","type":"boolean | undefined","required":false,"description":"Whether to show the week number column in the day view."},{"name":"startOfWeek","type":"number | undefined","required":false,"description":"The first day of the week.\n `0` - Sunday\n `1` - Monday\n `2` - Tuesday\n `3` - Wednesday\n `4` - Thursday\n `5` - Friday\n `6` - Saturday"},{"name":"timeZone","type":"string | undefined","required":false,"default":"UTC","description":"The time zone to use"},{"name":"translations","type":"IntlTranslations | undefined","required":false,"description":"The localized messages to use."},{"name":"value","type":"{ year: number; month: number; day: number; }[]","required":false,"description":"Selected date(s) as plain objects — one entry per selected date (single mode uses a one-element array)."},{"name":"view","type":"DateView | undefined","required":false,"description":"The view of the calendar"}],"events":[{"name":"onFocusChange","arg":"FocusChangeDetails","description":"Function called when the focused date changes."},{"name":"onOpenChange","arg":"OpenChangeDetails","description":"Function called when the calendar opens or closes."},{"name":"onValueChange","arg":"ValueChangeDetails","description":"Function called when the value changes."},{"name":"onViewChange","arg":"ViewChangeDetails","description":"Function called when the view changes."},{"name":"onVisibleRangeChange","arg":"VisibleRangeChangeDetails","description":"Function called when the visible range changes."},{"name":"valueChange","arg":"{ year: number; month: number; day: number; }[]","description":"Fires with the full selection as plain date objects whenever it changes; enables `value:=` two-way binding."}],"keyboard":[],"accessibilityNotes":[],"examples":[{"id":"calendar-basic","title":"Basic","source":"import Calendar from \"@marko-ui/shadcn/ui/calendar/calendar.marko\";\n\nstatic const focusedValue = { year: 2026, month: 8, day: 15 };\n\n<Calendar defaultFocusedValue=focusedValue/>","description":"An uncontrolled calendar with no date selected initially."},{"id":"calendar-hijri","title":"Persian / Hijri / Jalali Calendar","source":"import Calendar from \"@marko-ui/shadcn/ui/calendar/calendar.marko\";\nimport { createCalendar as createCalendarInstance } from \"@internationalized/date\";\n\n<Calendar\n  locale=\"fa-IR\"\n  createCalendar=(id) => createCalendarInstance(id)\n  defaultFocusedValue={ year: 2026, month: 8, day: 15, calendar: \"persian\" }\n  class=\"rounded-lg border\"\n/>","description":"Pass `locale` and `createCalendar` (re-exported from `@internationalized/date`, the same helper upstream's own MDX points to) to switch the calendar system — no react-day-picker locale swap needed (upstream swaps in a persian build of the library and loads the Vazirmatn webfont for Arabic-script glyphs; here the font is your app's business). A controlled initial `value` is intentionally omitted here: `calendar.marko`'s `toDateValue()` always builds a Gregorian `CalendarDate`, so a hardcoded non-Gregorian `{ year, month, day }` would be silently misinterpreted."},{"id":"calendar-range","title":"Range","source":"import Calendar from \"@marko-ui/shadcn/ui/calendar/calendar.marko\";\n\nstatic const focusedValue = { year: 2026, month: 8, day: 15 };\n\n<Calendar selectionMode=\"range\" defaultFocusedValue=focusedValue/>","description":"Set `selectionMode=\"range\"` to select a start and end date."},{"id":"calendar-caption","title":"Month and Year Selector","source":"import Calendar from \"@marko-ui/shadcn/ui/calendar/calendar.marko\";\n\nstatic const focusedValue = { year: 2026, month: 8, day: 15 };\n\n<Calendar captionLayout=\"dropdown\" defaultFocusedValue=focusedValue/>","description":"Set `captionLayout=\"dropdown\"` to swap the caption label for native month and year `<select>` controls."},{"id":"calendar-presets","title":"Presets","source":"import Calendar from \"@marko-ui/shadcn/ui/calendar/calendar.marko\";\nimport Card from \"@marko-ui/shadcn/ui/card/card.marko\";\nimport CardContent from \"@marko-ui/shadcn/ui/card/content.marko\";\nimport CardFooter from \"@marko-ui/shadcn/ui/card/footer.marko\";\nimport Button from \"@marko-ui/shadcn/ui/button/button.marko\";\n\nstatic const today = new Date(2026, 5, 15);\n\nstatic function addDays(base: Date, days: number) {\n  const result = new Date(base);\n  result.setDate(result.getDate() + days);\n  return { year: result.getFullYear(), month: result.getMonth() + 1, day: result.getDate() };\n}\n\nstatic const presets = [\n  { label: \"Today\", days: 0 },\n  { label: \"Tomorrow\", days: 1 },\n  { label: \"In 3 days\", days: 3 },\n  { label: \"In a week\", days: 7 },\n  { label: \"In 2 weeks\", days: 14 },\n];\n\n<let/value=[addDays(today, 0)]/>\n\n<Card class=\"mx-auto w-fit max-w-[300px]\" size=\"sm\">\n  <CardContent>\n    <Calendar\n      value=value\n      valueChange=(next) => { value = next; }\n      class=\"p-0 [--cell-size:--spacing(9.5)]\"\n    />\n  </CardContent>\n  <CardFooter class=\"flex flex-wrap gap-2 border-t\">\n    <for|preset| of=presets>\n      <Button type=\"button\"\n        variant=\"outline\"\n        size=\"sm\"\n        class=\"flex-1\"\n        onClick() {\n          value = [addDays(today, preset.days)];\n        }\n      >\n        ${preset.label}\n      </Button>\n    </for>\n  </CardFooter>\n</Card>","description":"Preset buttons that jump the selection to a relative date. Our Calendar has no separate controlled visible-month prop (upstream's `month`/`onMonthChange`) — the Zag machine re-focuses its visible month whenever `value` changes, so presets still navigate the grid correctly."},{"id":"calendar-time","title":"Date and Time Picker","source":"import Calendar from \"@marko-ui/shadcn/ui/calendar/calendar.marko\";\nimport Card from \"@marko-ui/shadcn/ui/card/card.marko\";\nimport CardContent from \"@marko-ui/shadcn/ui/card/content.marko\";\nimport CardFooter from \"@marko-ui/shadcn/ui/card/footer.marko\";\nimport Field from \"@marko-ui/shadcn/ui/field/field.marko\";\nimport FieldGroup from \"@marko-ui/shadcn/ui/field/field-group.marko\";\nimport FieldLabel from \"@marko-ui/shadcn/ui/field/field-label.marko\";\nimport InputGroup from \"@marko-ui/shadcn/ui/input-group/input-group.marko\";\nimport InputGroupInput from \"@marko-ui/shadcn/ui/input-group/input.marko\";\nimport InputGroupAddon from \"@marko-ui/shadcn/ui/input-group/addon.marko\";\nimport Icon from \"@marko-ui/shadcn/ui/icon/icon.marko\";\n\nstatic const focusedValue = { year: 2026, month: 8, day: 15 };\n\n<Card size=\"sm\" class=\"mx-auto w-fit\">\n  <CardContent>\n    <Calendar class=\"p-0\" defaultFocusedValue=focusedValue/>\n  </CardContent>\n  <CardFooter class=\"border-t bg-card\">\n    <FieldGroup>\n      <Field>\n        <FieldLabel for=\"calendar-time-from\">Start Time</FieldLabel>\n        <InputGroup>\n          <InputGroupInput\n            id=\"calendar-time-from\"\n            type=\"time\"\n            step=\"1\"\n            value=\"10:30:00\"\n            class=\"appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none\"\n          />\n          <InputGroupAddon>\n            <Icon name=\"Clock2Icon\" class=\"text-muted-foreground\"/>\n          </InputGroupAddon>\n        </InputGroup>\n      </Field>\n      <Field>\n        <FieldLabel for=\"calendar-time-to\">End Time</FieldLabel>\n        <InputGroup>\n          <InputGroupInput\n            id=\"calendar-time-to\"\n            type=\"time\"\n            step=\"1\"\n            value=\"12:30:00\"\n            class=\"appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none\"\n          />\n          <InputGroupAddon>\n            <Icon name=\"Clock2Icon\" class=\"text-muted-foreground\"/>\n          </InputGroupAddon>\n        </InputGroup>\n      </Field>\n    </FieldGroup>\n  </CardFooter>\n</Card>","description":"A calendar paired with `Field`/`InputGroup` time inputs in a card footer."},{"id":"calendar-booked-dates","title":"Booked dates","source":"import Calendar from \"@marko-ui/shadcn/ui/calendar/calendar.marko\";\nimport Card from \"@marko-ui/shadcn/ui/card/card.marko\";\nimport CardContent from \"@marko-ui/shadcn/ui/card/content.marko\";\n\nstatic const bookedStart = new Date(2026, 0, 12);\n\nstatic function toPlain(date: Date) {\n  return { year: date.getFullYear(), month: date.getMonth() + 1, day: date.getDate() };\n}\n\nstatic const bookedDates = Array.from({ length: 15 }, (_, i) => {\n  const date = new Date(bookedStart);\n  date.setDate(date.getDate() + i);\n  return toPlain(date);\n});\n\nstatic function isBooked(candidate: { year: number; month: number; day: number }) {\n  return bookedDates.some(\n    (booked) => booked.year === candidate.year && booked.month === candidate.month && booked.day === candidate.day,\n  );\n}\n\nstatic const focusedValue = toPlain(bookedStart);\n\n<Card class=\"mx-auto w-fit p-0\">\n  <CardContent class=\"p-0\">\n    <Calendar\n      isDateUnavailable=(date) => isBooked({ year: date.year, month: date.month, day: date.day })\n      defaultFocusedValue=focusedValue\n    />\n  </CardContent>\n</Card>","description":"Use `isDateUnavailable` to mark specific dates unselectable. Unlike upstream's strikethrough styling, our day button has no per-day custom-class hook, so booked days render as ordinary unselectable days rather than struck through."},{"id":"calendar-custom-days","title":"Custom Cell Size","source":"import Calendar from \"@marko-ui/shadcn/ui/calendar/calendar.marko\";\nimport Card from \"@marko-ui/shadcn/ui/card/card.marko\";\nimport CardContent from \"@marko-ui/shadcn/ui/card/content.marko\";\n\nstatic const rangeStart = new Date(2026, 5, 8);\n\nstatic function toPlain(date: Date) {\n  return { year: date.getFullYear(), month: date.getMonth() + 1, day: date.getDate() };\n}\n\nstatic function addDays(base: Date, days: number) {\n  const result = new Date(base);\n  result.setDate(result.getDate() + days);\n  return result;\n}\n\n<let/value=[toPlain(rangeStart), toPlain(addDays(rangeStart, 10))]/>\n\n<Card class=\"mx-auto w-fit p-0\">\n  <CardContent class=\"p-0\">\n    <Calendar\n      selectionMode=\"range\"\n      value=value\n      valueChange=(next) => { value = next; }\n      captionLayout=\"dropdown\"\n      class=\"[--cell-size:--spacing(10)] md:[--cell-size:--spacing(12)]\"\n    />\n  </CardContent>\n</Card>","description":"Customize cell size with the `--cell-size` CSS variable, responsively via breakpoint-specific arbitrary values. Upstream's per-day weekend/weekday price labels are omitted — our day button has no content-render slot to inject them."},{"id":"calendar-demo","title":"Dropdown caption","source":"import Calendar from \"@marko-ui/shadcn/ui/calendar/calendar.marko\";\n\nstatic const focusedValue = { year: 2026, month: 8, day: 15 };\n\n<Calendar captionLayout=\"dropdown\" defaultFocusedValue=focusedValue/>","description":"The hero preview: `captionLayout=\"dropdown\"` swaps the caption label for native month and year `<select>` controls."},{"id":"calendar-controlled","title":"Controlled","source":"import Calendar from \"@marko-ui/shadcn/ui/calendar/calendar.marko\";\n\n<let/value=[{ year: 2026, month: 8, day: 10 }]/>\n\n<div class=\"flex flex-col items-start gap-4\">\n  <Calendar\n    value=value\n    valueChange=(next) => { value = next; }\n  />\n  <span class=\"text-sm text-muted-foreground\">\n    Selected: ${value[0] ? `${value[0].year}-${value[0].month}-${value[0].day}` : \"none\"}\n  </span>\n</div>","description":"Zag machines are controlled: a `value` prop without a change handler never moves. Pair it with `valueChange`, or use Marko's bind shorthand `value:=state`.","essential":true},{"id":"calendar-multiple","title":"Multiple","source":"import Calendar from \"@marko-ui/shadcn/ui/calendar/calendar.marko\";\n\nstatic const focusedValue = { year: 2026, month: 8, day: 15 };\n\n<Calendar selectionMode=\"multiple\" defaultFocusedValue=focusedValue/>","description":"Set `selectionMode=\"multiple\"` to allow selecting more than one date."},{"id":"calendar-min-max","title":"Min / Max","source":"import Calendar from \"@marko-ui/shadcn/ui/calendar/calendar.marko\";\n\n<Calendar min={ year: 2026, month: 8, day: 1 } max={ year: 2026, month: 8, day: 20 }/>","description":"Pass `min` and `max` as plain `{ year, month, day }` objects to restrict the selectable range."},{"id":"calendar-disabled","title":"Disabled","source":"import Calendar from \"@marko-ui/shadcn/ui/calendar/calendar.marko\";\n\nstatic const focusedValue = { year: 2026, month: 8, day: 15 };\n\n<Calendar disabled defaultFocusedValue=focusedValue/>","description":"A disabled calendar ignores pointer input and is skipped by the tab order."},{"id":"calendar-rtl","title":"RTL","source":"import Calendar from \"@marko-ui/shadcn/ui/calendar/calendar.marko\";\n\nstatic const focusedValue = { year: 2026, month: 8, day: 15 };\n\n<div dir=\"rtl\">\n  <Calendar\n    captionLayout=\"dropdown\"\n    dir=\"rtl\"\n    locale=\"ar-SA\"\n    defaultFocusedValue=focusedValue\n    class=\"rounded-lg border [--cell-size:--spacing(9)]\"\n  />\n</div>","description":"Pass `dir=\"rtl\"` and a BCP-47 `locale` string (the Zag machine's own locale shape, rather than upstream's `react-day-picker/locale` object) for right-to-left rendering."}],"importInstallCommand":"bun add @marko-ui/shadcn marko-zag","nativeAttributes":"div"}
