|
|
|
|
@ -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,9 +53,9 @@ export class ScrollAreaProvider {
|
|
|
|
|
|
|
|
|
|
private constructor(opts: ScrollAreaOpts) {
|
|
|
|
|
this.opts = opts;
|
|
|
|
|
this.runtimePart = Soma.require()
|
|
|
|
|
.runtime(scrollAreaMorfo, {})
|
|
|
|
|
.part('provider', {
|
|
|
|
|
this.soma = Soma.require();
|
|
|
|
|
this.runtime = this.soma.runtime(scrollAreaMorfo, {});
|
|
|
|
|
this.runtimePart = this.runtime.part('provider', {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
ref: opts.ref,
|
|
|
|
|
owner: this,
|
|
|
|
|
@ -169,9 +169,8 @@ export class ScrollAreaViewportProvider {
|
|
|
|
|
|
|
|
|
|
private constructor(opts: ScrollAreaViewportOpts) {
|
|
|
|
|
this.opts = opts;
|
|
|
|
|
this.runtimePart = Soma.require()
|
|
|
|
|
.runtime(scrollAreaMorfo, {})
|
|
|
|
|
.part('viewport', {
|
|
|
|
|
this.provider = ScrollAreaProvider.require();
|
|
|
|
|
this.runtimePart = this.provider.runtime.part('viewport', {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
ref: opts.ref,
|
|
|
|
|
owner: this,
|
|
|
|
|
@ -179,7 +178,6 @@ export class ScrollAreaViewportProvider {
|
|
|
|
|
this.provider.setupViewport(el);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
this.provider = ScrollAreaProvider.require();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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', {
|
|
|
|
|
this.provider = ScrollAreaProvider.require();
|
|
|
|
|
this.runtimePart = this.provider.runtime.part('scrollbar', {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
ref: opts.ref,
|
|
|
|
|
owner: this,
|
|
|
|
|
context: ScrollAreaScrollbarProvider.ctx
|
|
|
|
|
});
|
|
|
|
|
this.provider = ScrollAreaProvider.require();
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
|