import { Provider, context, type ProviderOpts } from '../../provider'; import { createAttrs, registerContract } from '$uix/morfo'; import { type Active, type ActiveProps, type StateProps } from '$libs/reactive'; import type { OnChangeFn, Direction } from '../../types'; import { Soma } from '../../core/soma.svelte'; import type { DateValue, DateMatcher, DateOnInvalid, DateValidator, EditableSegmentPart, Granularity, HourCycle, WeekStartsOn } from '../../external/dates'; // ── Contract ───────────────────────────────────────────────────────────────── import { datePickerMorfo } from '../../../morfo/components/date-picker'; const attrs = createAttrs(datePickerMorfo); registerContract(datePickerMorfo); // ── Provider (context — shared state for DateField + Popover + Calendar) ──── interface DatePickerOpts extends ProviderOpts, StateProps<{ value: DateValue | undefined; placeholder: DateValue; open: boolean; }>, ActiveProps<{ // Validation validate: DateValidator | undefined; onInvalid: DateOnInvalid | undefined; minValue: DateValue | undefined; maxValue: DateValue | undefined; isDateDisabled: DateMatcher | undefined; isDateUnavailable: DateMatcher | undefined; // State flags disabled: boolean; readonly: boolean; readonlySegments: EditableSegmentPart[]; required: boolean; closeOnDateSelect: boolean; // Format / i18n granularity: Granularity | undefined; hideTimeZone: boolean; hourCycle: HourCycle | undefined; locale: string | undefined; dir: Direction; // Calendar options pagedNavigation: boolean; weekStartsOn: WeekStartsOn | undefined; weekdayFormat: Intl.DateTimeFormatOptions['weekday']; fixedWeeks: boolean; numberOfMonths: number; calendarLabel: string | undefined; disableDaysOutsideMonth: boolean; preventDeselect: boolean; initialFocus: boolean; monthFormat: Intl.DateTimeFormatOptions['month'] | ((month: number) => string); yearFormat: Intl.DateTimeFormatOptions['year'] | ((year: number) => string); showWeekNumbers: boolean; weekNumberFormat: 'iso' | 'simple'; isDateHoliday: DateMatcher | undefined; announceNavigation: boolean; // ARIA errorMessageId: string | undefined; // Callbacks onOpenChangeComplete: OnChangeFn; }> {} /** * Shared-state coordinator for DatePicker. The wrapper also creates a * `PopoverProvider` and a `DateFieldProvider` pointing at the same reactive * sources; `DatePicker.Calendar` creates a `CalendarProvider` likewise. This * class exists so every sub-part can reach the same value / placeholder / * open flags through a single context. */ export class DatePickerProvider extends Provider { static readonly ctx = context('DatePicker'); static get(): DatePickerProvider | undefined { return this.ctx.getOr(undefined) as DatePickerProvider | undefined; } static require(): DatePickerProvider { return this.ctx.get(); } static create(opts: DatePickerOpts) { return new DatePickerProvider(opts); } readonly soma = Soma.get(); private constructor(opts: DatePickerOpts) { super(opts, 'DatePicker', 'provider', attrs.provider, DatePickerProvider.ctx); } /** Close the popover if `closeOnDateSelect` is `true`. Called by the Calendar. */ handleDateSelect(): void { if (this.opts.closeOnDateSelect.current) { this.opts.open.current = false; } } } export { attrs as datePickerAttrs };