From c7527fc107d1c5e3d619d4b846d1803a45a91c3b Mon Sep 17 00:00:00 2001 From: dev Date: Fri, 15 May 2026 02:33:22 +0200 Subject: [PATCH] Consolidate Soma overlay runtimes --- .../link-preview-provider.svelte.ts | 66 ++++++------- .../scroll-area-provider.svelte.ts | 74 +++++++-------- .../tooltip/tooltip-provider.svelte.ts | 95 ++++++++++--------- 3 files changed, 120 insertions(+), 115 deletions(-) diff --git a/src/uix/soma/components/link-preview/link-preview-provider.svelte.ts b/src/uix/soma/components/link-preview/link-preview-provider.svelte.ts index f1f5025af..068184fa1 100644 --- a/src/uix/soma/components/link-preview/link-preview-provider.svelte.ts +++ b/src/uix/soma/components/link-preview/link-preview-provider.svelte.ts @@ -19,7 +19,7 @@ import { import { linkPreviewMorfo } from '../../../morfo/components/link-preview'; import { Soma } from '../../core/soma.svelte'; -import type { SomaRuntimePart } from '../../runtime.svelte'; +import type { SomaRuntime, SomaRuntimePart } from '../../runtime.svelte'; // ── Root ───────────────────────────────────────────────────────────────────── @@ -38,6 +38,8 @@ interface LinkPreviewOpts export class LinkPreviewProvider { readonly opts: LinkPreviewOpts; readonly runtimePart: SomaRuntimePart; + readonly soma: Soma; + readonly runtime: SomaRuntime; static readonly ctx = context('LinkPreview'); static get(): LinkPreviewProvider | undefined { return this.ctx.getOr(undefined) as LinkPreviewProvider | undefined; @@ -63,14 +65,14 @@ export class LinkPreviewProvider { private constructor(opts: LinkPreviewOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(linkPreviewMorfo, {}) - .part('provider', { - id: opts.id, - ref: opts.ref, - owner: this, - context: LinkPreviewProvider.ctx - }); + this.soma = Soma.require(); + this.runtime = this.soma.runtime(linkPreviewMorfo, {}); + this.runtimePart = this.runtime.part('provider', { + id: opts.id, + ref: opts.ref, + owner: this, + context: LinkPreviewProvider.ctx + }); this.floatingProvider = FloatingProvider.create(); @@ -164,17 +166,15 @@ export class LinkPreviewTriggerProvider { private constructor(opts: LinkPreviewTriggerOpts) { // A30: mirror trigger element to provider via `onRefChange`. this.opts = opts; - this.runtimePart = Soma.require() - .runtime(linkPreviewMorfo, {}) - .part('trigger', { - id: opts.id, - ref: opts.ref, - owner: this, - onRefChange: (el) => { - this.provider.triggerRef.current = el; - } - }); this.provider = LinkPreviewProvider.require(); + this.runtimePart = this.provider.runtime.part('trigger', { + id: opts.id, + ref: opts.ref, + owner: this, + onRefChange: (el) => { + this.provider.triggerRef.current = el; + } + }); this.provider.triggerId.current = opts.id.current; FloatingAnchor.create({ @@ -245,17 +245,15 @@ export class LinkPreviewContentProvider { private constructor(opts: LinkPreviewContentOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(linkPreviewMorfo, {}) - .part('content', { - id: opts.id, - ref: opts.ref, - owner: this, - onRefChange: (el) => { - this.provider.setContentRef(el); - } - }); this.provider = LinkPreviewProvider.require(); + this.runtimePart = this.provider.runtime.part('content', { + id: opts.id, + ref: opts.ref, + owner: this, + onRefChange: (el) => { + this.provider.setContentRef(el); + } + }); this.provider.contentId.current = opts.id.current; this.floating = FloatingContent.create({ @@ -344,6 +342,7 @@ interface LinkPreviewArrowOpts extends WithRefOpts {} export class LinkPreviewArrowProvider { readonly opts: LinkPreviewArrowOpts; readonly runtimePart: SomaRuntimePart; + readonly provider: LinkPreviewProvider; static create(opts: LinkPreviewArrowOpts) { return new LinkPreviewArrowProvider(opts); } @@ -352,9 +351,12 @@ export class LinkPreviewArrowProvider { private constructor(opts: LinkPreviewArrowOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(linkPreviewMorfo, {}) - .part('arrow', { id: opts.id, ref: opts.ref, owner: this }); + this.provider = LinkPreviewProvider.require(); + this.runtimePart = this.provider.runtime.part('arrow', { + id: opts.id, + ref: opts.ref, + owner: this + }); this.floatingArrow = FloatingArrow.create(opts); } diff --git a/src/uix/soma/components/scroll-area/scroll-area-provider.svelte.ts b/src/uix/soma/components/scroll-area/scroll-area-provider.svelte.ts index 3c9b3ae04..c621b3982 100644 --- a/src/uix/soma/components/scroll-area/scroll-area-provider.svelte.ts +++ b/src/uix/soma/components/scroll-area/scroll-area-provider.svelte.ts @@ -7,7 +7,7 @@ import { SCROLL_AREA_LANGS } from './langs'; import { isBrowser } from '$adom'; import { scrollAreaMorfo } from '../../../morfo/components/scroll-area'; -import type { SomaRuntimePart } from '../../runtime.svelte'; +import type { SomaRuntime, SomaRuntimePart } from '../../runtime.svelte'; export type ScrollAreaType = 'hover' | 'scroll' | 'auto' | 'always'; @@ -26,6 +26,8 @@ interface ScrollAreaOpts export class ScrollAreaProvider { readonly opts: ScrollAreaOpts; readonly runtimePart: SomaRuntimePart; + readonly soma: Soma; + readonly runtime: SomaRuntime; static readonly ctx = context('ScrollArea'); static get(): ScrollAreaProvider | undefined { return this.ctx.getOr(undefined) as ScrollAreaProvider | undefined; @@ -38,8 +40,6 @@ export class ScrollAreaProvider { return new ScrollAreaProvider(opts); } - readonly soma = Soma.get(); - // Viewport tracking viewportRef = state(null); contentWidth = $state(0); @@ -53,14 +53,14 @@ export class ScrollAreaProvider { private constructor(opts: ScrollAreaOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(scrollAreaMorfo, {}) - .part('provider', { - id: opts.id, - ref: opts.ref, - owner: this, - context: ScrollAreaProvider.ctx - }); + this.soma = Soma.require(); + this.runtime = this.soma.runtime(scrollAreaMorfo, {}); + this.runtimePart = this.runtime.part('provider', { + id: opts.id, + ref: opts.ref, + owner: this, + context: ScrollAreaProvider.ctx + }); // Cleanup resize observer on unmount $effect(() => { @@ -169,17 +169,15 @@ export class ScrollAreaViewportProvider { private constructor(opts: ScrollAreaViewportOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(scrollAreaMorfo, {}) - .part('viewport', { - id: opts.id, - ref: opts.ref, - owner: this, - onRefChange: (el) => { - this.provider.setupViewport(el); - } - }); this.provider = ScrollAreaProvider.require(); + this.runtimePart = this.provider.runtime.part('viewport', { + id: opts.id, + ref: opts.ref, + owner: this, + onRefChange: (el) => { + this.provider.setupViewport(el); + } + }); } readonly onscroll = (e: Event) => { @@ -241,15 +239,13 @@ export class ScrollAreaScrollbarProvider { private constructor(opts: ScrollAreaScrollbarOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(scrollAreaMorfo, {}) - .part('scrollbar', { - id: opts.id, - ref: opts.ref, - owner: this, - context: ScrollAreaScrollbarProvider.ctx - }); this.provider = ScrollAreaProvider.require(); + this.runtimePart = this.provider.runtime.part('scrollbar', { + id: opts.id, + ref: opts.ref, + owner: this, + context: ScrollAreaScrollbarProvider.ctx + }); // Skip initial-mount flash — defer mounted flag to next frame requestAnimationFrame(() => { @@ -387,8 +383,8 @@ export class ScrollAreaScrollbarProvider { 'aria-valuemax': 100, 'aria-orientation': this.opts.orientation.current, 'aria-label': this.isVertical - ? this.provider.soma?.langs.ts(SCROLL_AREA_LANGS.SCROLLBAR_VERTICAL) - : this.provider.soma?.langs.ts(SCROLL_AREA_LANGS.SCROLLBAR_HORIZONTAL), + ? this.provider.soma.langs.ts(SCROLL_AREA_LANGS.SCROLLBAR_VERTICAL) + : this.provider.soma.langs.ts(SCROLL_AREA_LANGS.SCROLLBAR_HORIZONTAL), tabindex: -1, 'data-state': this.shouldShow ? 'visible' : 'hidden', 'data-orientation': this.opts.orientation.current, @@ -424,11 +420,13 @@ export class ScrollAreaThumbProvider { private constructor(opts: ScrollAreaThumbOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(scrollAreaMorfo, {}) - .part('thumb', { id: opts.id, ref: opts.ref, owner: this }); this.scrollbar = ScrollAreaScrollbarProvider.require(); this.provider = this.scrollbar.provider; + this.runtimePart = this.provider.runtime.part('thumb', { + id: opts.id, + ref: opts.ref, + owner: this + }); } private get isVertical(): boolean { @@ -561,10 +559,12 @@ export class ScrollAreaCornerProvider { private constructor(opts: ScrollAreaCornerOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(scrollAreaMorfo, {}) - .part('corner', { id: opts.id, ref: opts.ref, owner: this }); this.provider = ScrollAreaProvider.require(); + this.runtimePart = this.provider.runtime.part('corner', { + id: opts.id, + ref: opts.ref, + owner: this + }); } readonly shouldShow = $derived.by(() => this.provider.hasOverflowX && this.provider.hasOverflowY); diff --git a/src/uix/soma/components/tooltip/tooltip-provider.svelte.ts b/src/uix/soma/components/tooltip/tooltip-provider.svelte.ts index 4c509824f..49692f552 100644 --- a/src/uix/soma/components/tooltip/tooltip-provider.svelte.ts +++ b/src/uix/soma/components/tooltip/tooltip-provider.svelte.ts @@ -30,7 +30,7 @@ import { import { tooltipMorfo } from '../../../morfo/components/tooltip'; import { Soma } from '../../core/soma.svelte'; -import type { SomaRuntimePart } from '../../runtime.svelte'; +import type { SomaRuntime, SomaRuntimePart } from '../../runtime.svelte'; export type TooltipState = 'closed' | 'delayed-open' | 'instant-open'; @@ -93,6 +93,8 @@ interface TooltipOpts export class TooltipProvider { readonly opts: TooltipOpts; readonly runtimePart: SomaRuntimePart; + readonly soma: Soma; + readonly runtime: SomaRuntime; static readonly ctx = context('Tooltip'); static get(): TooltipProvider | undefined { return this.ctx.getOr(undefined) as TooltipProvider | undefined; @@ -137,9 +139,14 @@ export class TooltipProvider { private constructor(opts: TooltipOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(tooltipMorfo, {}) - .part('provider', { id: opts.id, ref: opts.ref, owner: this, context: TooltipProvider.ctx }); + this.soma = Soma.require(); + this.runtime = this.soma.runtime(tooltipMorfo, {}); + this.runtimePart = this.runtime.part('provider', { + id: opts.id, + ref: opts.ref, + owner: this, + context: TooltipProvider.ctx + }); this.group = TooltipGroupProvider.get(); this.floatingProvider = FloatingProvider.create(); @@ -257,27 +264,22 @@ export class TooltipTriggerProvider { // A30: mirror trigger element to provider via `onRefChange`. this.opts = opts; this.provider = TooltipProvider.require(); - this.runtimePart = Soma.require() - .runtime(tooltipMorfo, { - states: { - 'instant-open': () => this.provider.tooltipState - }, - parts: { - content: () => - this.provider.opts.open.current - ? this.provider.contentId.current || undefined - : undefined - } - }) - .part('trigger', { - id: opts.id, - ref: opts.ref, - owner: this, - onRefChange: (el) => { - this.provider.triggerRef.current = el; - }, - syncAttrs: true - }); + this.runtimePart = this.provider.runtime.part('trigger', { + id: opts.id, + ref: opts.ref, + states: { + 'instant-open': () => this.provider.tooltipState + }, + parts: { + content: () => + this.provider.opts.open.current ? this.provider.contentId.current || undefined : undefined + }, + owner: this, + onRefChange: (el) => { + this.provider.triggerRef.current = el; + }, + syncAttrs: true + }); this.provider.triggerId.current = opts.id.current; FloatingAnchor.create({ @@ -353,24 +355,21 @@ export class TooltipContentProvider { private constructor(opts: TooltipContentOpts) { this.opts = opts; this.provider = TooltipProvider.require(); - this.runtimePart = Soma.require() - .runtime(tooltipMorfo, { - states: { - 'instant-open': () => this.provider.tooltipState - }, - props: { - ariaLabel: () => this.opts.ariaLabel.current - } - }) - .part('content', { - id: opts.id, - ref: opts.ref, - owner: this, - onRefChange: (el) => { - this.provider.setContentRef(el); - }, - syncAttrs: true - }); + this.runtimePart = this.provider.runtime.part('content', { + id: opts.id, + ref: opts.ref, + states: { + 'instant-open': () => this.provider.tooltipState + }, + props: { + ariaLabel: () => this.opts.ariaLabel.current + }, + owner: this, + onRefChange: (el) => { + this.provider.setContentRef(el); + }, + syncAttrs: true + }); this.provider.contentId.current = opts.id.current; // Floating @@ -453,6 +452,7 @@ interface TooltipArrowOpts extends WithRefOpts {} export class TooltipArrowProvider { readonly opts: TooltipArrowOpts; readonly runtimePart: SomaRuntimePart; + readonly provider: TooltipProvider; static create(opts: TooltipArrowOpts) { return new TooltipArrowProvider(opts); } @@ -461,9 +461,12 @@ export class TooltipArrowProvider { private constructor(opts: TooltipArrowOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(tooltipMorfo, {}) - .part('arrow', { id: opts.id, ref: opts.ref, owner: this }); + this.provider = TooltipProvider.require(); + this.runtimePart = this.provider.runtime.part('arrow', { + id: opts.id, + ref: opts.ref, + owner: this + }); this.floatingArrow = FloatingArrow.create(opts); }