This commit is contained in:
eewing
2026-02-17 14:10:16 -06:00
parent 2bca5834c5
commit cf73cd3b4c
11246 changed files with 1690552 additions and 0 deletions
@@ -0,0 +1,38 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import type { RangeCalendarCellProps } from "../types.js";
import { RangeCalendarCellState } from "../range-calendar.svelte.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
children,
child,
id = createId(uid),
ref = $bindable(null),
date,
month,
...restProps
}: RangeCalendarCellProps = $props();
const cellState = RangeCalendarCellState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
date: boxWith(() => date),
month: boxWith(() => month),
});
const mergedProps = $derived(mergeProps(restProps, cellState.props));
</script>
{#if child}
{@render child({ props: mergedProps, ...cellState.snippetProps })}
{:else}
<td {...mergedProps}>
{@render children?.(cellState.snippetProps)}
</td>
{/if}
@@ -0,0 +1,4 @@
import type { RangeCalendarCellProps } from "../types.js";
declare const RangeCalendarCell: import("svelte").Component<RangeCalendarCellProps, {}, "ref">;
type RangeCalendarCell = ReturnType<typeof RangeCalendarCell>;
export default RangeCalendarCell;
@@ -0,0 +1,38 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import type { RangeCalendarDayProps } from "../types.js";
import { RangeCalendarDayState } from "../range-calendar.svelte.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
children,
child,
id = createId(uid),
ref = $bindable(null),
...restProps
}: RangeCalendarDayProps = $props();
const dayState = RangeCalendarDayState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
});
const mergedProps = $derived(mergeProps(restProps, dayState.props));
</script>
{#if child}
{@render child({ props: mergedProps, ...dayState.snippetProps })}
{:else}
<div {...mergedProps}>
{#if children}
{@render children?.(dayState.snippetProps)}
{:else}
{dayState.cell.opts.date.current.day}
{/if}
</div>
{/if}
@@ -0,0 +1,4 @@
import type { RangeCalendarDayProps } from "../types.js";
declare const RangeCalendarDay: import("svelte").Component<RangeCalendarDayProps, {}, "ref">;
type RangeCalendarDay = ReturnType<typeof RangeCalendarDay>;
export default RangeCalendarDay;
@@ -0,0 +1,153 @@
<script lang="ts">
import { watch } from "runed";
import { boxWith, mergeProps } from "svelte-toolbelt";
import { type DateValue } from "@internationalized/date";
import type { RangeCalendarRootProps } from "../types.js";
import { RangeCalendarRootState } from "../range-calendar.svelte.js";
import { noop } from "../../../internal/noop.js";
import { createId } from "../../../internal/create-id.js";
import { getDefaultDate } from "../../../internal/date-time/utils.js";
import { resolveLocaleProp } from "../../utilities/config/prop-resolvers.js";
const uid = $props.id();
let {
children,
child,
id = createId(uid),
ref = $bindable(null),
value = $bindable(),
onValueChange = noop,
placeholder = $bindable(),
onPlaceholderChange = noop,
weekdayFormat = "narrow",
weekStartsOn,
pagedNavigation = false,
isDateDisabled = () => false,
isDateUnavailable = () => false,
fixedWeeks = false,
numberOfMonths = 1,
locale,
calendarLabel = "Event",
disabled = false,
readonly = false,
minValue = undefined,
maxValue = undefined,
preventDeselect = false,
disableDaysOutsideMonth = true,
minDays,
maxDays,
onStartValueChange = noop,
onEndValueChange = noop,
excludeDisabled = false,
monthFormat = "long",
yearFormat = "numeric",
...restProps
}: RangeCalendarRootProps = $props();
let startValue = $state<DateValue | undefined>(value?.start);
let endValue = $state<DateValue | undefined>(value?.end);
const defaultPlaceholder = getDefaultDate({
defaultValue: value?.start,
minValue,
maxValue,
});
function handleDefaultPlaceholder() {
if (placeholder !== undefined) return;
placeholder = defaultPlaceholder;
}
// SSR
handleDefaultPlaceholder();
watch.pre(
() => placeholder,
() => {
handleDefaultPlaceholder();
}
);
function handleDefaultValue() {
if (value !== undefined) return;
value = { start: undefined, end: undefined };
}
// SSR
handleDefaultValue();
watch.pre(
() => value,
() => {
handleDefaultValue();
}
);
const rootState = RangeCalendarRootState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
value: boxWith(
() => value!,
(v) => {
value = v;
onValueChange(v);
}
),
placeholder: boxWith(
() => placeholder!,
(v) => {
placeholder = v;
onPlaceholderChange(v);
}
),
disabled: boxWith(() => disabled),
readonly: boxWith(() => readonly),
preventDeselect: boxWith(() => preventDeselect),
minValue: boxWith(() => minValue),
maxValue: boxWith(() => maxValue),
isDateUnavailable: boxWith(() => isDateUnavailable),
isDateDisabled: boxWith(() => isDateDisabled),
pagedNavigation: boxWith(() => pagedNavigation),
weekStartsOn: boxWith(() => weekStartsOn),
weekdayFormat: boxWith(() => weekdayFormat),
numberOfMonths: boxWith(() => numberOfMonths),
locale: resolveLocaleProp(() => locale),
calendarLabel: boxWith(() => calendarLabel),
fixedWeeks: boxWith(() => fixedWeeks),
disableDaysOutsideMonth: boxWith(() => disableDaysOutsideMonth),
minDays: boxWith(() => minDays),
maxDays: boxWith(() => maxDays),
excludeDisabled: boxWith(() => excludeDisabled),
startValue: boxWith(
() => startValue,
(v) => {
startValue = v;
onStartValueChange(v);
}
),
endValue: boxWith(
() => endValue,
(v) => {
endValue = v;
onEndValueChange(v);
}
),
monthFormat: boxWith(() => monthFormat),
yearFormat: boxWith(() => yearFormat),
defaultPlaceholder,
});
const mergedProps = $derived(mergeProps(restProps, rootState.props));
</script>
{#if child}
{@render child({ props: mergedProps, ...rootState.snippetProps })}
{:else}
<div {...mergedProps}>
{@render children?.(rootState.snippetProps)}
</div>
{/if}
@@ -0,0 +1,4 @@
import type { RangeCalendarRootProps } from "../types.js";
declare const RangeCalendar: import("svelte").Component<RangeCalendarRootProps, {}, "value" | "placeholder" | "ref">;
type RangeCalendar = ReturnType<typeof RangeCalendar>;
export default RangeCalendar;
+15
View File
@@ -0,0 +1,15 @@
export { default as Root } from "./components/range-calendar.svelte";
export { default as Day } from "./components/range-calendar-day.svelte";
export { default as Cell } from "./components/range-calendar-cell.svelte";
export { default as Grid } from "../calendar/components/calendar-grid.svelte";
export { default as GridBody } from "../calendar/components/calendar-grid-body.svelte";
export { default as GridHead } from "../calendar/components/calendar-grid-head.svelte";
export { default as HeadCell } from "../calendar/components/calendar-head-cell.svelte";
export { default as GridRow } from "../calendar/components/calendar-grid-row.svelte";
export { default as Header } from "../calendar/components/calendar-header.svelte";
export { default as Heading } from "../calendar/components/calendar-heading.svelte";
export { default as NextButton } from "../calendar/components/calendar-next-button.svelte";
export { default as PrevButton } from "../calendar/components/calendar-prev-button.svelte";
export { default as MonthSelect } from "../calendar/components/calendar-month-select.svelte";
export { default as YearSelect } from "../calendar/components/calendar-year-select.svelte";
export type { RangeCalendarRootProps as RootProps, RangeCalendarPrevButtonProps as PrevButtonProps, RangeCalendarNextButtonProps as NextButtonProps, RangeCalendarHeadingProps as HeadingProps, RangeCalendarHeaderProps as HeaderProps, RangeCalendarGridProps as GridProps, RangeCalendarGridHeadProps as GridHeadProps, RangeCalendarHeadCellProps as HeadCellProps, RangeCalendarGridBodyProps as GridBodyProps, RangeCalendarCellProps as CellProps, RangeCalendarGridRowProps as GridRowProps, RangeCalendarDayProps as DayProps, RangeCalendarMonthSelectProps as MonthSelectProps, RangeCalendarYearSelectProps as YearSelectProps, } from "./types.js";
+14
View File
@@ -0,0 +1,14 @@
export { default as Root } from "./components/range-calendar.svelte";
export { default as Day } from "./components/range-calendar-day.svelte";
export { default as Cell } from "./components/range-calendar-cell.svelte";
export { default as Grid } from "../calendar/components/calendar-grid.svelte";
export { default as GridBody } from "../calendar/components/calendar-grid-body.svelte";
export { default as GridHead } from "../calendar/components/calendar-grid-head.svelte";
export { default as HeadCell } from "../calendar/components/calendar-head-cell.svelte";
export { default as GridRow } from "../calendar/components/calendar-grid-row.svelte";
export { default as Header } from "../calendar/components/calendar-header.svelte";
export { default as Heading } from "../calendar/components/calendar-heading.svelte";
export { default as NextButton } from "../calendar/components/calendar-next-button.svelte";
export { default as PrevButton } from "../calendar/components/calendar-prev-button.svelte";
export { default as MonthSelect } from "../calendar/components/calendar-month-select.svelte";
export { default as YearSelect } from "../calendar/components/calendar-year-select.svelte";
+1
View File
@@ -0,0 +1 @@
export * as RangeCalendar from "./exports.js";
+1
View File
@@ -0,0 +1 @@
export * as RangeCalendar from "./exports.js";
@@ -0,0 +1,232 @@
import { type DateValue } from "@internationalized/date";
import { DOMContext, type ReadableBoxedValues, type WritableBoxedValues } from "svelte-toolbelt";
import type { DateRange, Month } from "../../shared/index.js";
import type { BitsFocusEvent, BitsKeyboardEvent, BitsMouseEvent, RefAttachment, WithRefOpts } from "../../internal/types.js";
import { type Announcer } from "../../internal/date-time/announcer.js";
import { type Formatter } from "../../internal/date-time/formatter.js";
import { calendarAttrs } from "../../internal/date-time/calendar-helpers.svelte.js";
import type { WeekStartsOn } from "../../shared/date/types.js";
interface RangeCalendarRootStateOpts extends WithRefOpts, WritableBoxedValues<{
value: DateRange;
placeholder: DateValue;
startValue: DateValue | undefined;
endValue: DateValue | undefined;
}>, ReadableBoxedValues<{
preventDeselect: boolean;
minValue: DateValue | undefined;
maxValue: DateValue | undefined;
disabled: boolean;
pagedNavigation: boolean;
weekStartsOn: WeekStartsOn | undefined;
weekdayFormat: Intl.DateTimeFormatOptions["weekday"];
isDateDisabled: (date: DateValue) => boolean;
isDateUnavailable: (date: DateValue) => boolean;
fixedWeeks: boolean;
numberOfMonths: number;
locale: string;
calendarLabel: string;
readonly: boolean;
disableDaysOutsideMonth: boolean;
excludeDisabled: boolean;
minDays: number | undefined;
maxDays: number | undefined;
/**
* This is strictly used by the `DateRangePicker` component to close the popover when a date range
* is selected. It is not intended to be used by the user.
*/
onRangeSelect?: () => void;
monthFormat: Intl.DateTimeFormatOptions["month"] | ((month: number) => string);
yearFormat: Intl.DateTimeFormatOptions["year"] | ((year: number) => string);
}> {
defaultPlaceholder: DateValue;
}
export declare class RangeCalendarRootState {
#private;
static create(opts: RangeCalendarRootStateOpts): RangeCalendarRootState | import("../calendar/calendar.svelte.js").CalendarRootState;
readonly opts: RangeCalendarRootStateOpts;
readonly attachment: RefAttachment;
readonly visibleMonths: DateValue[];
months: Month<DateValue>[];
announcer: Announcer;
formatter: Formatter;
accessibleHeadingId: string;
focusedValue: DateValue | undefined;
lastPressedDateValue: DateValue | undefined;
domContext: DOMContext;
/**
* This derived state holds an array of localized day names for the current
* locale and calendar view. It dynamically syncs with the 'weekStartsOn' option,
* updating its content when the option changes. Using this state to render the
* calendar's days of the week is strongly recommended, as it guarantees that
* the days are correctly formatted for the current locale and calendar view.
*/
readonly weekdays: string[];
readonly isStartInvalid: boolean;
readonly isEndInvalid: boolean;
readonly isInvalid: boolean;
readonly isNextButtonDisabled: boolean;
readonly isPrevButtonDisabled: boolean;
readonly headingValue: string;
readonly fullCalendarLabel: string;
readonly highlightedRange: {
start: DateValue;
end: DateValue;
} | null;
readonly initialPlaceholderYear: number;
readonly defaultYears: number[];
constructor(opts: RangeCalendarRootStateOpts);
setMonths: (months: Month<DateValue>[]) => void;
isOutsideVisibleMonths(date: DateValue): boolean;
isDateDisabled(date: DateValue): boolean;
isDateUnavailable(date: DateValue): boolean;
isSelectionStart(date: DateValue): boolean;
isSelectionEnd(date: DateValue): boolean;
isSelected(date: DateValue): boolean;
shiftFocus(node: HTMLElement, add: number): void;
handleCellClick(e: Event, date: DateValue): void;
onkeydown(event: BitsKeyboardEvent): void;
/**
* Navigates to the next page of the calendar.
*/
nextPage(): void;
/**
* Navigates to the previous page of the calendar.
*/
prevPage(): void;
nextYear(): void;
prevYear(): void;
setYear(year: number): void;
setMonth(month: number): void;
getBitsAttr: (typeof calendarAttrs)["getAttr"];
readonly snippetProps: {
months: Month<DateValue>[];
weekdays: string[];
};
readonly props: {
readonly onkeydown: (event: BitsKeyboardEvent) => void;
readonly id: string;
readonly role: "application";
readonly "aria-label": string;
readonly "data-invalid": "" | undefined;
readonly "data-disabled": "" | undefined;
readonly "data-readonly": "" | undefined;
};
}
interface RangeCalendarCellStateOpts extends WithRefOpts, ReadableBoxedValues<{
date: DateValue;
month: DateValue;
}> {
}
export declare class RangeCalendarCellState {
static create(opts: RangeCalendarCellStateOpts): RangeCalendarCellState;
readonly opts: RangeCalendarCellStateOpts;
readonly root: RangeCalendarRootState;
readonly attachment: RefAttachment;
readonly cellDate: Date;
readonly isOutsideMonth: boolean;
readonly isDisabled: boolean;
readonly isUnavailable: boolean;
readonly isDateToday: boolean;
readonly isOutsideVisibleMonths: boolean;
readonly isFocusedDate: boolean;
readonly isSelectedDate: boolean;
readonly isSelectionStart: boolean;
readonly isRangeStart: boolean;
readonly isRangeEnd: boolean;
readonly isRangeMiddle: boolean;
readonly isSelectionMiddle: boolean;
readonly isSelectionEnd: boolean;
readonly isHighlighted: boolean;
readonly labelText: string;
constructor(opts: RangeCalendarCellStateOpts, root: RangeCalendarRootState);
readonly snippetProps: {
disabled: boolean;
unavailable: boolean;
selected: boolean;
};
readonly ariaDisabled: boolean;
readonly sharedDataAttrs: {
readonly "data-unavailable": "" | undefined;
readonly "data-today": "" | undefined;
readonly "data-outside-month": "" | undefined;
readonly "data-outside-visible-months": "" | undefined;
readonly "data-focused": "" | undefined;
readonly "data-selection-start": "" | undefined;
readonly "data-selection-end": "" | undefined;
readonly "data-range-start": "" | undefined;
readonly "data-range-end": "" | undefined;
readonly "data-range-middle": "" | undefined;
readonly "data-highlighted": "" | undefined;
readonly "data-selected": "" | undefined;
readonly "data-value": string;
readonly "data-type": string;
readonly "data-disabled": "" | undefined;
};
readonly props: {
readonly "data-unavailable": "" | undefined;
readonly "data-today": "" | undefined;
readonly "data-outside-month": "" | undefined;
readonly "data-outside-visible-months": "" | undefined;
readonly "data-focused": "" | undefined;
readonly "data-selection-start": "" | undefined;
readonly "data-selection-end": "" | undefined;
readonly "data-range-start": "" | undefined;
readonly "data-range-end": "" | undefined;
readonly "data-range-middle": "" | undefined;
readonly "data-highlighted": "" | undefined;
readonly "data-selected": "" | undefined;
readonly "data-value": string;
readonly "data-type": string;
readonly "data-disabled": "" | undefined;
readonly id: string;
readonly role: "gridcell";
readonly "aria-selected": "true" | "false";
readonly "aria-disabled": "true" | "false";
};
}
interface RangeCalendarDayStateOpts extends WithRefOpts {
}
export declare class RangeCalendarDayState {
#private;
static create(opts: RangeCalendarDayStateOpts): RangeCalendarDayState;
readonly opts: RangeCalendarDayStateOpts;
readonly cell: RangeCalendarCellState;
readonly attachment: RefAttachment;
constructor(opts: RangeCalendarDayStateOpts, cell: RangeCalendarCellState);
onclick(e: BitsMouseEvent): void;
onmouseenter(_: BitsMouseEvent): void;
onfocusin(_: BitsFocusEvent): void;
readonly snippetProps: {
disabled: boolean;
unavailable: boolean;
selected: boolean;
day: string;
};
readonly props: {
readonly tabindex: 0 | -1 | undefined;
readonly "data-bits-day": "";
readonly onclick: (e: BitsMouseEvent) => void;
readonly onmouseenter: (_: BitsMouseEvent) => void;
readonly onfocusin: (_: BitsFocusEvent) => void;
readonly "data-unavailable": "" | undefined;
readonly "data-today": "" | undefined;
readonly "data-outside-month": "" | undefined;
readonly "data-outside-visible-months": "" | undefined;
readonly "data-focused": "" | undefined;
readonly "data-selection-start": "" | undefined;
readonly "data-selection-end": "" | undefined;
readonly "data-range-start": "" | undefined;
readonly "data-range-end": "" | undefined;
readonly "data-range-middle": "" | undefined;
readonly "data-highlighted": "" | undefined;
readonly "data-selected": "" | undefined;
readonly "data-value": string;
readonly "data-type": string;
readonly "data-disabled": "" | undefined;
readonly id: string;
readonly role: "button";
readonly "aria-label": string;
readonly "aria-disabled": "true" | "false";
};
}
export {};
+672
View File
@@ -0,0 +1,672 @@
import { getLocalTimeZone, isSameDay, isSameMonth, isToday, } from "@internationalized/date";
import { attachRef, DOMContext, } from "svelte-toolbelt";
import { Context, watch } from "runed";
import { CalendarRootContext } from "../calendar/calendar.svelte.js";
import { useId } from "../../internal/use-id.js";
import { boolToStr, boolToEmptyStrOrUndef } from "../../internal/attrs.js";
import { getAnnouncer } from "../../internal/date-time/announcer.js";
import { createFormatter } from "../../internal/date-time/formatter.js";
import { calendarAttrs, createMonths, getCalendarElementProps, getCalendarHeadingValue, getDefaultYears, getIsNextButtonDisabled, getIsPrevButtonDisabled, getWeekdays, handleCalendarKeydown, handleCalendarNextPage, handleCalendarPrevPage, shiftCalendarFocus, useEnsureNonDisabledPlaceholder, useMonthViewOptionsSync, useMonthViewPlaceholderSync, } from "../../internal/date-time/calendar-helpers.svelte.js";
import { areAllDaysBetweenValid, getDateValueType, isAfter, isBefore, isBetweenInclusive, toDate, } from "../../internal/date-time/utils.js";
import { onMount, untrack } from "svelte";
const RangeCalendarCellContext = new Context("RangeCalendar.Cell");
export class RangeCalendarRootState {
static create(opts) {
return CalendarRootContext.set(new RangeCalendarRootState(opts));
}
opts;
attachment;
visibleMonths = $derived.by(() => this.months.map((month) => month.value));
months = $state([]);
announcer;
formatter;
accessibleHeadingId = useId();
focusedValue = $state(undefined);
lastPressedDateValue = undefined;
domContext;
/**
* This derived state holds an array of localized day names for the current
* locale and calendar view. It dynamically syncs with the 'weekStartsOn' option,
* updating its content when the option changes. Using this state to render the
* calendar's days of the week is strongly recommended, as it guarantees that
* the days are correctly formatted for the current locale and calendar view.
*/
weekdays = $derived.by(() => {
return getWeekdays({
months: this.months,
formatter: this.formatter,
weekdayFormat: this.opts.weekdayFormat.current,
});
});
isStartInvalid = $derived.by(() => {
if (!this.opts.startValue.current)
return false;
return (this.isDateUnavailable(this.opts.startValue.current) ||
this.isDateDisabled(this.opts.startValue.current));
});
isEndInvalid = $derived.by(() => {
if (!this.opts.endValue.current)
return false;
return (this.isDateUnavailable(this.opts.endValue.current) ||
this.isDateDisabled(this.opts.endValue.current));
});
isInvalid = $derived.by(() => {
if (this.isStartInvalid || this.isEndInvalid)
return true;
if (this.opts.endValue.current &&
this.opts.startValue.current &&
isBefore(this.opts.endValue.current, this.opts.startValue.current))
return true;
return false;
});
isNextButtonDisabled = $derived.by(() => {
return getIsNextButtonDisabled({
maxValue: this.opts.maxValue.current,
months: this.months,
disabled: this.opts.disabled.current,
});
});
isPrevButtonDisabled = $derived.by(() => {
return getIsPrevButtonDisabled({
minValue: this.opts.minValue.current,
months: this.months,
disabled: this.opts.disabled.current,
});
});
headingValue = $derived.by(() => {
this.opts.monthFormat.current;
this.opts.yearFormat.current;
return getCalendarHeadingValue({
months: this.months,
formatter: this.formatter,
locale: this.opts.locale.current,
});
});
fullCalendarLabel = $derived.by(() => `${this.opts.calendarLabel.current} ${this.headingValue}`);
highlightedRange = $derived.by(() => {
if (this.opts.startValue.current && this.opts.endValue.current)
return null;
if (!this.opts.startValue.current || !this.focusedValue)
return null;
const isStartBeforeFocused = isBefore(this.opts.startValue.current, this.focusedValue);
const start = isStartBeforeFocused ? this.opts.startValue.current : this.focusedValue;
const end = isStartBeforeFocused ? this.focusedValue : this.opts.startValue.current;
const range = { start, end };
if (isSameDay(start.add({ days: 1 }), end) || isSameDay(start, end)) {
return range;
}
const isValid = areAllDaysBetweenValid(start, end, this.isDateUnavailable, this.isDateDisabled);
if (isValid)
return range;
return null;
});
initialPlaceholderYear = $derived.by(() => untrack(() => this.opts.placeholder.current.year));
defaultYears = $derived.by(() => {
return getDefaultYears({
minValue: this.opts.minValue.current,
maxValue: this.opts.maxValue.current,
placeholderYear: this.initialPlaceholderYear,
});
});
constructor(opts) {
this.opts = opts;
this.attachment = attachRef(opts.ref);
this.domContext = new DOMContext(opts.ref);
this.announcer = getAnnouncer(null);
this.formatter = createFormatter({
initialLocale: this.opts.locale.current,
monthFormat: this.opts.monthFormat,
yearFormat: this.opts.yearFormat,
});
this.months = createMonths({
dateObj: this.opts.placeholder.current,
weekStartsOn: this.opts.weekStartsOn.current,
locale: this.opts.locale.current,
fixedWeeks: this.opts.fixedWeeks.current,
numberOfMonths: this.opts.numberOfMonths.current,
});
$effect.pre(() => {
if (this.formatter.getLocale() === this.opts.locale.current)
return;
this.formatter.setLocale(this.opts.locale.current);
});
onMount(() => {
this.announcer = getAnnouncer(this.domContext.getDocument());
});
/**
* Updates the displayed months based on changes in the placeholder values,
* which determines the month to show in the calendar.
*/
useMonthViewPlaceholderSync({
placeholder: this.opts.placeholder,
getVisibleMonths: () => this.visibleMonths,
weekStartsOn: this.opts.weekStartsOn,
locale: this.opts.locale,
fixedWeeks: this.opts.fixedWeeks,
numberOfMonths: this.opts.numberOfMonths,
setMonths: this.setMonths,
});
/**
* Updates the displayed months based on changes in the options values,
* which determines the month to show in the calendar.
*/
useMonthViewOptionsSync({
fixedWeeks: this.opts.fixedWeeks,
locale: this.opts.locale,
numberOfMonths: this.opts.numberOfMonths,
placeholder: this.opts.placeholder,
setMonths: this.setMonths,
weekStartsOn: this.opts.weekStartsOn,
});
/**
* Update the accessible heading's text content when the `fullCalendarLabel`
* changes.
*/
$effect(() => {
const node = this.domContext.getElementById(this.accessibleHeadingId);
if (!node)
return;
node.textContent = this.fullCalendarLabel;
});
/**
* Synchronize the start and end values with the `value` in case
* it is updated externally.
*/
watch(() => this.opts.value.current, (value) => {
if (value.start && value.end) {
this.opts.startValue.current = value.start;
this.opts.endValue.current = value.end;
}
else if (value.start) {
this.opts.startValue.current = value.start;
this.opts.endValue.current = undefined;
}
else if (value.start === undefined && value.end === undefined) {
this.opts.startValue.current = undefined;
this.opts.endValue.current = undefined;
}
});
/**
* Synchronize the placeholder value with the current start value
*/
watch(() => this.opts.value.current, (value) => {
const startValue = value.start;
if (startValue && this.opts.placeholder.current !== startValue) {
this.opts.placeholder.current = startValue;
}
});
/**
* Check for disabled dates in the selected range when excludeDisabled is enabled
*/
watch([
() => this.opts.startValue.current,
() => this.opts.endValue.current,
() => this.opts.excludeDisabled.current,
], ([startValue, endValue, excludeDisabled]) => {
if (!excludeDisabled || !startValue || !endValue)
return;
if (this.#hasDisabledDatesInRange(startValue, endValue)) {
this.#setStartValue(undefined);
this.#setEndValue(undefined);
this.#announceEmpty();
}
});
watch([() => this.opts.startValue.current, () => this.opts.endValue.current], ([startValue, endValue]) => {
if (this.opts.value.current &&
this.opts.value.current.start === startValue &&
this.opts.value.current.end === endValue) {
return;
}
if (startValue && endValue) {
this.#updateValue((prev) => {
if (prev.start === startValue && prev.end === endValue) {
return prev;
}
if (isBefore(endValue, startValue)) {
const start = startValue;
const end = endValue;
this.#setStartValue(end);
this.#setEndValue(start);
if (!this.#isRangeValid(endValue, startValue)) {
this.#setStartValue(startValue);
this.#setEndValue(undefined);
return { start: startValue, end: undefined };
}
return { start: endValue, end: startValue };
}
else {
if (!this.#isRangeValid(startValue, endValue)) {
this.#setStartValue(endValue);
this.#setEndValue(undefined);
return { start: endValue, end: undefined };
}
return {
start: startValue,
end: endValue,
};
}
});
}
else if (this.opts.value.current &&
this.opts.value.current.start &&
this.opts.value.current.end) {
this.opts.value.current.start = undefined;
this.opts.value.current.end = undefined;
}
});
this.shiftFocus = this.shiftFocus.bind(this);
this.handleCellClick = this.handleCellClick.bind(this);
this.onkeydown = this.onkeydown.bind(this);
this.nextPage = this.nextPage.bind(this);
this.prevPage = this.prevPage.bind(this);
this.nextYear = this.nextYear.bind(this);
this.prevYear = this.prevYear.bind(this);
this.setYear = this.setYear.bind(this);
this.setMonth = this.setMonth.bind(this);
this.isDateDisabled = this.isDateDisabled.bind(this);
this.isDateUnavailable = this.isDateUnavailable.bind(this);
this.isOutsideVisibleMonths = this.isOutsideVisibleMonths.bind(this);
this.isSelected = this.isSelected.bind(this);
useEnsureNonDisabledPlaceholder({
placeholder: opts.placeholder,
defaultPlaceholder: opts.defaultPlaceholder,
isDateDisabled: opts.isDateDisabled,
maxValue: opts.maxValue,
minValue: opts.minValue,
ref: opts.ref,
});
}
#updateValue(cb) {
const value = this.opts.value.current;
const newValue = cb(value);
this.opts.value.current = newValue;
if (newValue.start && newValue.end) {
this.opts.onRangeSelect?.current?.();
}
}
#setStartValue(value) {
this.opts.startValue.current = value;
// update the main value prop immediately for external consumers
this.#updateValue((prev) => ({
...prev,
start: value,
}));
}
#setEndValue(value) {
this.opts.endValue.current = value;
// update the main value prop immediately for external consumers
this.#updateValue((prev) => ({
...prev,
end: value,
}));
}
setMonths = (months) => {
this.months = months;
};
isOutsideVisibleMonths(date) {
return !this.visibleMonths.some((month) => isSameMonth(date, month));
}
isDateDisabled(date) {
if (this.opts.isDateDisabled.current(date) || this.opts.disabled.current)
return true;
const minValue = this.opts.minValue.current;
const maxValue = this.opts.maxValue.current;
if (minValue && isBefore(date, minValue))
return true;
if (maxValue && isAfter(date, maxValue))
return true;
return false;
}
isDateUnavailable(date) {
if (this.opts.isDateUnavailable.current(date))
return true;
return false;
}
isSelectionStart(date) {
if (!this.opts.startValue.current)
return false;
return isSameDay(date, this.opts.startValue.current);
}
isSelectionEnd(date) {
if (!this.opts.endValue.current)
return false;
return isSameDay(date, this.opts.endValue.current);
}
isSelected(date) {
if (this.opts.startValue.current && isSameDay(this.opts.startValue.current, date))
return true;
if (this.opts.endValue.current && isSameDay(this.opts.endValue.current, date))
return true;
if (this.opts.startValue.current && this.opts.endValue.current) {
return isBetweenInclusive(date, this.opts.startValue.current, this.opts.endValue.current);
}
return false;
}
#isRangeValid(start, end) {
// ensure we always use the correct order for calculation
const orderedStart = isBefore(end, start) ? end : start;
const orderedEnd = isBefore(end, start) ? start : end;
const startDate = orderedStart.toDate(getLocalTimeZone());
const endDate = orderedEnd.toDate(getLocalTimeZone());
const timeDifference = endDate.getTime() - startDate.getTime();
const daysDifference = Math.floor(timeDifference / (1000 * 60 * 60 * 24));
const daysInRange = daysDifference + 1; // +1 to include both start and end days
if (this.opts.minDays.current && daysInRange < this.opts.minDays.current)
return false;
if (this.opts.maxDays.current && daysInRange > this.opts.maxDays.current)
return false;
// check for disabled dates in range if excludeDisabled is enabled
if (this.opts.excludeDisabled.current &&
this.#hasDisabledDatesInRange(orderedStart, orderedEnd)) {
return false;
}
return true;
}
shiftFocus(node, add) {
return shiftCalendarFocus({
node,
add,
placeholder: this.opts.placeholder,
calendarNode: this.opts.ref.current,
isPrevButtonDisabled: this.isPrevButtonDisabled,
isNextButtonDisabled: this.isNextButtonDisabled,
months: this.months,
numberOfMonths: this.opts.numberOfMonths.current,
});
}
#announceEmpty() {
this.announcer.announce("Selected date is now empty.", "polite");
}
#announceSelectedDate(date) {
this.announcer.announce(`Selected Date: ${this.formatter.selectedDate(date, false)}`, "polite");
}
#announceSelectedRange(start, end) {
this.announcer.announce(`Selected Dates: ${this.formatter.selectedDate(start, false)} to ${this.formatter.selectedDate(end, false)}`, "polite");
}
handleCellClick(e, date) {
if (this.isDateDisabled(date) || this.isDateUnavailable(date))
return;
const prevLastPressedDate = this.lastPressedDateValue;
this.lastPressedDateValue = date;
if (this.opts.startValue.current && this.highlightedRange === null) {
if (isSameDay(this.opts.startValue.current, date) &&
!this.opts.preventDeselect.current &&
!this.opts.endValue.current) {
this.#setStartValue(undefined);
this.opts.placeholder.current = date;
this.#announceEmpty();
return;
}
else if (!this.opts.endValue.current) {
e.preventDefault();
if (prevLastPressedDate && isSameDay(prevLastPressedDate, date)) {
this.#setStartValue(date);
this.#announceSelectedDate(date);
}
}
}
if (this.opts.startValue.current &&
this.opts.endValue.current &&
isSameDay(this.opts.endValue.current, date) &&
!this.opts.preventDeselect.current) {
this.#setStartValue(undefined);
this.#setEndValue(undefined);
this.opts.placeholder.current = date;
this.#announceEmpty();
return;
}
if (!this.opts.startValue.current) {
this.#announceSelectedDate(date);
this.#setStartValue(date);
}
else if (!this.opts.endValue.current) {
// determine the start and end dates for validation
const startDate = this.opts.startValue.current;
const endDate = date;
const orderedStart = isBefore(endDate, startDate) ? endDate : startDate;
const orderedEnd = isBefore(endDate, startDate) ? startDate : endDate;
// check if the range violates constraints
if (!this.#isRangeValid(orderedStart, orderedEnd)) {
// reset to just the clicked date
this.#setStartValue(date);
this.#setEndValue(undefined);
this.#announceSelectedDate(date);
}
else {
// ensure start and end are properly ordered
if (isBefore(endDate, startDate)) {
// backward selection - reorder the values
this.#setStartValue(endDate);
this.#setEndValue(startDate);
this.#announceSelectedRange(endDate, startDate);
}
else {
// forward selection - keep original order
this.#setEndValue(date);
this.#announceSelectedRange(this.opts.startValue.current, date);
}
}
}
else if (this.opts.endValue.current && this.opts.startValue.current) {
this.#setEndValue(undefined);
this.#announceSelectedDate(date);
this.#setStartValue(date);
}
}
onkeydown(event) {
return handleCalendarKeydown({
event,
handleCellClick: this.handleCellClick,
placeholderValue: this.opts.placeholder.current,
shiftFocus: this.shiftFocus,
});
}
/**
* Navigates to the next page of the calendar.
*/
nextPage() {
handleCalendarNextPage({
fixedWeeks: this.opts.fixedWeeks.current,
locale: this.opts.locale.current,
numberOfMonths: this.opts.numberOfMonths.current,
pagedNavigation: this.opts.pagedNavigation.current,
setMonths: this.setMonths,
setPlaceholder: (date) => (this.opts.placeholder.current = date),
weekStartsOn: this.opts.weekStartsOn.current,
months: this.months,
});
}
/**
* Navigates to the previous page of the calendar.
*/
prevPage() {
handleCalendarPrevPage({
fixedWeeks: this.opts.fixedWeeks.current,
locale: this.opts.locale.current,
numberOfMonths: this.opts.numberOfMonths.current,
pagedNavigation: this.opts.pagedNavigation.current,
setMonths: this.setMonths,
setPlaceholder: (date) => (this.opts.placeholder.current = date),
weekStartsOn: this.opts.weekStartsOn.current,
months: this.months,
});
}
nextYear() {
this.opts.placeholder.current = this.opts.placeholder.current.add({ years: 1 });
}
prevYear() {
this.opts.placeholder.current = this.opts.placeholder.current.subtract({ years: 1 });
}
setYear(year) {
this.opts.placeholder.current = this.opts.placeholder.current.set({ year });
}
setMonth(month) {
this.opts.placeholder.current = this.opts.placeholder.current.set({ month });
}
getBitsAttr = (part) => {
return calendarAttrs.getAttr(part, "range-calendar");
};
snippetProps = $derived.by(() => ({
months: this.months,
weekdays: this.weekdays,
}));
props = $derived.by(() => ({
...getCalendarElementProps({
fullCalendarLabel: this.fullCalendarLabel,
id: this.opts.id.current,
isInvalid: this.isInvalid,
disabled: this.opts.disabled.current,
readonly: this.opts.readonly.current,
}),
[this.getBitsAttr("root")]: "",
//
onkeydown: this.onkeydown,
...this.attachment,
}));
#hasDisabledDatesInRange(start, end) {
for (let date = start; isBefore(date, end) || isSameDay(date, end); date = date.add({ days: 1 })) {
if (this.isDateDisabled(date))
return true;
}
return false;
}
}
export class RangeCalendarCellState {
static create(opts) {
return RangeCalendarCellContext.set(new RangeCalendarCellState(opts, CalendarRootContext.get()));
}
opts;
root;
attachment;
cellDate = $derived.by(() => toDate(this.opts.date.current));
isOutsideMonth = $derived.by(() => !isSameMonth(this.opts.date.current, this.opts.month.current));
isDisabled = $derived.by(() => this.root.isDateDisabled(this.opts.date.current) ||
(this.isOutsideMonth && this.root.opts.disableDaysOutsideMonth.current));
isUnavailable = $derived.by(() => this.root.opts.isDateUnavailable.current(this.opts.date.current));
isDateToday = $derived.by(() => isToday(this.opts.date.current, getLocalTimeZone()));
isOutsideVisibleMonths = $derived.by(() => this.root.isOutsideVisibleMonths(this.opts.date.current));
isFocusedDate = $derived.by(() => isSameDay(this.opts.date.current, this.root.opts.placeholder.current));
isSelectedDate = $derived.by(() => this.root.isSelected(this.opts.date.current));
isSelectionStart = $derived.by(() => this.root.isSelectionStart(this.opts.date.current));
isRangeStart = $derived.by(() => this.root.isSelectionStart(this.opts.date.current));
isRangeEnd = $derived.by(() => {
if (!this.root.opts.endValue.current)
return this.root.isSelectionStart(this.opts.date.current);
return this.root.isSelectionEnd(this.opts.date.current);
});
isRangeMiddle = $derived.by(() => this.isSelectionMiddle);
isSelectionMiddle = $derived.by(() => {
return this.isSelectedDate && !this.isSelectionStart && !this.isSelectionEnd;
});
isSelectionEnd = $derived.by(() => this.root.isSelectionEnd(this.opts.date.current));
isHighlighted = $derived.by(() => this.root.highlightedRange
? isBetweenInclusive(this.opts.date.current, this.root.highlightedRange.start, this.root.highlightedRange.end)
: false);
labelText = $derived.by(() => this.root.formatter.custom(this.cellDate, {
weekday: "long",
month: "long",
day: "numeric",
year: "numeric",
}));
constructor(opts, root) {
this.opts = opts;
this.root = root;
this.attachment = attachRef(opts.ref);
}
snippetProps = $derived.by(() => ({
disabled: this.isDisabled,
unavailable: this.isUnavailable,
selected: this.isSelectedDate,
}));
ariaDisabled = $derived.by(() => {
return (this.isDisabled ||
(this.isOutsideMonth && this.root.opts.disableDaysOutsideMonth.current) ||
this.isUnavailable);
});
sharedDataAttrs = $derived.by(() => ({
"data-unavailable": boolToEmptyStrOrUndef(this.isUnavailable),
"data-today": this.isDateToday ? "" : undefined,
"data-outside-month": this.isOutsideMonth ? "" : undefined,
"data-outside-visible-months": this.isOutsideVisibleMonths ? "" : undefined,
"data-focused": this.isFocusedDate ? "" : undefined,
"data-selection-start": this.isSelectionStart ? "" : undefined,
"data-selection-end": this.isSelectionEnd ? "" : undefined,
"data-range-start": this.isRangeStart ? "" : undefined,
"data-range-end": this.isRangeEnd ? "" : undefined,
"data-range-middle": this.isRangeMiddle ? "" : undefined,
"data-highlighted": this.isHighlighted ? "" : undefined,
"data-selected": boolToEmptyStrOrUndef(this.isSelectedDate),
"data-value": this.opts.date.current.toString(),
"data-type": getDateValueType(this.opts.date.current),
"data-disabled": boolToEmptyStrOrUndef(this.isDisabled ||
(this.isOutsideMonth && this.root.opts.disableDaysOutsideMonth.current)),
}));
props = $derived.by(() => ({
id: this.opts.id.current,
role: "gridcell",
"aria-selected": boolToStr(this.isSelectedDate),
"aria-disabled": boolToStr(this.ariaDisabled),
...this.sharedDataAttrs,
[this.root.getBitsAttr("cell")]: "",
...this.attachment,
}));
}
export class RangeCalendarDayState {
static create(opts) {
return new RangeCalendarDayState(opts, RangeCalendarCellContext.get());
}
opts;
cell;
attachment;
constructor(opts, cell) {
this.opts = opts;
this.cell = cell;
this.attachment = attachRef(opts.ref);
this.onclick = this.onclick.bind(this);
this.onmouseenter = this.onmouseenter.bind(this);
this.onfocusin = this.onfocusin.bind(this);
}
#tabindex = $derived.by(() => (this.cell.isOutsideMonth && this.cell.root.opts.disableDaysOutsideMonth.current) ||
this.cell.isDisabled
? undefined
: this.cell.isFocusedDate
? 0
: -1);
onclick(e) {
if (this.cell.isDisabled)
return;
this.cell.root.handleCellClick(e, this.cell.opts.date.current);
}
onmouseenter(_) {
if (this.cell.isDisabled)
return;
this.cell.root.focusedValue = this.cell.opts.date.current;
}
onfocusin(_) {
if (this.cell.isDisabled)
return;
this.cell.root.focusedValue = this.cell.opts.date.current;
}
snippetProps = $derived.by(() => ({
disabled: this.cell.isDisabled,
unavailable: this.cell.isUnavailable,
selected: this.cell.isSelectedDate,
day: `${this.cell.opts.date.current.day}`,
}));
props = $derived.by(() => ({
id: this.opts.id.current,
role: "button",
"aria-label": this.cell.labelText,
"aria-disabled": boolToStr(this.cell.ariaDisabled),
...this.cell.sharedDataAttrs,
tabindex: this.#tabindex,
[this.cell.root.getBitsAttr("day")]: "",
// Shared logic for range calendar and calendar
"data-bits-day": "",
//
onclick: this.onclick,
onmouseenter: this.onmouseenter,
onfocusin: this.onfocusin,
...this.attachment,
}));
}
+212
View File
@@ -0,0 +1,212 @@
import type { DateValue } from "@internationalized/date";
import type { OnChangeFn, WithChild, Without } from "../../internal/types.js";
import type { DateMatcher, DateRange, Month } from "../../shared/index.js";
import type { BitsPrimitiveDivAttributes } from "../../shared/attributes.js";
export type RangeCalendarRootSnippetProps = {
months: Month<DateValue>[];
weekdays: string[];
};
export type RangeCalendarRootPropsWithoutHTML = WithChild<{
/**
* The value of the selected date range.
* @bindable
*/
value?: DateRange;
/**
* A callback function called when the value changes.
*/
onValueChange?: OnChangeFn<DateRange>;
/**
* The placeholder date, used to control the view of the
* calendar when no value is present.
*
* @default the current date
*/
placeholder?: DateValue;
/**
* A callback function called when the placeholder value
* changes.
*/
onPlaceholderChange?: OnChangeFn<DateValue>;
/**
* The minimum number of days that can be selected in a range.
*
* @default undefined
*/
minDays?: number;
/**
* The maximum number of days that can be selected in a range.
*
* @default undefined
*/
maxDays?: number;
/**
* Whether or not users can deselect a date once selected
* without selecting another date.
*
* @default false
*/
preventDeselect?: boolean;
/**
* The minimum date that can be selected in the calendar.
*/
minValue?: DateValue;
/**
* The maximum date that can be selected in the calendar.
*/
maxValue?: DateValue;
/**
* Whether or not the calendar is disabled.
*
* @default false
*/
disabled?: boolean;
/**
* Applicable only when `numberOfMonths` is greater than 1.
*
* Controls whether to use paged navigation for the next and previous buttons in the
* date picker. With paged navigation set to `true`, clicking the next/prev buttons
* changes all months in view. When set to `false`, it shifts the view by a single month.
*
* For example, with `pagedNavigation` set to `true` and 2 months displayed (January and
* February), clicking the next button changes the view to March and April. If `pagedNavigation`
* is `false`, the view shifts to February and March.
*
* @default false
*/
pagedNavigation?: boolean;
/**
* The day of the week to start the calendar on, which must
* be a number between 0 and 6, where 0 is Sunday and 6 is
* Saturday.
*
* @default 0 (Sunday)
*/
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
/**
* How the string representation of the weekdays provided via the `weekdays` state store
* should be formatted.
*
* ```md
* - "long": "Sunday", "Monday", "Tuesday", etc.
* - "short": "Sun", "Mon", "Tue", etc.
* - "narrow": "S", "M", "T", etc.
*```
*
* @default "narrow"
*
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#weekday
*/
weekdayFormat?: Intl.DateTimeFormatOptions["weekday"];
/**
* A function that receives a date and returns `true` or `false` to indicate whether
* the date is disabled.
*
* @remarks
* Disabled dates cannot be focused or selected. Additionally, they are tagged
* with a data attribute to enable custom styling.
*
* `[data-disabled]` - applied to disabled dates
*
*/
isDateDisabled?: DateMatcher;
/**
* Dates matching the provided matchers are marked as "unavailable." Unlike disabled dates,
* users can still focus and select unavailable dates. However, selecting an unavailable date
* renders the date picker as invalid.
*
* For example, in a calendar for booking appointments, you might mark already booked dates as
* unavailable. These dates could become available again before the appointment date, allowing
* users to select them to learn more about the appointment.
*
* `[data-unavailable]` - applied to unavailable dates
*
*/
isDateUnavailable?: DateMatcher;
/**
* Display 6 weeks per month, regardless the month's number of weeks.
* This is useful for displaying a consistent calendar, where the size
* of the calendar doesn't change month to month.
*
* To display 6 weeks per month, you will need to render out the previous
* and next month's dates in the calendar as well.
*
* @default false
*/
fixedWeeks?: boolean;
/**
* Determines the number of months to display on the calendar simultaneously.
* For navigation between months, refer to the `pagedNavigation` prop.
*
* @default 1
*/
numberOfMonths?: number;
/**
* This label is exclusively used for accessibility, remaining hidden from the page.
* It's read by screen readers when the calendar is opened. The current month and year
* are automatically appended to the label, so you only need to provide the base label.
*
* For instance:
* - 'Date of birth' will be read as 'Date of birth, January 2021' if the current month is January 2021.
* - 'Appointment date' will be read as 'Appointment date, January 2021' if the current month is January 2021.
* - 'Booking date' will be read as 'Booking date, January 2021' if the current month is January 2021.
*/
calendarLabel?: string;
/**
* The default locale setting.
*
* @default 'en'
*/
locale?: string;
/**
* Whether the calendar is readonly. When true, the user will be able
* to focus and navigate the calendar, but will not be able to select
* dates. @see disabled for a similar prop that prevents focusing
* and selecting dates.
*
* @default false
*/
readonly?: boolean;
/**
* Whether to disable the selection of days outside the current month. By default,
* days outside the current month are rendered to fill the calendar grid, but they
* are not selectable. Setting this prop to `true` will disable this behavior.
*
* @default false
*/
disableDaysOutsideMonth?: boolean;
/**
* Whether to automatically reset the range if any date within the selected range
* becomes disabled. When true, the entire range will be cleared if a disabled
* date is found between the start and end dates.
*
* @default false
*/
excludeDisabled?: boolean;
/**
* A callback function called when the start value changes. This doesn't necessarily mean
* the `value` has updated and should be used to apply cosmetic changes to the calendar when
* only part of the value is changed/completed.
*/
onStartValueChange?: OnChangeFn<DateValue | undefined>;
/**
* A callback function called when the end value changes. This doesn't necessarily mean
* the `value` has updated and should be used to apply cosmetic changes to the calendar when
* only part of the value is changed/completed.
*/
onEndValueChange?: OnChangeFn<DateValue | undefined>;
/**
* The format of the month names in the calendar.
*
* @default "long"
*/
monthFormat?: Intl.DateTimeFormatOptions["month"] | ((month: number) => string);
/**
* The format of the year names in the calendar.
*
* @default "numeric"
*/
yearFormat?: Intl.DateTimeFormatOptions["year"] | ((year: number) => string);
}, RangeCalendarRootSnippetProps>;
export type RangeCalendarRootProps = RangeCalendarRootPropsWithoutHTML & Without<BitsPrimitiveDivAttributes, RangeCalendarRootPropsWithoutHTML>;
export type { CalendarPrevButtonProps as RangeCalendarPrevButtonProps, CalendarPrevButtonPropsWithoutHTML as RangeCalendarPrevButtonPropsWithoutHTML, CalendarNextButtonProps as RangeCalendarNextButtonProps, CalendarNextButtonPropsWithoutHTML as RangeCalendarNextButtonPropsWithoutHTML, CalendarHeadingProps as RangeCalendarHeadingProps, CalendarHeadingPropsWithoutHTML as RangeCalendarHeadingPropsWithoutHTML, CalendarGridProps as RangeCalendarGridProps, CalendarGridPropsWithoutHTML as RangeCalendarGridPropsWithoutHTML, CalendarCellProps as RangeCalendarCellProps, CalendarCellPropsWithoutHTML as RangeCalendarCellPropsWithoutHTML, CalendarDayProps as RangeCalendarDayProps, CalendarDayPropsWithoutHTML as RangeCalendarDayPropsWithoutHTML, CalendarGridBodyProps as RangeCalendarGridBodyProps, CalendarGridBodyPropsWithoutHTML as RangeCalendarGridBodyPropsWithoutHTML, CalendarGridHeadProps as RangeCalendarGridHeadProps, CalendarGridHeadPropsWithoutHTML as RangeCalendarGridHeadPropsWithoutHTML, CalendarGridRowProps as RangeCalendarGridRowProps, CalendarGridRowPropsWithoutHTML as RangeCalendarGridRowPropsWithoutHTML, CalendarHeadCellProps as RangeCalendarHeadCellProps, CalendarHeadCellPropsWithoutHTML as RangeCalendarHeadCellPropsWithoutHTML, CalendarHeaderProps as RangeCalendarHeaderProps, CalendarHeaderPropsWithoutHTML as RangeCalendarHeaderPropsWithoutHTML, CalendarMonthSelectProps as RangeCalendarMonthSelectProps, CalendarMonthSelectPropsWithoutHTML as RangeCalendarMonthSelectPropsWithoutHTML, CalendarYearSelectProps as RangeCalendarYearSelectProps, CalendarYearSelectPropsWithoutHTML as RangeCalendarYearSelectPropsWithoutHTML, } from "../calendar/types.js";
+1
View File
@@ -0,0 +1 @@
export {};