/** * createCalendar - the HEADLESS core behind , in the same spirit as * `createListbox` / `createSvGrid`: a runes-based state machine (view page + * drill level, roving focus, every Smart `selectionMode`, range hover preview, * full keyboard) with **prop-getters** you spread onto YOUR OWN markup. No * styles, no DOM assumptions. It reuses the framework-free date engine in * ./datetime for all the actual date math / selection / restriction, so this * file only owns the reactive component-level state + interaction wiring. * * ```svelte * *
* {#each cal.panels[0].matrix as week} * {#each week as cell} * * {/each} * {/each} *
* ``` * * The styled is one renderer over this core; it keeps the render-only * concerns (WAAPI view-change animation, mouse-wheel navigation, DOM refs) itself. */ import { type DateLike, type MonthMatrixCell } from './datetime/date-core'; import { type SelectionMode } from './datetime/date-selection'; import { type RestrictOptions } from './datetime/date-restrict'; import { type RecurrenceRule } from './recurrence'; export type DisplayMode = 'month' | 'year' | 'decade'; export type CalendarNameFormat = 'narrow' | 'short' | 'long'; /** Selected value(s). Single Date for one/zeroOrOne, array for multi modes. */ export type CalendarValue = Date | number | string | ReadonlyArray | null; /** A one-click shortcut shown in the presets rail. `value` is a single date * (or an [start, end] range for range mode); pass a function for "today"- * relative shortcuts that resolve at click time. */ export type CalendarPreset = { label: string; value: Date | number | string | readonly [Date | number | string, Date | number | string] | (() => Date | number | string | readonly [Date | number | string, Date | number | string]); }; /** How the calendar animates a navigation / drill (a render concern the styled * component acts on; the core only reports the direction). */ export type CalendarAnimation = 'slide' | 'fade' | 'none'; /** Why the visible view last changed - drives the styled component's animation. */ export type CalendarNavDir = 'next' | 'prev' | 'drillDown' | 'drillUp' | 'fade'; /** Per-day interaction/visual state (booleans the renderer maps to classes). */ export type CalendarDayState = { disabled: boolean; selected: boolean; important: boolean; today: boolean; outside: boolean; focused: boolean; preview: boolean; /** Matches one of the `recurrence` rules (repeat pattern). */ recurring: boolean; }; /** Reactive inputs are passed as getters so the core tracks live prop changes; * callbacks are plain closures. */ export type CalendarConfig = { value: () => CalendarValue; onChange?: (dates: Date[]) => void; onNavigate?: (viewDate: Date, displayMode: DisplayMode) => void; selectionMode?: () => SelectionMode; min?: () => DateLike | null; max?: () => DateLike | null; restrictedDates?: () => RestrictOptions['restrictedDates']; importantDates?: () => ReadonlyArray | ((d: Date) => boolean) | null; firstDayOfWeek?: () => number; weeks?: () => number; months?: () => number; displayMode?: () => DisplayMode; disabled?: () => boolean; readonly?: () => boolean; locale?: () => string; dayNameFormat?: () => CalendarNameFormat; monthNameFormat?: () => CalendarNameFormat; dateTooltip?: () => ((date: Date) => string | null | undefined) | undefined; /** Repeat pattern(s) - matching days get `dayState().recurring = true`. */ recurrence?: () => RecurrenceRule | ReadonlyArray | null; }; /** Normalize a calendar value to an ordered day-midnight list (pure). */ export declare function normalizeCalendarValue(v: CalendarValue): Date[]; export declare function createCalendar(config: CalendarConfig): { readonly view: Date; readonly mode: DisplayMode; readonly title: string; readonly panels: { month: Date; title: string; matrix: MonthMatrixCell[][]; }[]; readonly weekdayHeaders: { label: string; weekday: number; }[]; readonly yearCells: { index: number; label: string; current: boolean; selected: boolean; }[]; readonly decadeCells: { year: number; outside: boolean; current: boolean; selected: boolean; }[]; readonly selectedDates: Date[]; readonly previewRange: Date[] | null; readonly focusDate: Date; readonly hoverDate: Date | null; readonly canClear: boolean; readonly isInteractive: boolean; /** Increments on every view/drill change; the styled component watches it to * replay its WAAPI animation on the updated DOM. */ readonly navToken: number; /** Why the view last changed (read untracked from the animation effect). */ readonly navDir: CalendarNavDir; dayState: (d: Date, panelMonth: Date) => CalendarDayState; inPreview: (d: Date) => boolean; formatFullDate: (d: Date) => string; formatMonthTitle: (d: Date) => string; pickDay: (d: Date, e?: { ctrlKey?: boolean; metaKey?: boolean; shiftKey?: boolean; }) => void; step: (dir: -1 | 1) => void; drillUp: () => void; pickMonth: (monthIndex: number) => void; pickYear: (year: number) => void; goToday: () => void; applyPreset: (p: CalendarPreset) => void; clearSelection: () => void; setHover: (d: Date | null) => Date | null; onKeydown: (e: KeyboardEvent) => void; /** Previous / Next navigation button. */ navProps(dir: -1 | 1): { type: "button"; disabled: boolean; 'aria-label': string; onclick: () => void; }; /** The centered title button that drills up (month -> year -> decade). */ titleProps(): { type: "button"; disabled: boolean; 'aria-live': "polite"; onclick: () => void; }; /** The grid container: keyboard + ARIA. Works for month, year and decade * modes (the handler dispatches on the current mode). */ gridProps(): { role: "grid"; 'aria-label': string; tabindex: number; onkeydown: (e: KeyboardEvent) => void; }; /** A single day button in the month grid. Combine with `dayState` for the * visual class flags. */ dayProps(cell: MonthMatrixCell, panelMonth: Date): { type: "button"; role: "gridcell"; 'aria-selected': boolean; 'aria-disabled': boolean; 'aria-current': "date" | undefined; 'aria-label': string; title: string | undefined; tabindex: number; disabled: boolean; onclick: (e: MouseEvent) => void; onpointerenter: () => Date | null; onpointerleave: () => Date | null; }; /** A month cell in year mode. */ monthCellProps(monthIndex: number): { type: "button"; disabled: boolean; onclick: () => void; }; /** A year cell in decade mode. */ yearCellProps(year: number): { type: "button"; disabled: boolean; onclick: () => void; }; /** A preset shortcut button. */ presetProps(preset: CalendarPreset): { type: "button"; disabled: boolean; onclick: () => void; }; }; export type Calendar = ReturnType;