Files
Oxicloud/frontend/src/lib/components/DialogHost.svelte
T
2026-06-17 22:07:18 -06:00

119 lines
3.2 KiB
Svelte

<script lang="ts">
import Modal from '$lib/components/Modal.svelte';
import { dialogs } from '$lib/stores/dialogs.svelte';
import { t } from '$lib/i18n/index.svelte';
// Local input value for prompt dialogs; reset whenever a new prompt opens.
let value = $state('');
let lastId: object | null = null;
let inputEl = $state<HTMLInputElement | null>(null);
$effect(() => {
const c = dialogs.current;
if (c && c !== lastId) {
lastId = c;
value = c.kind === 'prompt' ? (c.opts.defaultValue ?? '') : '';
if (c.kind === 'prompt' && c.opts.selectOnOpen) {
const select = c.opts.selectOnOpen;
requestAnimationFrame(() => {
const el = inputEl;
if (!el) return;
el.focus();
if (select === 'name') {
// Select the filename stem only — leave the extension
// untouched so a rename replaces just the name.
const dot = value.lastIndexOf('.');
const end = dot > 0 ? dot : value.length;
el.setSelectionRange(0, end);
} else {
el.select();
}
});
}
}
});
const open = $derived(dialogs.current !== null);
function submit(e?: SubmitEvent) {
e?.preventDefault();
const c = dialogs.current;
if (!c || dialogs.busy) return;
// `resolve` runs any async action and keeps the dialog open on failure.
if (c.kind === 'prompt') void dialogs.resolve(value);
else void dialogs.resolve(true);
}
</script>
{#if dialogs.current}
{@const c = dialogs.current}
<Modal {open} title={c.opts.title} onclose={() => dialogs.cancel()}>
{#if c.kind === 'prompt'}
<form id="dialog-form" onsubmit={submit}>
{#if c.opts.message}<p class="dlg-msg">{c.opts.message}</p>{/if}
<input
class="dlg-input"
type="text"
bind:this={inputEl}
bind:value
placeholder={c.opts.placeholder ?? ''}
autocomplete="off"
disabled={dialogs.busy}
/>
</form>
{:else if c.opts.message}
<p class="dlg-msg">{c.opts.message}</p>
{/if}
{#if dialogs.error}
<p class="dlg-error" role="alert">{dialogs.error}</p>
{/if}
{#snippet footer()}
<button class="btn btn-secondary" disabled={dialogs.busy} onclick={() => dialogs.cancel()}>
{c.opts.cancelText ?? t('common.cancel', 'Cancel')}
</button>
{#if c.kind === 'prompt'}
<button class="btn btn-primary" type="submit" form="dialog-form" disabled={dialogs.busy}>
{dialogs.busy
? t('common.loading', 'Loading…')
: (c.opts.confirmText ?? t('common.ok', 'OK'))}
</button>
{:else}
<button
class="btn {c.opts.danger ? 'btn-danger' : 'btn-primary'}"
disabled={dialogs.busy}
onclick={() => dialogs.resolve(true)}
>
{dialogs.busy
? t('common.loading', 'Loading…')
: (c.opts.confirmText ?? t('common.ok', 'OK'))}
</button>
{/if}
{/snippet}
</Modal>
{/if}
<style>
.dlg-msg {
margin: 0 0 var(--space-3);
color: var(--color-text);
}
.dlg-input {
width: 100%;
padding: var(--space-2-5) var(--space-3);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
background: var(--color-bg-input);
color: var(--color-text);
font-size: var(--text-base);
}
.dlg-error {
margin: var(--space-3) 0 0;
color: var(--color-danger-text);
font-size: var(--text-sm);
}
</style>