You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/src/uix/soma/core/soma.svelte.ts

115 lines
3.8 KiB

import { Context } from 'runed';
import { getActiveUix, type ActiveUix } from '$active-uix';
import type { ActiveLangs } from '$langs';
import type { ActiveDom } from '$adom';
import type { ActiveNumbers } from '$format/numbers/types';
import type { ActiveCurrency } from '$format/currency/types';
import type { ActiveDates } from '$format/dates/types';
import type { ActiveUnits } from '$format/units/types';
import type { EngineLogger } from '$logger';
import type { EngineSemantic } from '$uix/sema';
import { createActiveUixPrefsView, type ActiveUixPrefsView } from '$active-uix/prefs';
import type { Morfo } from '$uix/morfo';
import { createSomaRuntime, type SomaRuntime, type SomaRuntimeSources } from '../runtime.svelte';
import { SomaNoContextError } from '../errors';
// ── Soma class ──────────────────────────────────────────────────────────────
const _ctx = new Context<Soma>('Soma');
export interface SomaOptions {
/** Override portal target for this scope */
portalTo?: string | HTMLElement;
}
/**
* Soma — component runtime scope.
*
* Reads `ActiveUix` from Svelte context (`getActiveUix()`) and exposes
* services to soma components through a stable adapter surface. Soma
* components import local internals through relative paths, NEVER from
* `$active-app` directly — the UIX layer is decoupled from the App
* composition layer by design.
*
* The adapter exposes the services soma actually consumes: langs, DOM,
* events, format slices and prefs view.
*
* Nestable: child <Soma portalTo="#modals"> overrides parent.
*/
export class Soma {
readonly uix: ActiveUix;
readonly portalTo: string | HTMLElement | undefined;
readonly #prefs: ActiveUixPrefsView;
private constructor(uix: ActiveUix, opts: SomaOptions = {}) {
this.uix = uix;
const parent = _ctx.getOr(null as unknown as Soma) as Soma | null;
this.portalTo = opts.portalTo ?? parent?.portalTo ?? uix.portal;
this.#prefs = createActiveUixPrefsView(uix.prefs);
}
// ── Service accessors (delegate to ActiveUix) ──────────────────────
get langs(): ActiveLangs {
return this.uix.langs;
}
get dom(): ActiveDom {
return this.uix.dom;
}
get events(): EngineSemantic | undefined {
return this.uix.events;
}
get nums(): ActiveNumbers | undefined {
return this.uix.format?.numbers;
}
get money(): ActiveCurrency | undefined {
return this.uix.format?.currency;
}
get dates(): ActiveDates | undefined {
return this.uix.format?.dates;
}
get units(): ActiveUnits | undefined {
return this.uix.format?.units;
}
get prefs(): ActiveUixPrefsView {
return this.#prefs;
}
get logger(): EngineLogger {
return this.uix.logger;
}
runtime<M extends Morfo>(
morfo: M,
sources: Omit<SomaRuntimeSources, 'dom' | 'eventEngine'>
): SomaRuntime {
return createSomaRuntime(morfo, {
dom: this.dom,
eventEngine: this.events,
translate: (key) => this.langs.ts(key),
// Wire the shared aria-live region to morfo events that declare
// `a11ySemantic.requiresLiveRegion`. The SomaRuntime calls this
// only when the caller passes a `message` via TriggerOptions.
announce: (message, priority) => this.uix.announce(message, priority),
...sources
});
}
// ── Static methods ──────────────────────────────────────────────────
static create(opts: SomaOptions = {}): Soma {
const uix = getActiveUix();
const instance = new Soma(uix, opts);
return _ctx.set(instance);
}
static get(): Soma | undefined {
return _ctx.getOr(undefined) as Soma | undefined;
}
static require(): Soma {
const soma = _ctx.getOr(null as unknown as Soma);
if (!soma) throw new SomaNoContextError();
return soma;
}
}

Powered by TurnKey Linux.