Consolidate Soma overlay runtimes

active-uix
dev 5 months ago
parent cac7e89232
commit c7527fc107

@ -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<LinkPreviewProvider>('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);
}

@ -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<ScrollAreaProvider>('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<HTMLElement | null>(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);

@ -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<TooltipProvider>('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);
}

Loading…
Cancel
Save

Powered by TurnKey Linux.