You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
130 lines
4.0 KiB
130 lines
4.0 KiB
import { type WithRefOpts } from '../../provider';
|
|
import type { SomaMouseEvent } from '../../types';
|
|
import type { ActiveProps } from '$libs/reactive';
|
|
import { Soma } from '../../core/soma.svelte';
|
|
import { DialogProvider } from '../dialog/dialog-provider.svelte';
|
|
import { ALERT_DIALOG_LANGS } from './langs';
|
|
|
|
/**
|
|
* AlertDialog is a thin specialisation of Dialog. The actual dialog state
|
|
* (open, nesting, presence, focus scope, scroll lock …) lives entirely in
|
|
* `DialogProvider` — the root `<AlertDialog.Provider>` simply forces
|
|
* `variant='alertdialog'` and `modal=true` on the underlying Dialog.
|
|
*
|
|
* The only parts that need their own providers are `Action` and `Cancel` —
|
|
* they emit dedicated `data-alert-dialog-action` / `data-alert-dialog-cancel`
|
|
* attributes so themes can style the primary/destructive vs safe buttons
|
|
* differently, and they wire `onclick` to close the dialog.
|
|
*/
|
|
import { alertDialogMorfo } from '../../../morfo/components/alert-dialog';
|
|
import type { SomaRuntime, SomaRuntimePart } from '../../runtime.svelte';
|
|
|
|
|
|
// ── Action button (primary / destructive) ─────────────────────────────────
|
|
|
|
interface AlertDialogActionOpts
|
|
extends WithRefOpts, ActiveProps<{ ariaLabel: string | undefined }> {}
|
|
|
|
export class AlertDialogActionProvider {
|
|
readonly opts: AlertDialogActionOpts;
|
|
readonly runtimePart: SomaRuntimePart;
|
|
static create(opts: AlertDialogActionOpts) {
|
|
return new AlertDialogActionProvider(opts);
|
|
}
|
|
|
|
readonly dialog: DialogProvider;
|
|
readonly soma: Soma;
|
|
readonly runtime: SomaRuntime;
|
|
|
|
private constructor(opts: AlertDialogActionOpts) {
|
|
this.opts = opts;
|
|
this.dialog = DialogProvider.require();
|
|
this.soma = Soma.require();
|
|
this.runtime = this.soma.runtime(alertDialogMorfo, {
|
|
props: {
|
|
ariaLabel: () => this.resolvedAriaLabel
|
|
}
|
|
});
|
|
this.runtimePart = this.runtime.part('action', {
|
|
id: opts.id,
|
|
ref: opts.ref,
|
|
owner: this,
|
|
syncAttrs: true
|
|
});
|
|
}
|
|
|
|
readonly resolvedAriaLabel = $derived.by(
|
|
() => this.opts.ariaLabel.current || this.soma.langs.ts(ALERT_DIALOG_LANGS.ACTION)
|
|
);
|
|
|
|
/**
|
|
* Resolved doctrinal intent of the parent AlertDialog. Exposed so the
|
|
* eidos `<AlertDialog.Action>` wrapper can forward it to its inner
|
|
* `<Button>` — a `risk`-flagged dialog produces a red Action automatically
|
|
* without needing a CSS ancestor cascade.
|
|
*/
|
|
readonly dialogIntent = $derived.by(
|
|
() => this.dialog.opts.intent.current ?? 'neutral'
|
|
);
|
|
|
|
readonly onclick = (_e: SomaMouseEvent<HTMLButtonElement>) => {
|
|
this.dialog.handleClose();
|
|
};
|
|
|
|
readonly props = $derived.by(() =>
|
|
this.runtimePart.assert({
|
|
...this.runtimePart.props,
|
|
onclick: this.onclick
|
|
} as const)
|
|
);
|
|
}
|
|
|
|
// ── Cancel button (secondary / safe) ───────────────────────────────────────
|
|
|
|
interface AlertDialogCancelOpts
|
|
extends WithRefOpts, ActiveProps<{ ariaLabel: string | undefined }> {}
|
|
|
|
export class AlertDialogCancelProvider {
|
|
readonly opts: AlertDialogCancelOpts;
|
|
readonly runtimePart: SomaRuntimePart;
|
|
static create(opts: AlertDialogCancelOpts) {
|
|
return new AlertDialogCancelProvider(opts);
|
|
}
|
|
|
|
readonly dialog: DialogProvider;
|
|
readonly soma: Soma;
|
|
readonly runtime: SomaRuntime;
|
|
|
|
private constructor(opts: AlertDialogCancelOpts) {
|
|
this.opts = opts;
|
|
this.dialog = DialogProvider.require();
|
|
this.soma = Soma.require();
|
|
this.runtime = this.soma.runtime(alertDialogMorfo, {
|
|
props: {
|
|
ariaLabel: () => this.resolvedAriaLabel
|
|
}
|
|
});
|
|
this.runtimePart = this.runtime.part('cancel', {
|
|
id: opts.id,
|
|
ref: opts.ref,
|
|
owner: this,
|
|
syncAttrs: true
|
|
});
|
|
}
|
|
|
|
readonly resolvedAriaLabel = $derived.by(
|
|
() => this.opts.ariaLabel.current || this.soma.langs.ts(ALERT_DIALOG_LANGS.CANCEL)
|
|
);
|
|
|
|
readonly onclick = (_e: SomaMouseEvent<HTMLButtonElement>) => {
|
|
this.dialog.handleClose();
|
|
};
|
|
|
|
readonly props = $derived.by(() =>
|
|
this.runtimePart.assert({
|
|
...this.runtimePart.props,
|
|
onclick: this.onclick
|
|
} as const)
|
|
);
|
|
}
|