|
|
|
|
@ -42,7 +42,7 @@ import type {
|
|
|
|
|
// ── Contract ─────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
import { calendarMorfo } from '../../../morfo/components/calendar';
|
|
|
|
|
import type { SomaRuntimePart } from '../../runtime.svelte';
|
|
|
|
|
import type { SomaRuntime, SomaRuntimePart } from '../../runtime.svelte';
|
|
|
|
|
|
|
|
|
|
const attrs = createAttrs(calendarMorfo);
|
|
|
|
|
|
|
|
|
|
@ -107,6 +107,8 @@ export function resolveInitialCalendarPlaceholder(
|
|
|
|
|
export class CalendarProvider {
|
|
|
|
|
readonly opts: CalendarOpts;
|
|
|
|
|
readonly runtimePart: SomaRuntimePart;
|
|
|
|
|
readonly soma: Soma;
|
|
|
|
|
readonly runtime: SomaRuntime;
|
|
|
|
|
static readonly ctx = context<CalendarProvider>('Calendar');
|
|
|
|
|
static get(): CalendarProvider | undefined {
|
|
|
|
|
return this.ctx.getOr(undefined) as CalendarProvider | undefined;
|
|
|
|
|
@ -118,8 +120,6 @@ export class CalendarProvider {
|
|
|
|
|
return new CalendarProvider(opts);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
readonly soma = Soma.get();
|
|
|
|
|
|
|
|
|
|
/** Child ids exposed so the Grid can wire aria-labelledby. */
|
|
|
|
|
headingId = state('');
|
|
|
|
|
/** Live-region text for announcements. */
|
|
|
|
|
@ -127,9 +127,14 @@ export class CalendarProvider {
|
|
|
|
|
|
|
|
|
|
private constructor(opts: CalendarOpts) {
|
|
|
|
|
this.opts = opts;
|
|
|
|
|
this.runtimePart = Soma.require()
|
|
|
|
|
.runtime(calendarMorfo, {})
|
|
|
|
|
.part('provider', { id: opts.id, ref: opts.ref, owner: this, context: CalendarProvider.ctx });
|
|
|
|
|
this.soma = Soma.require();
|
|
|
|
|
this.runtime = this.soma.runtime(calendarMorfo, {});
|
|
|
|
|
this.runtimePart = this.runtime.part('provider', {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
ref: opts.ref,
|
|
|
|
|
owner: this,
|
|
|
|
|
context: CalendarProvider.ctx
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Initial focus management used to live in the wrapper. Move it here so
|
|
|
|
|
// the wrapper stays thin and the focus contract is centralized.
|
|
|
|
|
@ -147,7 +152,7 @@ export class CalendarProvider {
|
|
|
|
|
// ── Derived reactive model ────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
readonly effectiveLocale = $derived.by(
|
|
|
|
|
() => this.opts.locale.current || this.soma?.langs.getLocale() || 'en'
|
|
|
|
|
() => this.opts.locale.current || this.soma.langs.getLocale() || 'en'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
readonly months: Month<DateValue>[] = $derived.by(() =>
|
|
|
|
|
@ -175,7 +180,7 @@ export class CalendarProvider {
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
readonly dateOrder: DateOrder = $derived.by(() => {
|
|
|
|
|
return (this.soma?.dates?.getDateOrder?.() ?? 'DMY') as DateOrder;
|
|
|
|
|
return (this.soma.dates?.getDateOrder?.() ?? 'DMY') as DateOrder;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -216,7 +221,7 @@ export class CalendarProvider {
|
|
|
|
|
|
|
|
|
|
readonly fullCalendarLabel = $derived.by(() => {
|
|
|
|
|
const base =
|
|
|
|
|
this.opts.calendarLabel.current ?? this.soma?.langs.ts(CALENDAR_LANGS.LABEL) ?? 'Calendar';
|
|
|
|
|
this.opts.calendarLabel.current ?? this.soma.langs.ts(CALENDAR_LANGS.LABEL) ?? 'Calendar';
|
|
|
|
|
return `${base}, ${this.headingValue}`;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@ -444,10 +449,12 @@ export class CalendarHeaderProvider {
|
|
|
|
|
|
|
|
|
|
private constructor(opts: WithRefOpts) {
|
|
|
|
|
this.opts = opts;
|
|
|
|
|
this.runtimePart = Soma.require()
|
|
|
|
|
.runtime(calendarMorfo, {})
|
|
|
|
|
.part('header', { id: opts.id, ref: opts.ref, owner: this });
|
|
|
|
|
CalendarProvider.require();
|
|
|
|
|
const provider = CalendarProvider.require();
|
|
|
|
|
this.runtimePart = provider.runtime.part('header', {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
ref: opts.ref,
|
|
|
|
|
owner: this
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
readonly props = $derived.by(() =>
|
|
|
|
|
@ -468,10 +475,12 @@ export class CalendarHeadingProvider {
|
|
|
|
|
|
|
|
|
|
private constructor(opts: WithRefOpts) {
|
|
|
|
|
this.opts = opts;
|
|
|
|
|
this.runtimePart = Soma.require()
|
|
|
|
|
.runtime(calendarMorfo, {})
|
|
|
|
|
.part('heading', { id: opts.id, ref: opts.ref, owner: this });
|
|
|
|
|
this.provider = CalendarProvider.require();
|
|
|
|
|
this.runtimePart = this.provider.runtime.part('heading', {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
ref: opts.ref,
|
|
|
|
|
owner: this
|
|
|
|
|
});
|
|
|
|
|
this.provider.headingId.current = opts.id.current;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -494,10 +503,12 @@ abstract class CalendarPaginationButtonProvider {
|
|
|
|
|
|
|
|
|
|
protected constructor(opts: WithRefOpts, part: 'prev-button' | 'next-button', labelRef: string) {
|
|
|
|
|
this.opts = opts;
|
|
|
|
|
this.runtimePart = Soma.require()
|
|
|
|
|
.runtime(calendarMorfo, {})
|
|
|
|
|
.part(part, { id: opts.id, ref: opts.ref, owner: this });
|
|
|
|
|
this.provider = CalendarProvider.require();
|
|
|
|
|
this.runtimePart = this.provider.runtime.part(part, {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
ref: opts.ref,
|
|
|
|
|
owner: this
|
|
|
|
|
});
|
|
|
|
|
this.labelRef = labelRef;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -511,7 +522,7 @@ abstract class CalendarPaginationButtonProvider {
|
|
|
|
|
return this.runtimePart.assert({
|
|
|
|
|
...this.runtimePart.props,
|
|
|
|
|
type: 'button' as const,
|
|
|
|
|
'aria-label': this.provider.soma?.langs.ts(this.labelRef),
|
|
|
|
|
'aria-label': this.provider.soma.langs.ts(this.labelRef),
|
|
|
|
|
'aria-disabled': boolToStr(disabled),
|
|
|
|
|
'data-disabled': boolToEmptyStrOrUndef(disabled),
|
|
|
|
|
disabled,
|
|
|
|
|
@ -572,10 +583,12 @@ export class CalendarMonthSelectProvider {
|
|
|
|
|
|
|
|
|
|
private constructor(opts: CalendarMonthSelectOpts) {
|
|
|
|
|
this.opts = opts;
|
|
|
|
|
this.runtimePart = Soma.require()
|
|
|
|
|
.runtime(calendarMorfo, {})
|
|
|
|
|
.part('month-select', { id: opts.id, ref: opts.ref, owner: this });
|
|
|
|
|
this.provider = CalendarProvider.require();
|
|
|
|
|
this.runtimePart = this.provider.runtime.part('month-select', {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
ref: opts.ref,
|
|
|
|
|
owner: this
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
readonly monthItems: CalendarMonthSelectSnippetProps['monthItems'] = $derived.by(() => {
|
|
|
|
|
@ -611,7 +624,7 @@ export class CalendarMonthSelectProvider {
|
|
|
|
|
const disabled = this.opts.disabled.current || this.provider.opts.disabled.current;
|
|
|
|
|
return this.runtimePart.assert({
|
|
|
|
|
...this.runtimePart.props,
|
|
|
|
|
'aria-label': this.provider.soma?.langs.ts(CALENDAR_LANGS.MONTH_SELECT),
|
|
|
|
|
'aria-label': this.provider.soma.langs.ts(CALENDAR_LANGS.MONTH_SELECT),
|
|
|
|
|
'aria-disabled': boolToStr(disabled),
|
|
|
|
|
'data-disabled': boolToEmptyStrOrUndef(disabled),
|
|
|
|
|
disabled,
|
|
|
|
|
@ -643,10 +656,12 @@ export class CalendarYearSelectProvider {
|
|
|
|
|
|
|
|
|
|
private constructor(opts: CalendarYearSelectOpts) {
|
|
|
|
|
this.opts = opts;
|
|
|
|
|
this.runtimePart = Soma.require()
|
|
|
|
|
.runtime(calendarMorfo, {})
|
|
|
|
|
.part('year-select', { id: opts.id, ref: opts.ref, owner: this });
|
|
|
|
|
this.provider = CalendarProvider.require();
|
|
|
|
|
this.runtimePart = this.provider.runtime.part('year-select', {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
ref: opts.ref,
|
|
|
|
|
owner: this
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
readonly yearItems: CalendarYearSelectSnippetProps['yearItems'] = $derived.by(() => {
|
|
|
|
|
@ -682,7 +697,7 @@ export class CalendarYearSelectProvider {
|
|
|
|
|
const disabled = this.opts.disabled.current || this.provider.opts.disabled.current;
|
|
|
|
|
return this.runtimePart.assert({
|
|
|
|
|
...this.runtimePart.props,
|
|
|
|
|
'aria-label': this.provider.soma?.langs.ts(CALENDAR_LANGS.YEAR_SELECT),
|
|
|
|
|
'aria-label': this.provider.soma.langs.ts(CALENDAR_LANGS.YEAR_SELECT),
|
|
|
|
|
'aria-disabled': boolToStr(disabled),
|
|
|
|
|
'data-disabled': boolToEmptyStrOrUndef(disabled),
|
|
|
|
|
disabled,
|
|
|
|
|
@ -705,10 +720,12 @@ export class CalendarGridProvider {
|
|
|
|
|
|
|
|
|
|
private constructor(opts: WithRefOpts) {
|
|
|
|
|
this.opts = opts;
|
|
|
|
|
this.runtimePart = Soma.require()
|
|
|
|
|
.runtime(calendarMorfo, {})
|
|
|
|
|
.part('grid', { id: opts.id, ref: opts.ref, owner: this });
|
|
|
|
|
this.provider = CalendarProvider.require();
|
|
|
|
|
this.runtimePart = this.provider.runtime.part('grid', {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
ref: opts.ref,
|
|
|
|
|
owner: this
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
readonly props = $derived.by(() => {
|
|
|
|
|
@ -731,10 +748,12 @@ export class CalendarGridHeadProvider {
|
|
|
|
|
}
|
|
|
|
|
private constructor(opts: WithRefOpts) {
|
|
|
|
|
this.opts = opts;
|
|
|
|
|
this.runtimePart = Soma.require()
|
|
|
|
|
.runtime(calendarMorfo, {})
|
|
|
|
|
.part('grid-head', { id: opts.id, ref: opts.ref, owner: this });
|
|
|
|
|
CalendarProvider.require();
|
|
|
|
|
const provider = CalendarProvider.require();
|
|
|
|
|
this.runtimePart = provider.runtime.part('grid-head', {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
ref: opts.ref,
|
|
|
|
|
owner: this
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
readonly props = $derived.by(() =>
|
|
|
|
|
this.runtimePart.assert({ ...this.runtimePart.props } as const)
|
|
|
|
|
@ -749,10 +768,12 @@ export class CalendarGridBodyProvider {
|
|
|
|
|
}
|
|
|
|
|
private constructor(opts: WithRefOpts) {
|
|
|
|
|
this.opts = opts;
|
|
|
|
|
this.runtimePart = Soma.require()
|
|
|
|
|
.runtime(calendarMorfo, {})
|
|
|
|
|
.part('grid-body', { id: opts.id, ref: opts.ref, owner: this });
|
|
|
|
|
CalendarProvider.require();
|
|
|
|
|
const provider = CalendarProvider.require();
|
|
|
|
|
this.runtimePart = provider.runtime.part('grid-body', {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
ref: opts.ref,
|
|
|
|
|
owner: this
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
readonly props = $derived.by(() =>
|
|
|
|
|
this.runtimePart.assert({ ...this.runtimePart.props } as const)
|
|
|
|
|
@ -767,10 +788,12 @@ export class CalendarGridRowProvider {
|
|
|
|
|
}
|
|
|
|
|
private constructor(opts: WithRefOpts) {
|
|
|
|
|
this.opts = opts;
|
|
|
|
|
this.runtimePart = Soma.require()
|
|
|
|
|
.runtime(calendarMorfo, {})
|
|
|
|
|
.part('grid-row', { id: opts.id, ref: opts.ref, owner: this });
|
|
|
|
|
CalendarProvider.require();
|
|
|
|
|
const provider = CalendarProvider.require();
|
|
|
|
|
this.runtimePart = provider.runtime.part('grid-row', {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
ref: opts.ref,
|
|
|
|
|
owner: this
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
readonly props = $derived.by(() =>
|
|
|
|
|
this.runtimePart.assert({ ...this.runtimePart.props, role: 'row' as const } as const)
|
|
|
|
|
@ -785,10 +808,12 @@ export class CalendarHeadCellProvider {
|
|
|
|
|
}
|
|
|
|
|
private constructor(opts: WithRefOpts) {
|
|
|
|
|
this.opts = opts;
|
|
|
|
|
this.runtimePart = Soma.require()
|
|
|
|
|
.runtime(calendarMorfo, {})
|
|
|
|
|
.part('head-cell', { id: opts.id, ref: opts.ref, owner: this });
|
|
|
|
|
CalendarProvider.require();
|
|
|
|
|
const provider = CalendarProvider.require();
|
|
|
|
|
this.runtimePart = provider.runtime.part('head-cell', {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
ref: opts.ref,
|
|
|
|
|
owner: this
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
readonly props = $derived.by(() =>
|
|
|
|
|
this.runtimePart.assert({ ...this.runtimePart.props, scope: 'col' as const } as const)
|
|
|
|
|
@ -818,10 +843,13 @@ export class CalendarCellProvider {
|
|
|
|
|
|
|
|
|
|
private constructor(opts: CalendarCellOpts) {
|
|
|
|
|
this.opts = opts;
|
|
|
|
|
this.runtimePart = Soma.require()
|
|
|
|
|
.runtime(calendarMorfo, {})
|
|
|
|
|
.part('cell', { id: opts.id, ref: opts.ref, owner: this, context: CalendarCellProvider.ctx });
|
|
|
|
|
this.provider = CalendarProvider.require();
|
|
|
|
|
this.runtimePart = this.provider.runtime.part('cell', {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
ref: opts.ref,
|
|
|
|
|
owner: this,
|
|
|
|
|
context: CalendarCellProvider.ctx
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
readonly flags = $derived.by(() => this.provider.computeFlags(this.opts.date.current));
|
|
|
|
|
@ -880,11 +908,13 @@ export class CalendarDayProvider {
|
|
|
|
|
|
|
|
|
|
private constructor(opts: WithRefOpts) {
|
|
|
|
|
this.opts = opts;
|
|
|
|
|
this.runtimePart = Soma.require()
|
|
|
|
|
.runtime(calendarMorfo, {})
|
|
|
|
|
.part('day', { id: opts.id, ref: opts.ref, owner: this });
|
|
|
|
|
this.provider = CalendarProvider.require();
|
|
|
|
|
this.cell = CalendarCellProvider.require();
|
|
|
|
|
this.runtimePart = this.provider.runtime.part('day', {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
ref: opts.ref,
|
|
|
|
|
owner: this
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
readonly focused = $derived.by(() =>
|
|
|
|
|
|