feat: Phase 1 — extract the client (web SPA + desktop) to dtoro/oikos-web
Problem: the hexagonal refactor churns the backend tree for nine more phases; the UI delivery stack (web/ SPA, cmd/desktop Wails wrapper, compose/web image) must move to its own repo first so doc/layout rewrites land once on a backend-only tree. Change: - New repo git.hubris.network/dtoro/oikos-web (v0.33.0): web/, desktop/ (updateURL repointed to oikos-web releases), compose/, own CI (web + desktop jobs), own deploy script (CI-green gate, TOCTOU guard, version-tagged images, prune-to-3), own webhook receiver on :9798 + launchd unit, own compose project publishing the same 8091:80. - Cutover executed on mac-mini in order: oikos stack's web service stopped+removed, oikos-web project brought up on 8091; outer Caddy untouched (targets the published port) — serving + Authentik flow + /wails 404 quirk verified post-cutover. - Stripped from oikos: web/, cmd/desktop/, compose/web/, desktop CI workflow, ci.yml web job, Makefile ui/desktop/desktop-package/install targets, the compose web service, oikos-web from deploy.sh's fallback prune list; wails + go-keyring dropped from go.mod, vendor synced. - README / CONTRIBUTING / AGENTS.md / .agents dev+operations docs now point at the new repo; mbse + mascot design docs carry a path note. Risk: production SPA serving depends on the new pipeline now; rollback is versioned-image re-up of the old web service from a pre-split checkout (port 8091). Desktop builds installed before the split still check dtoro/oikos releases — one manual reinstall, noted in the oikos-web release notes. Verification: go vet, make test (race), make generate-check, golangci (no new findings; baseline down 400→365); post-cutover curls — localhost:8091 200, /wails/runtime.js 404, outer Caddy 302 Authentik.
This commit is contained in:
38
web/node_modules/bits-ui/dist/bits/range-calendar/components/range-calendar-cell.svelte
generated
vendored
Normal file
38
web/node_modules/bits-ui/dist/bits/range-calendar/components/range-calendar-cell.svelte
generated
vendored
Normal file
@@ -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}
|
||||
4
web/node_modules/bits-ui/dist/bits/range-calendar/components/range-calendar-cell.svelte.d.ts
generated
vendored
Normal file
4
web/node_modules/bits-ui/dist/bits/range-calendar/components/range-calendar-cell.svelte.d.ts
generated
vendored
Normal file
@@ -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;
|
||||
38
web/node_modules/bits-ui/dist/bits/range-calendar/components/range-calendar-day.svelte
generated
vendored
Normal file
38
web/node_modules/bits-ui/dist/bits/range-calendar/components/range-calendar-day.svelte
generated
vendored
Normal file
@@ -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}
|
||||
4
web/node_modules/bits-ui/dist/bits/range-calendar/components/range-calendar-day.svelte.d.ts
generated
vendored
Normal file
4
web/node_modules/bits-ui/dist/bits/range-calendar/components/range-calendar-day.svelte.d.ts
generated
vendored
Normal file
@@ -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;
|
||||
153
web/node_modules/bits-ui/dist/bits/range-calendar/components/range-calendar.svelte
generated
vendored
Normal file
153
web/node_modules/bits-ui/dist/bits/range-calendar/components/range-calendar.svelte
generated
vendored
Normal file
@@ -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}
|
||||
4
web/node_modules/bits-ui/dist/bits/range-calendar/components/range-calendar.svelte.d.ts
generated
vendored
Normal file
4
web/node_modules/bits-ui/dist/bits/range-calendar/components/range-calendar.svelte.d.ts
generated
vendored
Normal file
@@ -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
web/node_modules/bits-ui/dist/bits/range-calendar/exports.d.ts
generated
vendored
Normal file
15
web/node_modules/bits-ui/dist/bits/range-calendar/exports.d.ts
generated
vendored
Normal 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
web/node_modules/bits-ui/dist/bits/range-calendar/exports.js
generated
vendored
Normal file
14
web/node_modules/bits-ui/dist/bits/range-calendar/exports.js
generated
vendored
Normal 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
web/node_modules/bits-ui/dist/bits/range-calendar/index.d.ts
generated
vendored
Normal file
1
web/node_modules/bits-ui/dist/bits/range-calendar/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export * as RangeCalendar from "./exports.js";
|
||||
1
web/node_modules/bits-ui/dist/bits/range-calendar/index.js
generated
vendored
Normal file
1
web/node_modules/bits-ui/dist/bits/range-calendar/index.js
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export * as RangeCalendar from "./exports.js";
|
||||
232
web/node_modules/bits-ui/dist/bits/range-calendar/range-calendar.svelte.d.ts
generated
vendored
Normal file
232
web/node_modules/bits-ui/dist/bits/range-calendar/range-calendar.svelte.d.ts
generated
vendored
Normal file
@@ -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
web/node_modules/bits-ui/dist/bits/range-calendar/range-calendar.svelte.js
generated
vendored
Normal file
672
web/node_modules/bits-ui/dist/bits/range-calendar/range-calendar.svelte.js
generated
vendored
Normal 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
web/node_modules/bits-ui/dist/bits/range-calendar/types.d.ts
generated
vendored
Normal file
212
web/node_modules/bits-ui/dist/bits/range-calendar/types.d.ts
generated
vendored
Normal 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
web/node_modules/bits-ui/dist/bits/range-calendar/types.js
generated
vendored
Normal file
1
web/node_modules/bits-ui/dist/bits/range-calendar/types.js
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export {};
|
||||
Reference in New Issue
Block a user