Consolidate Soma selection runtimes

active-uix
dev 5 months ago
parent 131cb4fc1b
commit c4c37a3719

@ -27,7 +27,7 @@ import {
} from '../../layers/floating';
import { comboboxMorfo } from '../../../morfo/components/combobox';
import type { SomaRuntimePart } from '../../runtime.svelte';
import type { SomaRuntime, SomaRuntimePart } from '../../runtime.svelte';
const attrs = createAttrs(comboboxMorfo);
@ -53,6 +53,8 @@ interface ComboboxOpts
export class ComboboxProvider {
readonly opts: ComboboxOpts;
readonly runtimePart: SomaRuntimePart;
readonly soma: Soma;
readonly runtime: SomaRuntime;
static readonly ctx = context<ComboboxProvider>('Combobox');
static get(): ComboboxProvider | undefined {
return this.ctx.getOr(undefined) as ComboboxProvider | undefined;
@ -65,7 +67,6 @@ export class ComboboxProvider {
return new ComboboxProvider(opts);
}
readonly soma = Soma.get();
readonly floatingProvider: FloatingProvider;
readonly contentPresence: Presence;
@ -98,9 +99,14 @@ export class ComboboxProvider {
private constructor(opts: ComboboxOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(comboboxMorfo, {})
.part('provider', { id: opts.id, ref: opts.ref, owner: this, context: ComboboxProvider.ctx });
this.soma = Soma.require();
this.runtime = this.soma.runtime(comboboxMorfo, {});
this.runtimePart = this.runtime.part('provider', {
id: opts.id,
ref: opts.ref,
owner: this,
context: ComboboxProvider.ctx
});
this.floatingProvider = FloatingProvider.create();
@ -191,10 +197,12 @@ export class ComboboxInputProvider {
private constructor(opts: ComboboxInputOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(comboboxMorfo, {})
.part('input', { id: opts.id, ref: opts.ref, owner: this });
this.provider = ComboboxProvider.require();
this.runtimePart = this.provider.runtime.part('input', {
id: opts.id,
ref: opts.ref,
owner: this
});
this.provider.inputId.current = opts.id.current;
this.provider.inputRef = opts.ref;
@ -297,10 +305,12 @@ export class ComboboxTriggerProvider {
private constructor(opts: ComboboxTriggerOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(comboboxMorfo, {})
.part('trigger', { id: opts.id, ref: opts.ref, owner: this });
this.provider = ComboboxProvider.require();
this.runtimePart = this.provider.runtime.part('trigger', {
id: opts.id,
ref: opts.ref,
owner: this
});
this.provider.triggerId.current = opts.id.current;
this.provider.triggerRef = opts.ref;
}
@ -314,7 +324,7 @@ export class ComboboxTriggerProvider {
...this.runtimePart.props,
type: 'button' as const,
tabindex: -1,
'aria-label': this.provider.soma?.langs.ts(COMBOBOX_LANGS.TOGGLE),
'aria-label': this.provider.soma.langs.ts(COMBOBOX_LANGS.TOGGLE),
'aria-haspopup': 'listbox' as const,
'aria-expanded': this.provider.opts.open.current,
'aria-controls': this.provider.contentId.current || undefined,
@ -364,9 +374,8 @@ export class ComboboxContentProvider {
private constructor(opts: ComboboxContentOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(comboboxMorfo, {})
.part('content', {
this.provider = ComboboxProvider.require();
this.runtimePart = this.provider.runtime.part('content', {
id: opts.id,
ref: opts.ref,
owner: this,
@ -374,7 +383,6 @@ export class ComboboxContentProvider {
this.provider.setContentRef(el);
}
});
this.provider = ComboboxProvider.require();
this.provider.contentId.current = opts.id.current;
// Floating
@ -528,9 +536,12 @@ export class ComboboxViewportProvider {
private constructor(opts: ComboboxViewportOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(comboboxMorfo, {})
.part('viewport', { id: opts.id, ref: opts.ref, owner: this });
const provider = ComboboxProvider.require();
this.runtimePart = provider.runtime.part('viewport', {
id: opts.id,
ref: opts.ref,
owner: this
});
}
readonly props = $derived.by(() =>
@ -563,10 +574,12 @@ export class ComboboxItemProvider {
private constructor(opts: ComboboxItemOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(comboboxMorfo, {})
.part('item', { id: opts.id, ref: opts.ref, owner: this });
this.provider = ComboboxProvider.require();
this.runtimePart = this.provider.runtime.part('item', {
id: opts.id,
ref: opts.ref,
owner: this
});
// Maintain value → label registry for display-text resolution without
// relying on DOM queries (content may be unmounted or lazy-rendered).
@ -617,6 +630,7 @@ interface ComboboxGroupOpts extends WithRefOpts {}
export class ComboboxGroupProvider {
readonly opts: ComboboxGroupOpts;
readonly runtimePart: SomaRuntimePart;
readonly provider: ComboboxProvider;
static readonly ctx = context<ComboboxGroupProvider>('ComboboxGroup');
static get(): ComboboxGroupProvider | undefined {
return this.ctx.getOr(undefined) as ComboboxGroupProvider | undefined;
@ -633,9 +647,8 @@ export class ComboboxGroupProvider {
private constructor(opts: ComboboxGroupOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(comboboxMorfo, {})
.part('group', {
this.provider = ComboboxProvider.require();
this.runtimePart = this.provider.runtime.part('group', {
id: opts.id,
ref: opts.ref,
owner: this,
@ -667,10 +680,13 @@ export class ComboboxGroupHeadingProvider {
private constructor(opts: ComboboxGroupHeadingOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(comboboxMorfo, {})
.part('group-heading', { id: opts.id, ref: opts.ref, owner: this });
this.group = ComboboxGroupProvider.get() as ComboboxGroupProvider | undefined;
const provider = this.group?.provider ?? ComboboxProvider.require();
this.runtimePart = provider.runtime.part('group-heading', {
id: opts.id,
ref: opts.ref,
owner: this
});
if (this.group) {
const group = this.group;
group.headingId.current = opts.id.current;
@ -695,9 +711,12 @@ export class ComboboxSeparatorProvider {
private constructor(opts: ComboboxSeparatorOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(comboboxMorfo, {})
.part('separator', { id: opts.id, ref: opts.ref, owner: this });
const provider = ComboboxProvider.require();
this.runtimePart = provider.runtime.part('separator', {
id: opts.id,
ref: opts.ref,
owner: this
});
}
readonly props = $derived.by(() =>
@ -723,9 +742,12 @@ export class ComboboxArrowProvider {
private constructor(opts: ComboboxArrowOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(comboboxMorfo, {})
.part('arrow', { id: opts.id, ref: opts.ref, owner: this });
const provider = ComboboxProvider.require();
this.runtimePart = provider.runtime.part('arrow', {
id: opts.id,
ref: opts.ref,
owner: this
});
this.floatingArrow = FloatingArrow.create(opts);
}

@ -33,7 +33,7 @@ import {
} from '../../layers/floating';
import { selectMorfo } from '../../../morfo/components/select';
import type { SomaRuntimePart } from '../../runtime.svelte';
import type { SomaRuntime, SomaRuntimePart } from '../../runtime.svelte';
const attrs = createAttrs(selectMorfo);
@ -96,6 +96,8 @@ interface SelectOpts
export class SelectProvider {
readonly opts: SelectOpts;
readonly runtimePart: SomaRuntimePart;
readonly soma: Soma;
readonly runtime: SomaRuntime;
static readonly ctx = context<SelectProvider>('Select');
static get(): SelectProvider | undefined {
return this.ctx.getOr(undefined) as SelectProvider | undefined;
@ -108,7 +110,6 @@ export class SelectProvider {
return new SelectProvider(opts);
}
readonly config = Soma.get();
readonly floatingProvider: FloatingProvider;
readonly contentPresence: Presence;
readonly typeahead = new Typeahead();
@ -136,9 +137,14 @@ export class SelectProvider {
private constructor(opts: SelectOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(selectMorfo, {})
.part('provider', { id: opts.id, ref: opts.ref, owner: this, context: SelectProvider.ctx });
this.soma = Soma.require();
this.runtime = this.soma.runtime(selectMorfo, {});
this.runtimePart = this.runtime.part('provider', {
id: opts.id,
ref: opts.ref,
owner: this,
context: SelectProvider.ctx
});
this.floatingProvider = FloatingProvider.create();
@ -258,9 +264,8 @@ export class SelectTriggerProvider {
// (fired by attachRef at mount/unmount inside `untrack`). Avoids the
// `$effect` child→parent reactive edge that used to exist here.
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(selectMorfo, {})
.part('trigger', {
this.provider = SelectProvider.require();
this.runtimePart = this.provider.runtime.part('trigger', {
id: opts.id,
ref: opts.ref,
owner: this,
@ -268,7 +273,6 @@ export class SelectTriggerProvider {
this.provider.triggerRef.current = el;
}
});
this.provider = SelectProvider.require();
this.provider.triggerId.current = opts.id.current;
FloatingAnchor.create({
@ -394,10 +398,12 @@ export class SelectValueProvider {
private constructor(opts: SelectValueOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(selectMorfo, {})
.part('value', { id: opts.id, ref: opts.ref, owner: this });
this.provider = SelectProvider.require();
this.runtimePart = this.provider.runtime.part('value', {
id: opts.id,
ref: opts.ref,
owner: this
});
}
readonly displayText = $derived.by(() => {
@ -454,9 +460,8 @@ export class SelectContentProvider {
private constructor(opts: SelectContentOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(selectMorfo, {})
.part('content', {
this.provider = SelectProvider.require();
this.runtimePart = this.provider.runtime.part('content', {
id: opts.id,
ref: opts.ref,
owner: this,
@ -464,7 +469,6 @@ export class SelectContentProvider {
this.provider.setContentRef(el);
}
});
this.provider = SelectProvider.require();
this.provider.contentId.current = opts.id.current;
// Floating
@ -614,9 +618,12 @@ export class SelectViewportProvider {
private constructor(opts: SelectViewportOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(selectMorfo, {})
.part('viewport', { id: opts.id, ref: opts.ref, owner: this });
const provider = SelectProvider.require();
this.runtimePart = provider.runtime.part('viewport', {
id: opts.id,
ref: opts.ref,
owner: this
});
}
readonly props = $derived.by(() =>
@ -648,10 +655,12 @@ export class SelectItemProvider {
private constructor(opts: SelectItemOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(selectMorfo, {})
.part('item', { id: opts.id, ref: opts.ref, owner: this });
this.provider = SelectProvider.require();
this.runtimePart = this.provider.runtime.part('item', {
id: opts.id,
ref: opts.ref,
owner: this
});
// Register label for display text (avoids DOM queries)
this.provider.registerLabel(opts.value.current, opts.label.current || opts.value.current);
@ -699,6 +708,7 @@ interface SelectGroupOpts extends WithRefOpts {}
export class SelectGroupProvider {
readonly opts: SelectGroupOpts;
readonly runtimePart: SomaRuntimePart;
readonly provider: SelectProvider;
static readonly ctx = context<SelectGroupProvider>('SelectGroup');
static get(): SelectGroupProvider | undefined {
return this.ctx.getOr(undefined) as SelectGroupProvider | undefined;
@ -715,9 +725,13 @@ export class SelectGroupProvider {
private constructor(opts: SelectGroupOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(selectMorfo, {})
.part('group', { id: opts.id, ref: opts.ref, owner: this, context: SelectGroupProvider.ctx });
this.provider = SelectProvider.require();
this.runtimePart = this.provider.runtime.part('group', {
id: opts.id,
ref: opts.ref,
owner: this,
context: SelectGroupProvider.ctx
});
}
readonly props = $derived.by(() =>
@ -744,10 +758,13 @@ export class SelectGroupHeadingProvider {
private constructor(opts: SelectGroupHeadingOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(selectMorfo, {})
.part('group-heading', { id: opts.id, ref: opts.ref, owner: this });
this.group = SelectGroupProvider.get() as SelectGroupProvider | undefined;
const provider = this.group?.provider ?? SelectProvider.require();
this.runtimePart = provider.runtime.part('group-heading', {
id: opts.id,
ref: opts.ref,
owner: this
});
if (this.group) {
const group = this.group;
group.headingId.current = opts.id.current;
@ -772,9 +789,12 @@ export class SelectSeparatorProvider {
private constructor(opts: SelectSeparatorOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(selectMorfo, {})
.part('separator', { id: opts.id, ref: opts.ref, owner: this });
const provider = SelectProvider.require();
this.runtimePart = provider.runtime.part('separator', {
id: opts.id,
ref: opts.ref,
owner: this
});
}
readonly props = $derived.by(() =>
@ -800,9 +820,12 @@ export class SelectArrowProvider {
private constructor(opts: SelectArrowOpts) {
this.opts = opts;
this.runtimePart = Soma.require()
.runtime(selectMorfo, {})
.part('arrow', { id: opts.id, ref: opts.ref, owner: this });
const provider = SelectProvider.require();
this.runtimePart = provider.runtime.part('arrow', {
id: opts.id,
ref: opts.ref,
owner: this
});
this.floatingArrow = FloatingArrow.create(opts);
}

Loading…
Cancel
Save

Powered by TurnKey Linux.