|
|
|
|
@ -1,6 +1,5 @@
|
|
|
|
|
import { context, type WithRefOpts } from '../../provider';
|
|
|
|
|
import { createAttrs } from '$uix/morfo';
|
|
|
|
|
import { boolToEmptyStrOrUndef } from '$adom';
|
|
|
|
|
import { readableActive, state, type Active, type ActiveProps } from '$libs/reactive';
|
|
|
|
|
import type { SomaKeyboardEvent } from '../../types';
|
|
|
|
|
import { KEYS } from '../../keyboard';
|
|
|
|
|
@ -45,12 +44,19 @@ export class FeedProvider {
|
|
|
|
|
private constructor(opts: FeedOpts) {
|
|
|
|
|
this.opts = opts;
|
|
|
|
|
this.soma = Soma.require();
|
|
|
|
|
this.runtime = this.soma.runtime(feedMorfo, {});
|
|
|
|
|
this.runtime = this.soma.runtime(feedMorfo, {
|
|
|
|
|
props: {
|
|
|
|
|
busy: () => this.opts.busy.current,
|
|
|
|
|
ariaLabel: () => this.resolvedAriaLabel.current,
|
|
|
|
|
ariaLabelledby: () => this.opts.ariaLabelledby.current
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
this.runtimePart = this.runtime.part('provider', {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
ref: opts.ref,
|
|
|
|
|
owner: this,
|
|
|
|
|
context: FeedProvider.ctx
|
|
|
|
|
context: FeedProvider.ctx,
|
|
|
|
|
syncAttrs: true
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -132,12 +138,7 @@ export class FeedProvider {
|
|
|
|
|
|
|
|
|
|
readonly props = $derived.by(() =>
|
|
|
|
|
this.runtimePart.assert({
|
|
|
|
|
...this.runtimePart.props,
|
|
|
|
|
role: 'feed' as const,
|
|
|
|
|
'aria-label': this.resolvedAriaLabel.current,
|
|
|
|
|
'aria-labelledby': this.opts.ariaLabelledby.current,
|
|
|
|
|
'aria-busy': this.opts.busy.current ? true : undefined,
|
|
|
|
|
'data-busy': boolToEmptyStrOrUndef(this.opts.busy.current)
|
|
|
|
|
...this.runtimePart.props
|
|
|
|
|
} as const)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
@ -176,17 +177,21 @@ export class FeedThreadProvider {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
ref: opts.ref,
|
|
|
|
|
owner: this,
|
|
|
|
|
context: FeedThreadProvider.ctx
|
|
|
|
|
context: FeedThreadProvider.ctx,
|
|
|
|
|
props: {
|
|
|
|
|
level: () => this.level
|
|
|
|
|
},
|
|
|
|
|
parts: {
|
|
|
|
|
'article-title': () => this.parentArticle.titleId.current || undefined
|
|
|
|
|
},
|
|
|
|
|
syncAttrs: true
|
|
|
|
|
});
|
|
|
|
|
this.level = this.parentArticle.level + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
readonly props = $derived.by(() =>
|
|
|
|
|
this.runtimePart.assert({
|
|
|
|
|
...this.runtimePart.props,
|
|
|
|
|
role: 'feed' as const,
|
|
|
|
|
'aria-labelledby': this.parentArticle.titleId.current || undefined,
|
|
|
|
|
'data-level': this.level
|
|
|
|
|
...this.runtimePart.props
|
|
|
|
|
} as const)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
@ -227,7 +232,17 @@ export class FeedArticleProvider {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
ref: opts.ref,
|
|
|
|
|
owner: this,
|
|
|
|
|
context: FeedArticleProvider.ctx
|
|
|
|
|
context: FeedArticleProvider.ctx,
|
|
|
|
|
props: {
|
|
|
|
|
posinset: () => this.resolvedPosinset,
|
|
|
|
|
setsize: () => this.resolvedSetsize,
|
|
|
|
|
level: () => this.level
|
|
|
|
|
},
|
|
|
|
|
parts: {
|
|
|
|
|
'article-title': () => this.titleId.current || undefined,
|
|
|
|
|
'article-description': () => this.descriptionId.current || undefined
|
|
|
|
|
},
|
|
|
|
|
syncAttrs: true
|
|
|
|
|
});
|
|
|
|
|
this.level = this.thread ? this.thread.level : 1;
|
|
|
|
|
}
|
|
|
|
|
@ -275,21 +290,13 @@ export class FeedArticleProvider {
|
|
|
|
|
this.provider.handleArticleKeydown(e as SomaKeyboardEvent<HTMLElement>);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
readonly props = $derived.by(() => {
|
|
|
|
|
const posinset = this.resolvedPosinset;
|
|
|
|
|
return this.runtimePart.assert({
|
|
|
|
|
readonly props = $derived.by(() =>
|
|
|
|
|
this.runtimePart.assert({
|
|
|
|
|
...this.runtimePart.props,
|
|
|
|
|
role: 'article' as const,
|
|
|
|
|
tabindex: 0,
|
|
|
|
|
'aria-posinset': posinset,
|
|
|
|
|
'aria-setsize': this.resolvedSetsize,
|
|
|
|
|
'aria-labelledby': this.titleId.current || undefined,
|
|
|
|
|
'aria-describedby': this.descriptionId.current || undefined,
|
|
|
|
|
'data-posinset': posinset,
|
|
|
|
|
'data-level': this.level,
|
|
|
|
|
onkeydown: this.onkeydown
|
|
|
|
|
} as const);
|
|
|
|
|
});
|
|
|
|
|
} as const)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── ArticleTitle ───────────────────────────────────────────────────────────
|
|
|
|
|
@ -311,7 +318,11 @@ export class FeedArticleTitleProvider {
|
|
|
|
|
this.runtimePart = this.article.provider.runtime.part('article-title', {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
ref: opts.ref,
|
|
|
|
|
owner: this
|
|
|
|
|
owner: this,
|
|
|
|
|
props: {
|
|
|
|
|
level: () => this.resolvedLevel
|
|
|
|
|
},
|
|
|
|
|
syncAttrs: true
|
|
|
|
|
});
|
|
|
|
|
// A30: direct assignment.
|
|
|
|
|
this.article.titleId.current = opts.id.current;
|
|
|
|
|
@ -330,9 +341,7 @@ export class FeedArticleTitleProvider {
|
|
|
|
|
|
|
|
|
|
readonly props = $derived.by(() =>
|
|
|
|
|
this.runtimePart.assert({
|
|
|
|
|
...this.runtimePart.props,
|
|
|
|
|
role: 'heading' as const,
|
|
|
|
|
'aria-level': this.resolvedLevel
|
|
|
|
|
...this.runtimePart.props
|
|
|
|
|
} as const)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
@ -404,7 +413,15 @@ export class FeedSentinelProvider {
|
|
|
|
|
this.soma = Soma.require();
|
|
|
|
|
this.feed = FeedProvider.get();
|
|
|
|
|
this.runtime = this.feed?.runtime ?? this.soma.runtime(feedMorfo, {});
|
|
|
|
|
this.runtimePart = this.runtime.part('sentinel', { id: opts.id, ref: opts.ref, owner: this });
|
|
|
|
|
this.runtimePart = this.runtime.part('sentinel', {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
ref: opts.ref,
|
|
|
|
|
owner: this,
|
|
|
|
|
props: {
|
|
|
|
|
intersecting: () => this.intersecting
|
|
|
|
|
},
|
|
|
|
|
syncAttrs: true
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$effect(() => {
|
|
|
|
|
const el = opts.ref.current;
|
|
|
|
|
@ -438,9 +455,7 @@ export class FeedSentinelProvider {
|
|
|
|
|
|
|
|
|
|
readonly props = $derived.by(() =>
|
|
|
|
|
this.runtimePart.assert({
|
|
|
|
|
...this.runtimePart.props,
|
|
|
|
|
'aria-hidden': true as const,
|
|
|
|
|
'data-intersecting': boolToEmptyStrOrUndef(this.intersecting)
|
|
|
|
|
...this.runtimePart.props
|
|
|
|
|
} as const)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|