Consolidate Soma file upload runtime

active-uix
dev 5 months ago
parent 3097a209f0
commit e1df915add

@ -9,7 +9,7 @@ import { FieldProvider } from '../field/field-provider.svelte';
import type { FileRejection, FileRejectionReason } from './types'; import type { FileRejection, FileRejectionReason } from './types';
import { fileUploadMorfo } from '../../../morfo/components/file-upload'; import { fileUploadMorfo } from '../../../morfo/components/file-upload';
import type { SomaRuntimePart } from '../../runtime.svelte'; import type { SomaRuntime, SomaRuntimePart } from '../../runtime.svelte';
// ── Helpers ────────────────────────────────────────────────────────────────── // ── Helpers ──────────────────────────────────────────────────────────────────
@ -65,6 +65,8 @@ interface FileUploadOpts
export class FileUploadProvider { export class FileUploadProvider {
readonly opts: FileUploadOpts; readonly opts: FileUploadOpts;
readonly runtimePart: SomaRuntimePart; readonly runtimePart: SomaRuntimePart;
readonly soma: Soma;
readonly runtime: SomaRuntime;
static readonly ctx = context<FileUploadProvider>('FileUpload'); static readonly ctx = context<FileUploadProvider>('FileUpload');
static get(): FileUploadProvider | undefined { static get(): FileUploadProvider | undefined {
return this.ctx.getOr(undefined) as FileUploadProvider | undefined; return this.ctx.getOr(undefined) as FileUploadProvider | undefined;
@ -77,7 +79,6 @@ export class FileUploadProvider {
return new FileUploadProvider(opts); return new FileUploadProvider(opts);
} }
readonly soma = Soma.get();
readonly field = FieldProvider.get(); readonly field = FieldProvider.get();
/** Set by HiddenInput so Dropzone/Trigger/Label can target it. */ /** Set by HiddenInput so Dropzone/Trigger/Label can target it. */
@ -87,9 +88,9 @@ export class FileUploadProvider {
private constructor(opts: FileUploadOpts) { private constructor(opts: FileUploadOpts) {
this.opts = opts; this.opts = opts;
this.runtimePart = Soma.require() this.soma = Soma.require();
.runtime(fileUploadMorfo, {}) this.runtime = this.soma.runtime(fileUploadMorfo, {});
.part('provider', { this.runtimePart = this.runtime.part('provider', {
id: opts.id, id: opts.id,
ref: opts.ref, ref: opts.ref,
owner: this, owner: this,
@ -238,10 +239,12 @@ export class FileUploadLabelProvider {
private constructor(opts: FileUploadLabelOpts) { private constructor(opts: FileUploadLabelOpts) {
this.opts = opts; this.opts = opts;
this.runtimePart = Soma.require()
.runtime(fileUploadMorfo, {})
.part('label', { id: opts.id, ref: opts.ref, owner: this });
this.provider = FileUploadProvider.require(); this.provider = FileUploadProvider.require();
this.runtimePart = this.provider.runtime.part('label', {
id: opts.id,
ref: opts.ref,
owner: this
});
} }
readonly props = $derived.by(() => readonly props = $derived.by(() =>
@ -269,10 +272,12 @@ export class FileUploadDropzoneProvider {
private constructor(opts: FileUploadDropzoneOpts) { private constructor(opts: FileUploadDropzoneOpts) {
this.opts = opts; this.opts = opts;
this.runtimePart = Soma.require()
.runtime(fileUploadMorfo, {})
.part('dropzone', { id: opts.id, ref: opts.ref, owner: this });
this.provider = FileUploadProvider.require(); this.provider = FileUploadProvider.require();
this.runtimePart = this.provider.runtime.part('dropzone', {
id: opts.id,
ref: opts.ref,
owner: this
});
} }
readonly onclick = () => { readonly onclick = () => {
@ -325,7 +330,7 @@ export class FileUploadDropzoneProvider {
...this.runtimePart.props, ...this.runtimePart.props,
role: 'button' as const, role: 'button' as const,
tabindex: this.provider.isDisabled ? -1 : 0, tabindex: this.provider.isDisabled ? -1 : 0,
'aria-label': this.provider.soma?.langs.ts(FILE_UPLOAD_LANGS.DROPZONE), 'aria-label': this.provider.soma.langs.ts(FILE_UPLOAD_LANGS.DROPZONE),
'aria-disabled': boolToStr(this.provider.isDisabled), 'aria-disabled': boolToStr(this.provider.isDisabled),
'data-disabled': boolToEmptyStrOrUndef(this.provider.isDisabled), 'data-disabled': boolToEmptyStrOrUndef(this.provider.isDisabled),
'data-dragging': boolToEmptyStrOrUndef(this.dragging), 'data-dragging': boolToEmptyStrOrUndef(this.dragging),
@ -355,10 +360,12 @@ export class FileUploadTriggerProvider {
private constructor(opts: FileUploadTriggerOpts) { private constructor(opts: FileUploadTriggerOpts) {
this.opts = opts; this.opts = opts;
this.runtimePart = Soma.require()
.runtime(fileUploadMorfo, {})
.part('trigger', { id: opts.id, ref: opts.ref, owner: this });
this.provider = FileUploadProvider.require(); this.provider = FileUploadProvider.require();
this.runtimePart = this.provider.runtime.part('trigger', {
id: opts.id,
ref: opts.ref,
owner: this
});
} }
readonly onclick = (e: SomaMouseEvent<HTMLButtonElement>) => { readonly onclick = (e: SomaMouseEvent<HTMLButtonElement>) => {
@ -372,7 +379,7 @@ export class FileUploadTriggerProvider {
this.runtimePart.assert({ this.runtimePart.assert({
...this.runtimePart.props, ...this.runtimePart.props,
type: 'button' as const, type: 'button' as const,
'aria-label': this.provider.soma?.langs.ts(FILE_UPLOAD_LANGS.TRIGGER), 'aria-label': this.provider.soma.langs.ts(FILE_UPLOAD_LANGS.TRIGGER),
'aria-disabled': boolToStr(this.provider.isDisabled), 'aria-disabled': boolToStr(this.provider.isDisabled),
disabled: this.provider.isDisabled || undefined, disabled: this.provider.isDisabled || undefined,
'data-disabled': boolToEmptyStrOrUndef(this.provider.isDisabled), 'data-disabled': boolToEmptyStrOrUndef(this.provider.isDisabled),
@ -400,9 +407,8 @@ export class FileUploadHiddenInputProvider {
// child→parent reactive edge the old `$effect` created was pure // child→parent reactive edge the old `$effect` created was pure
// overhead. // overhead.
this.opts = opts; this.opts = opts;
this.runtimePart = Soma.require() this.provider = FileUploadProvider.require();
.runtime(fileUploadMorfo, {}) this.runtimePart = this.provider.runtime.part('hidden-input', {
.part('hidden-input', {
id: opts.id, id: opts.id,
ref: opts.ref, ref: opts.ref,
owner: this, owner: this,
@ -410,7 +416,6 @@ export class FileUploadHiddenInputProvider {
this.provider.hiddenInputRef.current = el as HTMLInputElement | null; this.provider.hiddenInputRef.current = el as HTMLInputElement | null;
} }
}); });
this.provider = FileUploadProvider.require();
this.provider.hiddenInputId.current = opts.id.current; this.provider.hiddenInputId.current = opts.id.current;
} }
@ -454,17 +459,19 @@ export class FileUploadFileListProvider {
private constructor(opts: FileUploadFileListOpts) { private constructor(opts: FileUploadFileListOpts) {
this.opts = opts; this.opts = opts;
this.runtimePart = Soma.require()
.runtime(fileUploadMorfo, {})
.part('file-list', { id: opts.id, ref: opts.ref, owner: this });
this.provider = FileUploadProvider.require(); this.provider = FileUploadProvider.require();
this.runtimePart = this.provider.runtime.part('file-list', {
id: opts.id,
ref: opts.ref,
owner: this
});
} }
readonly props = $derived.by(() => readonly props = $derived.by(() =>
this.runtimePart.assert({ this.runtimePart.assert({
...this.runtimePart.props, ...this.runtimePart.props,
role: 'list' as const, role: 'list' as const,
'aria-label': this.provider.soma?.langs.ts(FILE_UPLOAD_LANGS.LIST) 'aria-label': this.provider.soma.langs.ts(FILE_UPLOAD_LANGS.LIST)
} as const) } as const)
); );
} }
@ -492,15 +499,13 @@ export class FileUploadItemProvider {
private constructor(opts: FileUploadItemOpts) { private constructor(opts: FileUploadItemOpts) {
this.opts = opts; this.opts = opts;
this.runtimePart = Soma.require() this.provider = FileUploadProvider.require();
.runtime(fileUploadMorfo, {}) this.runtimePart = this.provider.runtime.part('item', {
.part('item', {
id: opts.id, id: opts.id,
ref: opts.ref, ref: opts.ref,
owner: this, owner: this,
context: FileUploadItemProvider.ctx context: FileUploadItemProvider.ctx
}); });
this.provider = FileUploadProvider.require();
} }
readonly snippetProps = $derived.by(() => ({ readonly snippetProps = $derived.by(() => ({
@ -533,10 +538,12 @@ export class FileUploadItemNameProvider {
private constructor(opts: FileUploadItemNameOpts) { private constructor(opts: FileUploadItemNameOpts) {
this.opts = opts; this.opts = opts;
this.runtimePart = Soma.require()
.runtime(fileUploadMorfo, {})
.part('item-name', { id: opts.id, ref: opts.ref, owner: this });
this.item = FileUploadItemProvider.require(); this.item = FileUploadItemProvider.require();
this.runtimePart = this.item.provider.runtime.part('item-name', {
id: opts.id,
ref: opts.ref,
owner: this
});
} }
readonly defaultContent = $derived.by(() => this.item.opts.file.current.name); readonly defaultContent = $derived.by(() => this.item.opts.file.current.name);
@ -561,10 +568,12 @@ export class FileUploadItemSizeProvider {
private constructor(opts: FileUploadItemSizeOpts) { private constructor(opts: FileUploadItemSizeOpts) {
this.opts = opts; this.opts = opts;
this.runtimePart = Soma.require()
.runtime(fileUploadMorfo, {})
.part('item-size', { id: opts.id, ref: opts.ref, owner: this });
this.item = FileUploadItemProvider.require(); this.item = FileUploadItemProvider.require();
this.runtimePart = this.item.provider.runtime.part('item-size', {
id: opts.id,
ref: opts.ref,
owner: this
});
} }
readonly defaultContent = $derived.by(() => formatBytes(this.item.opts.file.current.size)); readonly defaultContent = $derived.by(() => formatBytes(this.item.opts.file.current.size));
@ -592,10 +601,12 @@ export class FileUploadItemPreviewProvider {
private constructor(opts: FileUploadItemPreviewOpts) { private constructor(opts: FileUploadItemPreviewOpts) {
this.opts = opts; this.opts = opts;
this.runtimePart = Soma.require()
.runtime(fileUploadMorfo, {})
.part('item-preview', { id: opts.id, ref: opts.ref, owner: this });
this.item = FileUploadItemProvider.require(); this.item = FileUploadItemProvider.require();
this.runtimePart = this.item.provider.runtime.part('item-preview', {
id: opts.id,
ref: opts.ref,
owner: this
});
$effect(() => { $effect(() => {
if (!this.matches) { if (!this.matches) {
@ -642,10 +653,12 @@ export class FileUploadItemRemoveProvider {
private constructor(opts: FileUploadItemRemoveOpts) { private constructor(opts: FileUploadItemRemoveOpts) {
this.opts = opts; this.opts = opts;
this.runtimePart = Soma.require()
.runtime(fileUploadMorfo, {})
.part('item-remove', { id: opts.id, ref: opts.ref, owner: this });
this.item = FileUploadItemProvider.require(); this.item = FileUploadItemProvider.require();
this.runtimePart = this.item.provider.runtime.part('item-remove', {
id: opts.id,
ref: opts.ref,
owner: this
});
} }
readonly onclick = (_e: SomaMouseEvent<HTMLButtonElement>) => { readonly onclick = (_e: SomaMouseEvent<HTMLButtonElement>) => {
@ -659,7 +672,7 @@ export class FileUploadItemRemoveProvider {
type: 'button' as const, type: 'button' as const,
disabled: this.item.provider.isDisabled || undefined, disabled: this.item.provider.isDisabled || undefined,
'aria-label': [ 'aria-label': [
this.item.provider.soma?.langs.ts(FILE_UPLOAD_LANGS.REMOVE), this.item.provider.soma.langs.ts(FILE_UPLOAD_LANGS.REMOVE),
this.item.opts.file.current.name this.item.opts.file.current.name
] ]
.filter(Boolean) .filter(Boolean)
@ -685,10 +698,12 @@ export class FileUploadItemProgressProvider {
private constructor(opts: FileUploadItemProgressOpts) { private constructor(opts: FileUploadItemProgressOpts) {
this.opts = opts; this.opts = opts;
this.runtimePart = Soma.require()
.runtime(fileUploadMorfo, {})
.part('item-progress', { id: opts.id, ref: opts.ref, owner: this });
this.item = FileUploadItemProvider.require(); this.item = FileUploadItemProvider.require();
this.runtimePart = this.item.provider.runtime.part('item-progress', {
id: opts.id,
ref: opts.ref,
owner: this
});
} }
readonly percent = $derived.by(() => Math.min(100, Math.max(0, this.opts.value.current ?? 0))); readonly percent = $derived.by(() => Math.min(100, Math.max(0, this.opts.value.current ?? 0)));
@ -721,10 +736,12 @@ export class FileUploadClearTriggerProvider {
private constructor(opts: FileUploadClearTriggerOpts) { private constructor(opts: FileUploadClearTriggerOpts) {
this.opts = opts; this.opts = opts;
this.runtimePart = Soma.require()
.runtime(fileUploadMorfo, {})
.part('clear-trigger', { id: opts.id, ref: opts.ref, owner: this });
this.provider = FileUploadProvider.require(); this.provider = FileUploadProvider.require();
this.runtimePart = this.provider.runtime.part('clear-trigger', {
id: opts.id,
ref: opts.ref,
owner: this
});
} }
readonly isDisabled = $derived.by(() => this.provider.isDisabled || this.provider.isEmpty); readonly isDisabled = $derived.by(() => this.provider.isDisabled || this.provider.isEmpty);
@ -739,7 +756,7 @@ export class FileUploadClearTriggerProvider {
...this.runtimePart.props, ...this.runtimePart.props,
type: 'button' as const, type: 'button' as const,
disabled: this.isDisabled || undefined, disabled: this.isDisabled || undefined,
'aria-label': this.provider.soma?.langs.ts(FILE_UPLOAD_LANGS.CLEAR), 'aria-label': this.provider.soma.langs.ts(FILE_UPLOAD_LANGS.CLEAR),
'data-disabled': boolToEmptyStrOrUndef(this.isDisabled), 'data-disabled': boolToEmptyStrOrUndef(this.isDisabled),
onclick: this.onclick onclick: this.onclick
} as const) } as const)

Loading…
Cancel
Save

Powered by TurnKey Linux.