Consolidate Soma provider runtimes

active-uix
dev 5 months ago
parent 73862094f6
commit a60d040282

@ -35,6 +35,7 @@ export class AccordionProvider {
readonly opts: AccordionOpts;
readonly runtimePart: SomaRuntimePart;
readonly soma = Soma.require();
readonly runtime: SomaRuntime;
static readonly ctx = context<AccordionProvider>('Accordion');
static get(): AccordionProvider | undefined {
return this.ctx.getOr(undefined) as AccordionProvider | undefined;
@ -51,20 +52,19 @@ export class AccordionProvider {
private constructor(opts: AccordionOpts) {
this.opts = opts;
this.runtimePart = this.soma
.runtime(accordionMorfo, {
props: {
orientation: () => this.opts.orientation.current,
disabled: () => this.opts.disabled.current
}
})
.part('provider', {
id: opts.id,
ref: opts.ref,
owner: this,
context: AccordionProvider.ctx,
syncAttrs: true
});
this.runtime = this.soma.runtime(accordionMorfo, {
props: {
orientation: () => this.opts.orientation.current,
disabled: () => this.opts.disabled.current
}
});
this.runtimePart = this.runtime.part('provider', {
id: opts.id,
ref: opts.ref,
owner: this,
context: AccordionProvider.ctx,
syncAttrs: true
});
$effect(() => {
if (this.opts.type.current !== 'single') return;
@ -177,7 +177,7 @@ export class AccordionItemProvider {
private constructor(opts: AccordionItemOpts) {
this.opts = opts;
this.provider = AccordionProvider.require();
this.runtime = Soma.require().runtime(accordionMorfo, {
this.runtime = this.provider.soma.runtime(accordionMorfo, {
states: {
open: () => this.isOpen
},
@ -255,18 +255,20 @@ export class AccordionHeaderProvider {
private constructor(opts: AccordionHeaderOpts) {
this.opts = opts;
this.item = AccordionItemProvider.require();
this.runtimePart = Soma.require()
.runtime(accordionMorfo, {
states: {
open: () => this.item.isOpen
},
props: {
level: () => Math.max(1, Math.min(6, this.opts.level.current)),
disabled: () => this.item.isDisabled,
orientation: () => this.item.provider.opts.orientation.current
}
})
.part('header', { id: opts.id, ref: opts.ref, owner: this, syncAttrs: true });
this.runtimePart = this.item.runtime.part('header', {
id: opts.id,
ref: opts.ref,
states: {
open: () => this.item.isOpen
},
props: {
level: () => Math.max(1, Math.min(6, this.opts.level.current)),
disabled: () => this.item.isDisabled,
orientation: () => this.item.provider.opts.orientation.current
},
owner: this,
syncAttrs: true
});
}
readonly props = $derived.by(() =>
@ -292,20 +294,22 @@ export class AccordionTriggerProvider {
private constructor(opts: AccordionTriggerOpts) {
this.opts = opts;
this.item = AccordionItemProvider.require();
this.runtimePart = Soma.require()
.runtime(accordionMorfo, {
states: {
open: () => this.item.isOpen
},
props: {
disabled: () => this.item.isDisabled,
orientation: () => this.item.provider.opts.orientation.current
},
parts: {
content: () => this.item.contentId.current || undefined
}
})
.part('trigger', { id: opts.id, ref: opts.ref, owner: this, syncAttrs: true });
this.runtimePart = this.item.runtime.part('trigger', {
id: opts.id,
ref: opts.ref,
states: {
open: () => this.item.isOpen
},
props: {
disabled: () => this.item.isDisabled,
orientation: () => this.item.provider.opts.orientation.current
},
parts: {
content: () => this.item.contentId.current || undefined
},
owner: this,
syncAttrs: true
});
this.item.triggerId.current = opts.id.current;
}
@ -377,29 +381,26 @@ export class AccordionContentProvider {
private constructor(opts: AccordionContentOpts) {
this.opts = opts;
this.item = AccordionItemProvider.require();
this.runtimePart = Soma.require()
.runtime(accordionMorfo, {
states: {
open: () => this.item.isOpen
},
props: {
disabled: () => this.item.isDisabled,
orientation: () => this.item.provider.opts.orientation.current,
region: () => this.item.provider.opts.region.current
},
parts: {
trigger: () => this.item.triggerId.current || undefined
}
})
.part('content', {
id: opts.id,
ref: opts.ref,
owner: this,
onRefChange: (el) => {
this.item.setContentRef(el);
},
syncAttrs: true
});
this.runtimePart = this.item.runtime.part('content', {
id: opts.id,
ref: opts.ref,
states: {
open: () => this.item.isOpen
},
props: {
disabled: () => this.item.isDisabled,
orientation: () => this.item.provider.opts.orientation.current,
region: () => this.item.provider.opts.region.current
},
parts: {
trigger: () => this.item.triggerId.current || undefined
},
owner: this,
onRefChange: (el) => {
this.item.setContentRef(el);
},
syncAttrs: true
});
this.item.contentId.current = opts.id.current;
}

@ -5,7 +5,7 @@ import { BREADCRUMB_LANGS } from './langs';
import type { BreadcrumbCurrent } from './types';
import { breadcrumbMorfo } from '../../../morfo/components/breadcrumb';
import type { SomaRuntimePart } from '../../runtime.svelte';
import type { SomaRuntime, SomaRuntimePart } from '../../runtime.svelte';
// ── Root ────────────────────────────────────────────────────────────────────
@ -21,6 +21,8 @@ interface BreadcrumbOpts
export class BreadcrumbProvider {
readonly opts: BreadcrumbOpts;
readonly runtimePart: SomaRuntimePart;
readonly soma: Soma;
readonly runtime: SomaRuntime;
static readonly ctx = context<BreadcrumbProvider>('Breadcrumb');
static get(): BreadcrumbProvider | undefined {
return this.ctx.getOr(undefined) as BreadcrumbProvider | undefined;
@ -33,30 +35,28 @@ export class BreadcrumbProvider {
return new BreadcrumbProvider(opts);
}
readonly soma = Soma.get();
private constructor(opts: BreadcrumbOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(breadcrumbMorfo, {
props: {
ariaLabel: () => this.resolvedAriaLabel.current,
ariaLabelledby: () => this.opts.ariaLabelledby.current
}
})
.part('provider', {
id: opts.id,
ref: opts.ref,
owner: this,
context: BreadcrumbProvider.ctx,
syncAttrs: true
});
this.soma = Soma.require();
this.runtime = this.soma.runtime(breadcrumbMorfo, {
props: {
ariaLabel: () => this.resolvedAriaLabel.current,
ariaLabelledby: () => this.opts.ariaLabelledby.current
}
});
this.runtimePart = this.runtime.part('provider', {
id: opts.id,
ref: opts.ref,
owner: this,
context: BreadcrumbProvider.ctx,
syncAttrs: true
});
}
readonly resolvedAriaLabel: Active<string | undefined> = readableActive(() =>
this.opts.ariaLabelledby.current
? undefined
: this.opts.ariaLabel.current || this.soma?.langs.ts(BREADCRUMB_LANGS.LABEL) || undefined
: this.opts.ariaLabel.current || this.soma.langs.ts(BREADCRUMB_LANGS.LABEL) || undefined
);
readonly props = $derived.by(() =>
@ -81,10 +81,12 @@ export class BreadcrumbListProvider {
private constructor(opts: BreadcrumbListOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(breadcrumbMorfo, {})
.part('list', { id: opts.id, ref: opts.ref, owner: this });
this.provider = BreadcrumbProvider.require();
this.runtimePart = this.provider.runtime.part('list', {
id: opts.id,
ref: opts.ref,
owner: this
});
}
readonly props = $derived.by(() =>
@ -117,20 +119,17 @@ export class BreadcrumbItemProvider {
private constructor(opts: BreadcrumbItemOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(breadcrumbMorfo, {
props: {
current: () => this.isCurrent
}
})
.part('item', {
id: opts.id,
ref: opts.ref,
owner: this,
context: BreadcrumbItemProvider.ctx,
syncAttrs: true
});
this.provider = BreadcrumbProvider.require();
this.runtimePart = this.provider.runtime.part('item', {
id: opts.id,
ref: opts.ref,
props: {
current: () => this.isCurrent
},
owner: this,
context: BreadcrumbItemProvider.ctx,
syncAttrs: true
});
}
readonly props = $derived.by(() =>
@ -157,14 +156,16 @@ export class BreadcrumbLinkProvider {
private constructor(opts: BreadcrumbLinkOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(breadcrumbMorfo, {
props: {
current: () => this.ariaCurrent
}
})
.part('link', { id: opts.id, ref: opts.ref, owner: this, syncAttrs: true });
this.provider = BreadcrumbProvider.require();
this.runtimePart = this.provider.runtime.part('link', {
id: opts.id,
ref: opts.ref,
props: {
current: () => this.ariaCurrent
},
owner: this,
syncAttrs: true
});
this.item = BreadcrumbItemProvider.get();
// Mirror current state onto the parent Item (so `[data-breadcrumb-item]`
@ -216,10 +217,13 @@ export class BreadcrumbSeparatorProvider {
private constructor(opts: BreadcrumbSeparatorOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(breadcrumbMorfo, {})
.part('separator', { id: opts.id, ref: opts.ref, owner: this, syncAttrs: true });
this.provider = BreadcrumbProvider.require();
this.runtimePart = this.provider.runtime.part('separator', {
id: opts.id,
ref: opts.ref,
owner: this,
syncAttrs: true
});
}
readonly props = $derived.by(() =>
@ -254,15 +258,17 @@ export class BreadcrumbEllipsisProvider {
private constructor(opts: BreadcrumbEllipsisOpts) {
this.opts = opts;
this.provider = BreadcrumbProvider.require();
this.runtimePart = Soma.require()
.runtime(breadcrumbMorfo, {
props: {
ariaLabel: () => this.resolvedAriaLabel.current,
interactive: () => this.isInteractive,
popupType: () => this.opts.popupType.current
}
})
.part('ellipsis', { id: opts.id, ref: opts.ref, owner: this, syncAttrs: true });
this.runtimePart = this.provider.runtime.part('ellipsis', {
id: opts.id,
ref: opts.ref,
props: {
ariaLabel: () => this.resolvedAriaLabel.current,
interactive: () => this.isInteractive,
popupType: () => this.opts.popupType.current
},
owner: this,
syncAttrs: true
});
}
readonly isInteractive = $derived.by(() => this.opts.interactive.current);
@ -270,7 +276,7 @@ export class BreadcrumbEllipsisProvider {
readonly resolvedAriaLabel: Active<string | undefined> = readableActive(
() =>
this.opts.ariaLabel.current ||
this.provider.soma?.langs.ts(BREADCRUMB_LANGS.ELLIPSIS) ||
this.provider.soma.langs.ts(BREADCRUMB_LANGS.ELLIPSIS) ||
undefined
);

@ -190,13 +190,15 @@ export class CheckboxIndicatorProvider {
private constructor(opts: CheckboxIndicatorOpts) {
this.opts = opts;
this.provider = CheckboxProvider.require();
this.runtimePart = Soma.require()
.runtime(checkboxMorfo, {
states: {
checked: () => this.provider.dataState
}
})
.part('indicator', { id: opts.id, ref: opts.ref, owner: this, syncAttrs: true });
this.runtimePart = this.provider.runtime.part('indicator', {
id: opts.id,
ref: opts.ref,
states: {
checked: () => this.provider.dataState
},
owner: this,
syncAttrs: true
});
}
get isVisible(): boolean {
@ -232,10 +234,13 @@ export class CheckboxHiddenInputProvider {
private constructor(opts: CheckboxHiddenInputOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(checkboxMorfo, {})
.part('hidden-input', { id: opts.id, ref: opts.ref, owner: this, syncAttrs: true });
this.provider = CheckboxProvider.require();
this.runtimePart = this.provider.runtime.part('hidden-input', {
id: opts.id,
ref: opts.ref,
owner: this,
syncAttrs: true
});
}
readonly props = $derived.by(() =>
@ -268,6 +273,8 @@ interface CheckboxGroupOpts
export class CheckboxGroupProvider {
readonly opts: CheckboxGroupOpts;
readonly runtimePart: SomaRuntimePart;
readonly soma: Soma;
readonly runtime: SomaRuntime;
static readonly ctx = context<CheckboxGroupProvider>('CheckboxGroup');
static get(): CheckboxGroupProvider | undefined {
return this.ctx.getOr(undefined) as CheckboxGroupProvider | undefined;
@ -284,23 +291,23 @@ export class CheckboxGroupProvider {
private constructor(opts: CheckboxGroupOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(checkboxMorfo, {
props: {
disabled: () => this.opts.disabled.current,
orientation: () => this.opts.orientation.current
},
parts: {
'group-label': () => this.labelId.current || undefined
}
})
.part('group', {
id: opts.id,
ref: opts.ref,
owner: this,
context: CheckboxGroupProvider.ctx,
syncAttrs: true
});
this.soma = Soma.require();
this.runtime = this.soma.runtime(checkboxMorfo, {
props: {
disabled: () => this.opts.disabled.current,
orientation: () => this.opts.orientation.current
},
parts: {
'group-label': () => this.labelId.current || undefined
}
});
this.runtimePart = this.runtime.part('group', {
id: opts.id,
ref: opts.ref,
owner: this,
context: CheckboxGroupProvider.ctx,
syncAttrs: true
});
}
isItemChecked(value: string): boolean {
@ -335,21 +342,24 @@ interface CheckboxGroupLabelOpts extends WithRefOpts {}
export class CheckboxGroupLabelProvider {
readonly opts: CheckboxGroupLabelOpts;
readonly runtimePart: SomaRuntimePart;
readonly group: CheckboxGroupProvider;
static create(opts: CheckboxGroupLabelOpts) {
return new CheckboxGroupLabelProvider(opts);
}
private constructor(opts: CheckboxGroupLabelOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(checkboxMorfo, {})
.part('group-label', { id: opts.id, ref: opts.ref, owner: this });
const group = CheckboxGroupProvider.get() as CheckboxGroupProvider | undefined;
this.group = CheckboxGroupProvider.require();
this.runtimePart = this.group.runtime.part('group-label', {
id: opts.id,
ref: opts.ref,
owner: this
});
// A30: register label id with direct assignment in the constructor.
// Ids are stable (generated once by `createId`) — wrapping this write
// in `$effect` would create an unneeded child→parent reactive edge.
if (group) group.setLabelId(opts.id.current);
this.group.setLabelId(opts.id.current);
}
readonly props = $derived.by(() =>

@ -4,7 +4,7 @@ import type { OnChangeFn, SomaMouseEvent } from '../../types';
import { Soma } from '../../core/soma.svelte';
import { CLIPBOARD_LANGS } from './langs';
import { clipboardMorfo } from '../../../morfo/components/clipboard';
import type { SomaRuntimePart } from '../../runtime.svelte';
import type { SomaRuntime, SomaRuntimePart } from '../../runtime.svelte';
// ── Root provider ──────────────────────────────────────────────────────────
@ -22,6 +22,8 @@ interface ClipboardOpts
export class ClipboardProvider {
readonly opts: ClipboardOpts;
readonly runtimePart: SomaRuntimePart;
readonly soma: Soma;
readonly runtime: SomaRuntime;
static readonly ctx = context<ClipboardProvider>('Clipboard');
static get(): ClipboardProvider | undefined {
return this.ctx.getOr(undefined) as ClipboardProvider | undefined;
@ -34,26 +36,24 @@ export class ClipboardProvider {
return new ClipboardProvider(opts);
}
readonly soma = Soma.get();
copied = $state(false);
private resetTimer: ReturnType<typeof setTimeout> | null = null;
private constructor(opts: ClipboardOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(clipboardMorfo, {
props: {
copied: () => this.copied
}
})
.part('provider', {
id: opts.id,
ref: opts.ref,
owner: this,
context: ClipboardProvider.ctx,
syncAttrs: true
});
this.soma = Soma.require();
this.runtime = this.soma.runtime(clipboardMorfo, {
props: {
copied: () => this.copied
}
});
this.runtimePart = this.runtime.part('provider', {
id: opts.id,
ref: opts.ref,
owner: this,
context: ClipboardProvider.ctx,
syncAttrs: true
});
$effect(() => {
return () => {
@ -120,14 +120,16 @@ export class ClipboardTriggerProvider {
private constructor(opts: ClipboardTriggerOpts) {
this.opts = opts;
this.provider = ClipboardProvider.require();
this.runtimePart = Soma.require()
.runtime(clipboardMorfo, {
props: {
copied: () => this.provider.copied,
ariaLabel: () => this.resolvedAriaLabel.current
}
})
.part('trigger', { id: opts.id, ref: opts.ref, owner: this, syncAttrs: true });
this.runtimePart = this.provider.runtime.part('trigger', {
id: opts.id,
ref: opts.ref,
props: {
copied: () => this.provider.copied,
ariaLabel: () => this.resolvedAriaLabel.current
},
owner: this,
syncAttrs: true
});
}
readonly resolvedAriaLabel: Active<string | undefined> = readableActive(() => {
@ -135,8 +137,8 @@ export class ClipboardTriggerProvider {
if (override) return override;
// When copied, announce the copied state; otherwise the copy action.
return this.provider.copied
? this.provider.soma?.langs.ts(CLIPBOARD_LANGS.COPIED)
: this.provider.soma?.langs.ts(CLIPBOARD_LANGS.COPY);
? this.provider.soma.langs.ts(CLIPBOARD_LANGS.COPIED)
: this.provider.soma.langs.ts(CLIPBOARD_LANGS.COPY);
});
readonly onclick = (_e: SomaMouseEvent<HTMLButtonElement>) => {
@ -170,13 +172,15 @@ export class ClipboardIndicatorProvider {
private constructor(opts: ClipboardIndicatorOpts) {
this.opts = opts;
this.provider = ClipboardProvider.require();
this.runtimePart = Soma.require()
.runtime(clipboardMorfo, {
props: {
copied: () => this.provider.copied
}
})
.part('indicator', { id: opts.id, ref: opts.ref, owner: this, syncAttrs: true });
this.runtimePart = this.provider.runtime.part('indicator', {
id: opts.id,
ref: opts.ref,
props: {
copied: () => this.provider.copied
},
owner: this,
syncAttrs: true
});
}
readonly isPresent = $derived.by(() => this.provider.copied);

@ -5,7 +5,7 @@ import { METER_LANGS } from './langs';
import type { MeterZone } from './types';
import { meterMorfo } from '../../../morfo/components/meter';
import type { SomaRuntimePart } from '../../runtime.svelte';
import type { SomaRuntime, SomaRuntimePart } from '../../runtime.svelte';
function computeZone(value: number, low: number, high: number): MeterZone {
@ -34,6 +34,8 @@ interface MeterOpts
export class MeterProvider {
readonly opts: MeterOpts;
readonly runtimePart: SomaRuntimePart;
readonly soma: Soma;
readonly runtime: SomaRuntime;
static readonly ctx = context<MeterProvider>('Meter');
static get(): MeterProvider | undefined {
return this.ctx.getOr(undefined) as MeterProvider | undefined;
@ -46,31 +48,29 @@ export class MeterProvider {
return new MeterProvider(opts);
}
readonly soma = Soma.get();
private constructor(opts: MeterOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(meterMorfo, {
states: {
optimum: () => this.state
},
props: {
min: () => this.opts.min.current,
max: () => this.opts.max.current,
value: () => this.opts.value.current,
valueText: () => this.resolvedValueText,
ariaLabel: () => this.resolvedAriaLabel.current,
ariaLabelledby: () => this.opts.ariaLabelledby.current
}
})
.part('provider', {
id: opts.id,
ref: opts.ref,
owner: this,
context: MeterProvider.ctx,
syncAttrs: true
});
this.soma = Soma.require();
this.runtime = this.soma.runtime(meterMorfo, {
states: {
optimum: () => this.state
},
props: {
min: () => this.opts.min.current,
max: () => this.opts.max.current,
value: () => this.opts.value.current,
valueText: () => this.resolvedValueText,
ariaLabel: () => this.resolvedAriaLabel.current,
ariaLabelledby: () => this.opts.ariaLabelledby.current
}
});
this.runtimePart = this.runtime.part('provider', {
id: opts.id,
ref: opts.ref,
owner: this,
context: MeterProvider.ctx,
syncAttrs: true
});
}
readonly resolvedLow = $derived.by(() => this.opts.low.current ?? this.opts.min.current);
@ -94,7 +94,7 @@ export class MeterProvider {
readonly resolvedAriaLabel: Active<string | undefined> = readableActive(() => {
if (this.opts.ariaLabelledby.current) return undefined;
return this.opts.ariaLabel.current || this.soma?.langs.ts(METER_LANGS.LABEL) || undefined;
return this.opts.ariaLabel.current || this.soma.langs.ts(METER_LANGS.LABEL) || undefined;
});
readonly resolvedValueText = $derived.by(() => {
@ -129,13 +129,15 @@ export class MeterIndicatorProvider {
private constructor(opts: MeterIndicatorOpts) {
this.opts = opts;
this.provider = MeterProvider.require();
this.runtimePart = Soma.require()
.runtime(meterMorfo, {
states: {
optimum: () => this.provider.state
}
})
.part('indicator', { id: opts.id, ref: opts.ref, owner: this, syncAttrs: true });
this.runtimePart = this.provider.runtime.part('indicator', {
id: opts.id,
ref: opts.ref,
states: {
optimum: () => this.provider.state
},
owner: this,
syncAttrs: true
});
}
readonly props = $derived.by(() =>

@ -5,7 +5,7 @@ import { Soma } from '../../core/soma.svelte';
import { PAGINATION_LANGS } from './langs';
import { paginationMorfo } from '../../../morfo/components/pagination';
import type { SomaRuntimePart } from '../../runtime.svelte';
import type { SomaRuntime, SomaRuntimePart } from '../../runtime.svelte';
// ── Page range algorithm ─────────────────────────────────────────────────────
@ -91,6 +91,8 @@ interface PaginationOpts
export class PaginationProvider {
readonly opts: PaginationOpts;
readonly runtimePart: SomaRuntimePart;
readonly soma: Soma;
readonly runtime: SomaRuntime;
static readonly ctx = context<PaginationProvider>('Pagination');
static get(): PaginationProvider | undefined {
return this.ctx.getOr(undefined) as PaginationProvider | undefined;
@ -103,23 +105,21 @@ export class PaginationProvider {
return new PaginationProvider(opts);
}
readonly soma = Soma.get();
private constructor(opts: PaginationOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(paginationMorfo, {
props: {
disabled: () => this.opts.disabled.current
}
})
.part('provider', {
id: opts.id,
ref: opts.ref,
owner: this,
context: PaginationProvider.ctx,
syncAttrs: true
});
this.soma = Soma.require();
this.runtime = this.soma.runtime(paginationMorfo, {
props: {
disabled: () => this.opts.disabled.current
}
});
this.runtimePart = this.runtime.part('provider', {
id: opts.id,
ref: opts.ref,
owner: this,
context: PaginationProvider.ctx,
syncAttrs: true
});
// Clamp page to valid range reactively (like Ark-UI/Zag.js)
$effect(() => {
@ -217,13 +217,15 @@ export class PaginationPrevTriggerProvider {
private constructor(opts: PaginationPrevTriggerOpts) {
this.opts = opts;
this.provider = PaginationProvider.require();
this.runtimePart = Soma.require()
.runtime(paginationMorfo, {
props: {
disabled: () => this.isDisabled
}
})
.part('prev-trigger', { id: opts.id, ref: opts.ref, owner: this, syncAttrs: true });
this.runtimePart = this.provider.runtime.part('prev-trigger', {
id: opts.id,
ref: opts.ref,
props: {
disabled: () => this.isDisabled
},
owner: this,
syncAttrs: true
});
}
readonly isDisabled = $derived.by(
@ -268,13 +270,15 @@ export class PaginationNextTriggerProvider {
private constructor(opts: PaginationNextTriggerOpts) {
this.opts = opts;
this.provider = PaginationProvider.require();
this.runtimePart = Soma.require()
.runtime(paginationMorfo, {
props: {
disabled: () => this.isDisabled
}
})
.part('next-trigger', { id: opts.id, ref: opts.ref, owner: this, syncAttrs: true });
this.runtimePart = this.provider.runtime.part('next-trigger', {
id: opts.id,
ref: opts.ref,
props: {
disabled: () => this.isDisabled
},
owner: this,
syncAttrs: true
});
}
readonly isDisabled = $derived.by(
@ -319,16 +323,18 @@ export class PaginationItemProvider {
private constructor(opts: PaginationItemOpts) {
this.opts = opts;
this.provider = PaginationProvider.require();
this.runtimePart = Soma.require()
.runtime(paginationMorfo, {
props: {
selected: () => this.isSelected,
disabled: () => this.isDisabled,
value: () => this.opts.value.current,
ariaLabel: () => this.ariaLabel
}
})
.part('item', { id: opts.id, ref: opts.ref, owner: this, syncAttrs: true });
this.runtimePart = this.provider.runtime.part('item', {
id: opts.id,
ref: opts.ref,
props: {
selected: () => this.isSelected,
disabled: () => this.isDisabled,
value: () => this.opts.value.current,
ariaLabel: () => this.ariaLabel
},
owner: this,
syncAttrs: true
});
}
readonly isSelected = $derived.by(
@ -339,7 +345,7 @@ export class PaginationItemProvider {
readonly ariaLabel = $derived.by(
() =>
this.provider.soma?.langs.t(PAGINATION_LANGS.PAGE, {
this.provider.soma.langs.t(PAGINATION_LANGS.PAGE, {
value: String(this.opts.value.current)
}) || `Page ${this.opts.value.current}`
);
@ -370,19 +376,23 @@ interface PaginationEllipsisOpts
export class PaginationEllipsisProvider {
readonly opts: PaginationEllipsisOpts;
readonly runtimePart: SomaRuntimePart;
readonly provider: PaginationProvider;
static create(opts: PaginationEllipsisOpts) {
return new PaginationEllipsisProvider(opts);
}
private constructor(opts: PaginationEllipsisOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(paginationMorfo, {
props: {
position: () => this.opts.position.current
}
})
.part('ellipsis', { id: opts.id, ref: opts.ref, owner: this, syncAttrs: true });
this.provider = PaginationProvider.require();
this.runtimePart = this.provider.runtime.part('ellipsis', {
id: opts.id,
ref: opts.ref,
props: {
position: () => this.opts.position.current
},
owner: this,
syncAttrs: true
});
}
readonly props = $derived.by(() =>

@ -5,7 +5,7 @@ import { PROGRESS_LANGS } from './langs';
import type { ProgressState } from './types';
import { progressMorfo } from '../../../morfo/components/progress';
import type { SomaRuntimePart } from '../../runtime.svelte';
import type { SomaRuntime, SomaRuntimePart } from '../../runtime.svelte';
function computeState(value: number | null, min: number, max: number): ProgressState {
@ -32,6 +32,8 @@ interface ProgressOpts
export class ProgressProvider {
readonly opts: ProgressOpts;
readonly runtimePart: SomaRuntimePart;
readonly soma: Soma;
readonly runtime: SomaRuntime;
static readonly ctx = context<ProgressProvider>('Progress');
static get(): ProgressProvider | undefined {
return this.ctx.getOr(undefined) as ProgressProvider | undefined;
@ -44,31 +46,29 @@ export class ProgressProvider {
return new ProgressProvider(opts);
}
readonly soma = Soma.get();
private constructor(opts: ProgressOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(progressMorfo, {
states: {
loading: () => this.state
},
props: {
min: () => this.opts.min.current,
max: () => this.opts.max.current,
value: () => this.opts.value.current ?? undefined,
valueText: () => this.resolvedValueText,
ariaLabel: () => this.resolvedAriaLabel.current,
ariaLabelledby: () => this.opts.ariaLabelledby.current
}
})
.part('provider', {
id: opts.id,
ref: opts.ref,
owner: this,
context: ProgressProvider.ctx,
syncAttrs: true
});
this.soma = Soma.require();
this.runtime = this.soma.runtime(progressMorfo, {
states: {
loading: () => this.state
},
props: {
min: () => this.opts.min.current,
max: () => this.opts.max.current,
value: () => this.opts.value.current ?? undefined,
valueText: () => this.resolvedValueText,
ariaLabel: () => this.resolvedAriaLabel.current,
ariaLabelledby: () => this.opts.ariaLabelledby.current
}
});
this.runtimePart = this.runtime.part('provider', {
id: opts.id,
ref: opts.ref,
owner: this,
context: ProgressProvider.ctx,
syncAttrs: true
});
}
readonly state: ReadonlyState = $derived.by(() => {
@ -88,7 +88,7 @@ export class ProgressProvider {
readonly resolvedAriaLabel: Active<string | undefined> = readableActive(() => {
if (this.opts.ariaLabelledby.current) return undefined;
return this.opts.ariaLabel.current || this.soma?.langs.ts(PROGRESS_LANGS.LABEL) || undefined;
return this.opts.ariaLabel.current || this.soma.langs.ts(PROGRESS_LANGS.LABEL) || undefined;
});
readonly resolvedValueText = $derived.by(() => {
@ -127,13 +127,15 @@ export class ProgressIndicatorProvider {
private constructor(opts: ProgressIndicatorOpts) {
this.opts = opts;
this.provider = ProgressProvider.require();
this.runtimePart = Soma.require()
.runtime(progressMorfo, {
states: {
loading: () => this.provider.state
}
})
.part('indicator', { id: opts.id, ref: opts.ref, owner: this, syncAttrs: true });
this.runtimePart = this.provider.runtime.part('indicator', {
id: opts.id,
ref: opts.ref,
states: {
loading: () => this.provider.state
},
owner: this,
syncAttrs: true
});
}
readonly props = $derived.by(() =>

Loading…
Cancel
Save

Powered by TurnKey Linux.