115 lines
2.4 KiB
Svelte
115 lines
2.4 KiB
Svelte
<script lang="ts">
|
|
export let open = false;
|
|
export let title = "";
|
|
export let message = "";
|
|
export let confirmText = "OK";
|
|
export let cancelText = "Cancel";
|
|
export let showCancel = false;
|
|
export let tone: "default" | "danger" = "default";
|
|
export let onConfirm: () => void = () => {};
|
|
export let onCancel: () => void = () => {};
|
|
|
|
function handleConfirm() {
|
|
onConfirm();
|
|
}
|
|
|
|
function handleCancel() {
|
|
onCancel();
|
|
}
|
|
|
|
function handleWindowKeydown(event: KeyboardEvent) {
|
|
if (!open) return;
|
|
if (event.key === "Escape") {
|
|
handleCancel();
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<svelte:window on:keydown={handleWindowKeydown} />
|
|
|
|
{#if open}
|
|
<div class="modal-backdrop">
|
|
<dialog class="modal" open aria-label={title}>
|
|
<header class="modal-header">
|
|
<h2>{title}</h2>
|
|
</header>
|
|
|
|
<p class="modal-message">{message}</p>
|
|
|
|
<div class="modal-actions">
|
|
{#if showCancel}
|
|
<button type="button" class="secondary" on:click={handleCancel}>{cancelText}</button>
|
|
{/if}
|
|
<button type="button" class:danger={tone === "danger"} on:click={handleConfirm}>
|
|
{confirmText}
|
|
</button>
|
|
</div>
|
|
</dialog>
|
|
</div>
|
|
{/if}
|
|
|
|
<style>
|
|
.modal-backdrop {
|
|
position: fixed;
|
|
inset: 0;
|
|
background: rgba(7, 9, 12, 0.6);
|
|
backdrop-filter: blur(2px);
|
|
display: grid;
|
|
place-items: center;
|
|
z-index: 1000;
|
|
padding: 16px;
|
|
}
|
|
|
|
.modal {
|
|
margin: 0;
|
|
width: min(420px, 100%);
|
|
border-radius: 12px;
|
|
border: 1px solid var(--border-strong);
|
|
background: var(--surface-1);
|
|
box-shadow: 0 18px 46px rgba(0, 0, 0, 0.45);
|
|
padding: 14px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
}
|
|
|
|
.modal-header h2 {
|
|
font-size: 0.98rem;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.modal-message {
|
|
color: var(--text-muted);
|
|
font-size: 0.86rem;
|
|
line-height: 1.45;
|
|
}
|
|
|
|
.modal-actions {
|
|
margin-top: 2px;
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 8px;
|
|
}
|
|
|
|
.modal-actions button {
|
|
border-radius: 7px;
|
|
border: 1px solid var(--border-soft);
|
|
color: var(--text-primary);
|
|
background: var(--surface-2);
|
|
padding: 6px 11px;
|
|
font-size: 0.8rem;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.modal-actions button.secondary {
|
|
background: var(--surface-1);
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.modal-actions button.danger {
|
|
border-color: var(--border-strong);
|
|
background: var(--surface-3);
|
|
color: var(--text-primary);
|
|
}
|
|
</style>
|