dialog: allow disabling controls from rendering

This commit is contained in:
Elijah Duffy
2025-12-17 22:17:42 -08:00
parent 6630051d67
commit 3cdda64686
2 changed files with 82 additions and 56 deletions

View File

@@ -41,24 +41,29 @@
title: (title: string) => void;
}
type DialogControlButton = {
label?: string;
class?: ClassValue;
action?: (dialog: DialogAPI) => void;
};
/**
* Configures the default dialog controls.
*/
export type DialogControlOpts = {
cancel?: {
label?: string;
class?: ClassValue;
action?: (dialog: DialogAPI) => void;
};
ok?: {
label?: string;
class?: ClassValue;
action?: (dialog: DialogAPI) => void;
};
close?: {
class?: ClassValue;
action?: (dialog: DialogAPI) => void;
export type DialogControls = {
cancel?: DialogControlButton | null;
ok?: DialogControlButton | null;
close?: Omit<DialogControlButton, 'label'> | null;
};
const defaultDialogControls: DialogControls = {
cancel: {
label: 'Cancel'
},
ok: {
label: 'OK'
},
close: {}
};
</script>
@@ -72,6 +77,7 @@
import { X } from 'phosphor-svelte';
import { ErrorMessage, type RawError } from './error';
import ErrorBox from './ErrorBox.svelte';
import { mergeOverrideObject } from './util';
interface Props {
open?: boolean;
@@ -80,7 +86,7 @@
size?: 'sm' | 'md' | 'lg' | 'max';
class?: ClassValue;
children?: Snippet;
controls?: Snippet | DialogControlOpts;
controls?: Snippet | DialogControls;
onopen?: (dialog: DialogAPI) => void;
onclose?: (dialog: DialogAPI) => void;
loading?: boolean;
@@ -95,7 +101,7 @@
size = 'sm',
class: classValue,
children,
controls,
controls: rawControls = defaultDialogControls,
onopen,
onclose,
loading = $bindable(false),
@@ -103,6 +109,12 @@
disabled = $bindable(false)
}: Props = $props();
let controls = $derived(
typeof rawControls === 'function'
? rawControls
: mergeOverrideObject(defaultDialogControls, rawControls)
);
let dialogContainer = $state<HTMLDivElement | null>(null);
let error = $state<ErrorMessage | null>(null);
@@ -192,8 +204,10 @@
{#if children}{@render children()}{:else}Dialog is empty{/if}
<!-- Dialog Controls -->
<div class="mt-6 flex justify-end gap-4">
{#if controls && typeof controls === 'function'}{@render controls()}{:else}
{#if controls.cancel !== null}
<Button
class={controls?.cancel?.class}
onclick={() => {
@@ -207,6 +221,8 @@
>
{controls?.cancel?.label || 'Cancel'}
</Button>
{/if}
{#if controls.ok !== null}
<Button
class={controls?.ok?.class}
onclick={() => {
@@ -222,19 +238,28 @@
{controls?.ok?.label || 'OK'}
</Button>
{/if}
{/if}
</div>
<!-- Close Button -->
{#if typeof controls === 'function' || controls?.close !== null}
<button
type="button"
aria-label="close"
class="absolute top-4 right-4 inline-flex cursor-pointer items-center
justify-center disabled:cursor-not-allowed disabled:opacity-50"
onclick={() => {
if (!frozen) open = false;
if (typeof controls !== 'function' && controls?.close?.action) {
controls?.close?.action?.(dialogAPI);
} else if (!frozen) {
open = false;
}
}}
disabled={frozen}
>
<X size="1.5em" weight="bold" />
</button>
{/if}
</div>
</div>
{/snippet}

View File

@@ -523,7 +523,8 @@
dialog.close();
alert('Dialog submitted!');
}
}
},
cancel: null
}}
onopen={(dialog) => {
dialog.error('Example error message!');