Consolidate Soma stepper menu runtimes

active-uix
dev 5 months ago
parent ff91194d71
commit 7822e74b3a

@ -20,7 +20,7 @@ import { FloatingAnchor } from '../../layers/floating';
import { menubarMorfo } from '../../../morfo/components/menubar'; import { menubarMorfo } from '../../../morfo/components/menubar';
import { Soma } from '../../core/soma.svelte'; import { Soma } from '../../core/soma.svelte';
import type { SomaRuntimePart } from '../../runtime.svelte'; import type { SomaRuntime, SomaRuntimePart } from '../../runtime.svelte';
// ── Root ───────────────────────────────────────────────────────────────────── // ── Root ─────────────────────────────────────────────────────────────────────
@ -37,6 +37,8 @@ interface MenubarOpts
export class MenubarProvider { export class MenubarProvider {
readonly opts: MenubarOpts; readonly opts: MenubarOpts;
readonly runtimePart: SomaRuntimePart; readonly runtimePart: SomaRuntimePart;
readonly soma: Soma;
readonly runtime: SomaRuntime;
static readonly ctx = context<MenubarProvider>('Menubar'); static readonly ctx = context<MenubarProvider>('Menubar');
static get(): MenubarProvider | undefined { static get(): MenubarProvider | undefined {
return this.ctx.getOr(undefined) as MenubarProvider | undefined; return this.ctx.getOr(undefined) as MenubarProvider | undefined;
@ -57,9 +59,14 @@ export class MenubarProvider {
private constructor(opts: MenubarOpts) { private constructor(opts: MenubarOpts) {
this.opts = opts; this.opts = opts;
this.runtimePart = Soma.require() this.soma = Soma.require();
.runtime(menubarMorfo, {}) this.runtime = this.soma.runtime(menubarMorfo, {});
.part('provider', { id: opts.id, ref: opts.ref, owner: this, context: MenubarProvider.ctx }); this.runtimePart = this.runtime.part('provider', {
id: opts.id,
ref: opts.ref,
owner: this,
context: MenubarProvider.ctx
});
} }
registerTrigger(value: string, el: HTMLElement) { registerTrigger(value: string, el: HTMLElement) {
@ -300,11 +307,13 @@ export class MenubarTriggerProvider {
private constructor(opts: MenubarTriggerOpts) { private constructor(opts: MenubarTriggerOpts) {
this.opts = opts; this.opts = opts;
this.runtimePart = Soma.require()
.runtime(menubarMorfo, {})
.part('trigger', { id: opts.id, ref: opts.ref, owner: this });
this.menu = MenubarMenuProvider.require(); this.menu = MenubarMenuProvider.require();
this.provider = this.menu.provider; this.provider = this.menu.provider;
this.runtimePart = this.provider.runtime.part('trigger', {
id: opts.id,
ref: opts.ref,
owner: this
});
// Pipe the trigger id so the internal DropdownMenu.MenuProvider's // Pipe the trigger id so the internal DropdownMenu.MenuProvider's
// content aria-labelledby targets this button. // content aria-labelledby targets this button.

@ -7,7 +7,7 @@ import { Soma } from '../../core/soma.svelte';
import { STEPPER_LANGS } from './langs'; import { STEPPER_LANGS } from './langs';
import { stepperMorfo } from '../../../morfo/components/stepper'; import { stepperMorfo } from '../../../morfo/components/stepper';
import type { SomaRuntimePart } from '../../runtime.svelte'; import type { SomaRuntime, SomaRuntimePart } from '../../runtime.svelte';
export type StepState = 'complete' | 'current' | 'incomplete'; export type StepState = 'complete' | 'current' | 'incomplete';
@ -27,6 +27,8 @@ interface StepperOpts
export class StepperProvider { export class StepperProvider {
readonly opts: StepperOpts; readonly opts: StepperOpts;
readonly runtimePart: SomaRuntimePart; readonly runtimePart: SomaRuntimePart;
readonly soma: Soma;
readonly runtime: SomaRuntime;
static readonly ctx = context<StepperProvider>('Stepper'); static readonly ctx = context<StepperProvider>('Stepper');
static get(): StepperProvider | undefined { static get(): StepperProvider | undefined {
return this.ctx.getOr(undefined) as StepperProvider | undefined; return this.ctx.getOr(undefined) as StepperProvider | undefined;
@ -39,7 +41,6 @@ export class StepperProvider {
return new StepperProvider(opts); return new StepperProvider(opts);
} }
readonly soma = Soma.get();
private _count = $state(0); private _count = $state(0);
get count(): number { get count(): number {
return this._count; return this._count;
@ -72,9 +73,14 @@ export class StepperProvider {
private constructor(opts: StepperOpts) { private constructor(opts: StepperOpts) {
this.opts = opts; this.opts = opts;
this.runtimePart = Soma.require() this.soma = Soma.require();
.runtime(stepperMorfo, {}) this.runtime = this.soma.runtime(stepperMorfo, {});
.part('provider', { id: opts.id, ref: opts.ref, owner: this, context: StepperProvider.ctx }); this.runtimePart = this.runtime.part('provider', {
id: opts.id,
ref: opts.ref,
owner: this,
context: StepperProvider.ctx
});
} }
get step(): number { get step(): number {
@ -142,17 +148,19 @@ export class StepperListProvider {
private constructor(opts: StepperListOpts) { private constructor(opts: StepperListOpts) {
this.opts = opts; this.opts = opts;
this.runtimePart = Soma.require()
.runtime(stepperMorfo, {})
.part('list', { id: opts.id, ref: opts.ref, owner: this });
this.provider = StepperProvider.require(); this.provider = StepperProvider.require();
this.runtimePart = this.provider.runtime.part('list', {
id: opts.id,
ref: opts.ref,
owner: this
});
} }
readonly props = $derived.by(() => readonly props = $derived.by(() =>
this.runtimePart.assert({ this.runtimePart.assert({
...this.runtimePart.props, ...this.runtimePart.props,
role: 'tablist' as const, role: 'tablist' as const,
'aria-label': this.opts.ariaLabel.current || this.provider.soma?.langs.ts(STEPPER_LANGS.LIST), 'aria-label': this.opts.ariaLabel.current || this.provider.soma.langs.ts(STEPPER_LANGS.LIST),
'aria-orientation': this.provider.opts.orientation.current, 'aria-orientation': this.provider.opts.orientation.current,
'data-orientation': this.provider.opts.orientation.current 'data-orientation': this.provider.opts.orientation.current
} as const) } as const)
@ -189,10 +197,13 @@ export class StepperItemProvider {
private constructor(opts: StepperItemOpts) { private constructor(opts: StepperItemOpts) {
this.opts = opts; this.opts = opts;
this.runtimePart = Soma.require()
.runtime(stepperMorfo, {})
.part('item', { id: opts.id, ref: opts.ref, owner: this, context: StepperItemProvider.ctx });
this.provider = StepperProvider.require(); this.provider = StepperProvider.require();
this.runtimePart = this.provider.runtime.part('item', {
id: opts.id,
ref: opts.ref,
owner: this,
context: StepperItemProvider.ctx
});
// Track count: increment on mount, decrement on unmount // Track count: increment on mount, decrement on unmount
this.provider.incrementCount(); this.provider.incrementCount();
@ -240,11 +251,13 @@ export class StepperTriggerProvider {
private constructor(opts: StepperTriggerOpts) { private constructor(opts: StepperTriggerOpts) {
this.opts = opts; this.opts = opts;
this.runtimePart = Soma.require()
.runtime(stepperMorfo, {})
.part('trigger', { id: opts.id, ref: opts.ref, owner: this });
this.provider = StepperProvider.require(); this.provider = StepperProvider.require();
this.item = StepperItemProvider.require(); this.item = StepperItemProvider.require();
this.runtimePart = this.provider.runtime.part('trigger', {
id: opts.id,
ref: opts.ref,
owner: this
});
this.item.triggerId.current = opts.id.current; this.item.triggerId.current = opts.id.current;
// Register in trigger registry for keyboard nav // Register in trigger registry for keyboard nav
@ -335,10 +348,12 @@ export class StepperIndicatorProvider {
private constructor(opts: StepperIndicatorOpts) { private constructor(opts: StepperIndicatorOpts) {
this.opts = opts; this.opts = opts;
this.runtimePart = Soma.require()
.runtime(stepperMorfo, {})
.part('indicator', { id: opts.id, ref: opts.ref, owner: this });
this.item = StepperItemProvider.require(); this.item = StepperItemProvider.require();
this.runtimePart = this.item.provider.runtime.part('indicator', {
id: opts.id,
ref: opts.ref,
owner: this
});
} }
/** Proxy to `item.stepState` so the wrapper snippet doesn't need `ctx.get()`. */ /** Proxy to `item.stepState` so the wrapper snippet doesn't need `ctx.get()`. */
@ -372,11 +387,13 @@ export class StepperSeparatorProvider {
private constructor(opts: StepperSeparatorOpts) { private constructor(opts: StepperSeparatorOpts) {
this.opts = opts; this.opts = opts;
this.runtimePart = Soma.require()
.runtime(stepperMorfo, {})
.part('separator', { id: opts.id, ref: opts.ref, owner: this });
this.item = StepperItemProvider.require(); this.item = StepperItemProvider.require();
this.provider = StepperProvider.require(); this.provider = this.item.provider;
this.runtimePart = this.provider.runtime.part('separator', {
id: opts.id,
ref: opts.ref,
owner: this
});
} }
readonly props = $derived.by(() => readonly props = $derived.by(() =>
@ -412,10 +429,12 @@ export class StepperContentProvider {
private constructor(opts: StepperContentOpts) { private constructor(opts: StepperContentOpts) {
this.opts = opts; this.opts = opts;
this.runtimePart = Soma.require()
.runtime(stepperMorfo, {})
.part('content', { id: opts.id, ref: opts.ref, owner: this });
this.provider = StepperProvider.require(); this.provider = StepperProvider.require();
this.runtimePart = this.provider.runtime.part('content', {
id: opts.id,
ref: opts.ref,
owner: this
});
// Content can be inside or outside an Item — use get() not require() // Content can be inside or outside an Item — use get() not require()
const item = StepperItemProvider.get(); const item = StepperItemProvider.get();
if (item) item.contentId.current = opts.id.current; if (item) item.contentId.current = opts.id.current;
@ -450,10 +469,12 @@ export class StepperCompletedContentProvider {
private constructor(opts: StepperCompletedContentOpts) { private constructor(opts: StepperCompletedContentOpts) {
this.opts = opts; this.opts = opts;
this.runtimePart = Soma.require()
.runtime(stepperMorfo, {})
.part('completed-content', { id: opts.id, ref: opts.ref, owner: this });
this.provider = StepperProvider.require(); this.provider = StepperProvider.require();
this.runtimePart = this.provider.runtime.part('completed-content', {
id: opts.id,
ref: opts.ref,
owner: this
});
} }
/** Proxy to `provider.isCompleted` so the wrapper doesn't need `ctx.get()`. */ /** Proxy to `provider.isCompleted` so the wrapper doesn't need `ctx.get()`. */
@ -487,10 +508,12 @@ export class StepperPrevTriggerProvider {
private constructor(opts: StepperPrevTriggerOpts) { private constructor(opts: StepperPrevTriggerOpts) {
this.opts = opts; this.opts = opts;
this.runtimePart = Soma.require()
.runtime(stepperMorfo, {})
.part('prev-trigger', { id: opts.id, ref: opts.ref, owner: this });
this.provider = StepperProvider.require(); this.provider = StepperProvider.require();
this.runtimePart = this.provider.runtime.part('prev-trigger', {
id: opts.id,
ref: opts.ref,
owner: this
});
} }
readonly isDisabled = $derived.by(() => this.opts.disabled.current || !this.provider.hasPrevStep); readonly isDisabled = $derived.by(() => this.opts.disabled.current || !this.provider.hasPrevStep);
@ -531,10 +554,12 @@ export class StepperNextTriggerProvider {
private constructor(opts: StepperNextTriggerOpts) { private constructor(opts: StepperNextTriggerOpts) {
this.opts = opts; this.opts = opts;
this.runtimePart = Soma.require()
.runtime(stepperMorfo, {})
.part('next-trigger', { id: opts.id, ref: opts.ref, owner: this });
this.provider = StepperProvider.require(); this.provider = StepperProvider.require();
this.runtimePart = this.provider.runtime.part('next-trigger', {
id: opts.id,
ref: opts.ref,
owner: this
});
} }
readonly isDisabled = $derived.by(() => this.opts.disabled.current || !this.provider.hasNextStep); readonly isDisabled = $derived.by(() => this.opts.disabled.current || !this.provider.hasNextStep);

Loading…
Cancel
Save

Powered by TurnKey Linux.