|
|
|
@ -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)
|
|
|
|
|