import type { Snippet } from 'svelte'; import type { HTMLAttributes } from 'svelte/elements'; import type { CalendarEvent, CalendarSelection, CalendarViewMode, DateCategory, DateRange, DayCellContext, EventItemContext, HeaderContext } from './calendar.types.js'; import type { CalendarSlots, CalendarVariants } from './calendar.variants.js'; export type CalendarSlotName = 'base' | 'header' | 'title' | 'nav' | 'navButton' | 'grid' | 'weekdayHeader' | 'weekday' | 'weekRow' | 'weekNumber' | 'day' | 'dayNumber' | 'dotContainer' | 'dot' | 'list' | 'dateHeader' | 'empty' | 'item' | 'colorBar' | 'eventTime' | 'eventTitle' | 'eventDescription' | 'eventHelper' | 'legend' | 'legendItem' | 'legendDot' | 'legendLabel' | 'yearGrid' | 'yearMonth' | 'yearMonthTitle' | 'yearMiniDay' | 'yearMiniDot' | 'weekGrid' | 'weekColumn' | 'weekColumnHeader' | 'weekColumnDayName' | 'weekColumnDayNumber' | 'weekEventList' | 'weekAllDayEvent' | 'multiDayBar' | 'multiDayBarContainer' | 'agendaView' | 'agendaDayGroup' | 'agendaDayHeader' | 'agendaEventList' | 'dayView' | 'dayViewHeader' | 'timeGrid' | 'timeGutter' | 'timeHeadCell' | 'timeCorner' | 'timeLabel' | 'timeSlotRow' | 'timeDayColumn' | 'timeEvent' | 'allDayArea' | 'currentTimeLine' | 'weekTimeLayout' | 'eventPopover' | 'eventPopoverItem' | 'miniCalendar' | 'miniCalendarHeader' | 'miniCalendarTitle' | 'miniCalendarNavButton' | 'miniCalendarWeekday' | 'miniCalendarDay'; /** * @summary Appointments on a real calendar — month, week or day, with multi-day spans and recurrence. * @description Flexible calendar component with month, year, week, and day views. * Renders timed appointments, multi-day spans and recurrence on a time grid, with * event display, date selection and configurable layout. For a headless grid that * buckets your own domain content (meals, shifts, bookings) per day, use `Planner`. * For one lane per resource (rooms, chairs, vehicles) with bars spanning the days * each is occupied, use `ResourceTimeline`. * * @tag display * @related Planner * @related ResourceTimeline * @related DatePicker * @related DateRangePicker * * @example * ```svelte * * ``` * * @example * ```svelte * * ``` * * @example * ```svelte * * {#snippet children()} * * * {/snippet} * * ``` * * @example * ```svelte * * * * ``` */ export interface CalendarProps extends Omit, Omit, 'children'> { /** Array of events to display on the calendar. @default [] */ events?: CalendarEvent[]; /** Event categories for color coding and legend. @default [] */ categories?: DateCategory[]; /** * Active view mode. Supports bind:view. * * `week` and `day` are hour grids. The week's seven columns keep at least * `--blocks-calendar-day-min-width` each (5 rem at `size="sm"`, 6 rem at `md`, * 7 rem at `lg`); once they no longer fit, the grid scrolls sideways instead of * shrinking the days to a stripe. The hour gutter stays pinned to the left * while it does and the day heads — with the all-day band under them — to the * top, and arrow-key navigation brings the focused day into view. Set the * property on the calendar — `style="--blocks-calendar-day-min-width: 8rem"` — * to trade more scrolling for wider days. * * While the week is scrolling, a horizontal touch drag belongs to the * scroller: it moves the days rather than paging to the next week. A week that * fits keeps the swipe. The header arrows, `ArrowLeft`/`ArrowRight` and * `bind:view` are unaffected either way. On a phone the honest week is often * no week at all: bind this prop to a `MediaQuery` and narrow `views` with it * (the `MediaQuery` example on the component) rather than handing over seven * columns behind a scrollbar. * @default 'month' * @summary Which view is on screen — month, week, day, year or agenda. */ view?: CalendarViewMode; /** * Which views the header's switcher offers. It filters the switcher only: a * `view` left out of the list still renders, so narrowing the list is how a * layout hides a view it cannot serve while still choosing it itself — the * viewport-driven pairing under `view`. * @default ['month', 'week', 'day', 'year', 'agenda'] * @summary Which views the header's switcher offers. */ views?: CalendarViewMode[]; /** Selection behavior. @default 'single' */ selectionMode?: 'single' | 'range' | 'multiple'; /** Currently selected date(s). Supports bind:value. */ value?: CalendarSelection; /** * Initial reference day the grid is anchored on, without selecting it. Use * this to open a **week** or **day** view on a specific week — `defaultMonth`/ * `defaultYear` resolve to the 1st, whose week can fall mostly in the previous * month. Ignored when `value` is set (the selection anchors instead); takes * priority over `defaultMonth`/`defaultYear`. Read at mount only. */ defaultDate?: Date; /** * Initial displayed month (0–11). Used only when `value` and `defaultDate` * are unset; when `value` is provided, the calendar opens on the value's * month. Best for month/year views — for week/day views prefer `defaultDate`. * Defaults to current month. */ defaultMonth?: number; /** * Initial displayed year. Used only when `value` is unset; when * `value` is provided, the calendar opens on the value's year. * Defaults to current year. */ defaultYear?: number; /** * BCP 47 locale tag for date formatting — month names, weekday names, the * header title and the clock time of timed events in the list-based views * (hour cycle, separator and padding all follow the locale, so a 12-hour * locale renders "9:05 AM"). Defaults to `'auto'`, which follows the active * `` locale, so an app that already declares its language does * not have to repeat it here. SSR-safe: the locale comes from context, so the * server and the client resolve the same tag (`Intl` with `undefined` would * follow the runtime and disagree across hydration). Falls back to the base * locale (`en`) when no provider is mounted. Pass an explicit tag * (e.g. `'de-DE'`, `'ja-JP'`) to override. * @default 'auto' * @summary Which language the month and weekday names are rendered in. */ locale?: string; /** First day of the week. 0 = Sunday, 1 = Monday. @default 1 */ weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6; /** Show ISO week numbers in the left margin. @default false */ showWeekNumbers?: boolean; /** Show days from previous/next months to fill the grid. @default true */ showOutsideDays?: boolean; /** Always show 6 weeks in the grid. @default false */ fixedWeeks?: boolean; /** * Visually mark today across every view — the month cell, the week column * header, the year mini-day, the agenda day header and the mini calendar. * `aria-current="date"` is **not** affected: it is a semantic pointer, so a * screen-reader user keeps the orientation a purely visual preference should * not take away. Neither is the time grid's current-time line, which marks the * current *time* rather than the day (see `timeGridHourHeight`'s neighbours). * Matches `Planner`'s prop of the same name. * @default true * @summary Whether today gets its visual marker. Never touches aria-current. */ highlightToday?: boolean; /** Earliest selectable/navigable date. */ minDate?: Date; /** Latest selectable/navigable date. */ maxDate?: Date; /** Specific dates that are disabled (not selectable). */ disabledDates?: Date[]; /** Function to test whether a date is disabled. */ isDateDisabled?: (date: Date) => boolean; /** Visual style. @default 'default' */ variant?: 'default' | 'bordered' | 'ghost'; /** Component size. @default 'md' */ size?: 'sm' | 'md' | 'lg'; /** Fires when the selected date(s) change. */ onValueChange?: (value: CalendarSelection) => void; /** * Fires when the displayed month/year changes via navigation — the month and * year views, and the header's month picker in any view. The agenda's arrows * step a window of days instead of a month and report through `onNavigate`. */ onMonthChange?: (month: number, year: number) => void; /** * Fires after **any** navigation, in every view, with the new reference date * and the visible range — load data here. A **view switch** counts as one: it * changes the window (a padded month grid, seven days, `agendaDays` from the * anchor …) without moving the reference date, and a loader that missed it * rendered the new view against the old view's rows. The per-view callbacks * (`onMonthChange` / `onWeekChange` / `onDayChange`) still fire and are the * better fit when you only care about one view; this one spares you * reconstructing the window yourself — and in the agenda it is the *only* * navigation callback, whose window no per-view callback can name. The range * is view-accurate: month spans the padded cell grid (spill days included), * week/day the visible days, year 1 Jan–31 Dec, agenda `agendaDays` from the * reference date. Matches `Planner`'s `onNavigate`. * @summary Fires on every navigation with the new visible range — the data-loading hook. */ onNavigate?: (date: Date, range: DateRange) => void; /** * Fires when the view mode changes — before `onNavigate`, which the switch * also fires because the visible window changed with it. A switch to the view * already on screen is a no-op and fires neither. */ onViewChange?: (view: CalendarViewMode) => void; /** Fires when a date cell is clicked (regardless of selection change). */ onDateClick?: (date: Date) => void; /** Fires when an event is clicked. */ onEventClick?: (event: CalendarEvent) => void; /** Fires when the displayed week changes (week view). */ onWeekChange?: (weekStart: Date) => void; /** Fires when the displayed day changes (day view). */ onDayChange?: (date: Date) => void; /** Fires on double-click on a day cell for event creation. The consumer shows their own form. */ onDateCreate?: (date: Date, view: CalendarViewMode) => void; /** Fires on click on an empty time slot for event creation. Returns default 1h duration. */ onTimeSlotCreate?: (start: Date, end: Date) => void; /** Custom snippet for rendering a day cell. */ dayCell?: Snippet<[DayCellContext]>; /** * Custom snippet for rendering an event item in the list-based views (agenda * and the month event list). Time-grid views (week/day) render events through * their hour grid and ignore this snippet. * * Replaces the default row entirely, including the clock time an event with * `allDay: false` shows there — format it from `event.start`/`event.end` * yourself (`formatTimeRange` from `@urbicon-ui/blocks/date` is the same * helper the default uses). For a multi-day event the context's `isStart` / * `isEnd` say which end of the span this row is: the default prints `start` * on the first day, "until " on the last, nothing in between, because * `event.start` is the same instant on every row. The chronological order of * a day is not part of the snippet's job: the calendar sorts each day before * rendering. */ eventItem?: Snippet<[EventItemContext]>; /** Custom snippet for the header area. */ header?: Snippet<[HeaderContext]>; /** Whether to show the built-in legend. Defaults to true when categories are provided. */ showLegend?: boolean; /** Whether to show the detail list when a date is selected. Auto-enabled when events are provided. */ showEventList?: boolean; /** * How many days the agenda lists, counted **from the reference date** — the * day `value`/`defaultDate` anchors on, or today. So `agendaDays={1}` next to * a `defaultDate` is one day's list, and the default is roughly a month ahead * of that day rather than the calendar month it sits in. * * The arrows, `ArrowLeft`/`ArrowRight` and the swipe step the whole window, so * the next list starts the day after the current one ends, and `minDate`/ * `maxDate` bound the WINDOW rather than its anchor: a step clamps * span-preserving and the arrows disable once an edge is reached. Navigation * reports through `onNavigate` — uniformly, including at `agendaDays={1}`, * rather than switching callbacks on a prop value; the header's month picker * still reports `onMonthChange`, because a month is what it picks. * * The window is always exactly this many days; the RENDERING skips days with * no events, so a month-long window of one busy day is one heading, and a * window of nothing renders the empty state. * * Values outside 1–366 (and non-numbers) fall back to the default and warn in * DEV — a list is not a place to accidentally walk a decade. * @default 30 * @summary How many days the list covers, counted from the reference date. */ agendaDays?: number; /** Show time grid in week/day views. Auto-detected from events with allDay: false. */ showTimeGrid?: boolean; /** First visible hour in time grid. @default 7 */ timeGridStartHour?: number; /** Last visible hour in time grid (exclusive). @default 20 */ timeGridEndHour?: number; /** Time slot interval in minutes. @default 60 */ timeGridInterval?: 30 | 60; /** * Height of one hour row in the time grid, in pixels. Left unset it follows * `size` (sm 40 · md 48 · lg 64), which is the only reason a nine-hour day * costs 432 px of card height whether or not the consumer has it. Set a * smaller number for a compact day, a larger one for finer slots. Drives the * label column, the slot rows, the grid's `min-height` and the auto-scroll * to the current time, so it is a number rather than a CSS variable — the * scroll math has to read it. * @summary Pixel height of one hour row in the time grid. Follows `size` when unset. */ timeGridHourHeight?: number; /** Show a rich popover on hover/focus for days with events (month view). @default false */ eventPopover?: boolean; /** Show a mini month calendar sidebar (week/day/agenda views). @default false */ showMiniCalendar?: boolean; /** Position of the mini calendar sidebar. @default 'left' */ miniCalendarPosition?: 'left' | 'right'; /** * Show the view switcher in the header. Below `sm` its labels condense to * their short form; the full label stays the accessible name. @default true */ showViewSwitcher?: boolean; /** Enable animated transitions for navigation. @default true */ animated?: boolean; /** Enable swipe gestures for touch navigation. @default true */ swipeable?: boolean; /** Enable drag & drop to move events between dates. @default false */ draggable?: boolean; /** Fires when an event is moved via drag & drop. */ onEventMove?: (event: CalendarEvent, newStart: Date, newEnd: Date) => void; /** Enable resize handles on timed events in the time grid. @default false */ resizable?: boolean; /** Fires when an event is resized via drag handle. */ onEventResize?: (event: CalendarEvent, newEnd: Date) => void; /** Disable the entire calendar. @default false */ disabled?: boolean; /** Default children snippet for custom layout composition. */ children?: Snippet; /** Extra CSS classes on the root element. */ class?: string; /** Strip all default tv() classes. */ unstyled?: boolean; /** Per-slot class overrides. */ slotClasses?: Partial>; /** * Apply a named preset registered via ``. * Prefer this over `class` overrides when the requested look falls outside the * semantic intent palette — presets keep hover/active/dark-mode logic coherent * and make the custom look reusable across the project. */ preset?: string; } export interface CalendarHeaderProps extends Omit, 'children'> { /** Custom content replacing the default header layout. */ children?: Snippet; /** Show today button. @default true */ showToday?: boolean; /** Show view switcher buttons. @default true */ showViewSwitcher?: boolean; class?: string; unstyled?: boolean; slotClasses?: Partial>; } export { default as Calendar } from './Calendar.svelte'; export { default as CalendarAgendaView } from './CalendarAgendaView.svelte'; export { default as CalendarDay } from './CalendarDay.svelte'; export { default as CalendarDayView } from './CalendarDayView.svelte'; export { default as CalendarEventItem } from './CalendarEventItem.svelte'; export { default as CalendarEventList } from './CalendarEventList.svelte'; export { default as CalendarEventPopover } from './CalendarEventPopover.svelte'; export { default as CalendarGrid } from './CalendarGrid.svelte'; export { default as CalendarHeader } from './CalendarHeader.svelte'; export { default as CalendarLegend } from './CalendarLegend.svelte'; export { default as CalendarMiniMonth } from './CalendarMiniMonth.svelte'; export { default as CalendarMultiDayBar } from './CalendarMultiDayBar.svelte'; export { default as CalendarTimeEvent } from './CalendarTimeEvent.svelte'; export { default as CalendarTimeGrid } from './CalendarTimeGrid.svelte'; export { default as CalendarWeekdayHeader } from './CalendarWeekdayHeader.svelte'; export { default as CalendarWeekGrid } from './CalendarWeekGrid.svelte'; export { default as CalendarYearGrid } from './CalendarYearGrid.svelte'; export type { CalendarContext } from './calendar.context.js'; export { createSlotHelper } from './calendar.context.js'; export type { CalendarEvent, CalendarSelection, CalendarViewMode, DateCategory, DateRange, DayCellContext, EventDayInfo, EventItemContext, HeaderContext, MultiDayBarSegment, PositionedEvent, RecurrenceRule, TimeSlot } from './calendar.types.js'; export { type CalendarVariants, calendarVariants } from './calendar.variants.js';