Use native HTML <dialog> element for modals
Replaced CSS-based modal with native HTML <dialog> element. Cleaner code, better accessibility, built-in backdrop handling.
This commit is contained in:
parent
a8aaa08d29
commit
5c3e4ce7d8
1 changed files with 46 additions and 46 deletions
|
|
@ -16,7 +16,7 @@
|
||||||
let domains = $state<Domain[]>([]);
|
let domains = $state<Domain[]>([]);
|
||||||
let user = $state<UserInfo | null>(null);
|
let user = $state<UserInfo | null>(null);
|
||||||
let loading = $state(true);
|
let loading = $state(true);
|
||||||
let showModal = $state(false);
|
let dialogEl = $state<HTMLDialogElement | null>(null);
|
||||||
let newDomain = $state({ name: '' });
|
let newDomain = $state({ name: '' });
|
||||||
let createError = $state('');
|
let createError = $state('');
|
||||||
|
|
||||||
|
|
@ -48,6 +48,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openModal() {
|
||||||
|
createError = '';
|
||||||
|
newDomain = { name: '' };
|
||||||
|
dialogEl?.showModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeModal() {
|
||||||
|
dialogEl?.close();
|
||||||
|
}
|
||||||
|
|
||||||
async function createDomain() {
|
async function createDomain() {
|
||||||
createError = '';
|
createError = '';
|
||||||
try {
|
try {
|
||||||
|
|
@ -58,8 +68,7 @@
|
||||||
});
|
});
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
showModal = false;
|
closeModal();
|
||||||
newDomain = { name: '' };
|
|
||||||
await loadData();
|
await loadData();
|
||||||
} else {
|
} else {
|
||||||
createError = data.error || 'Failed to create domain';
|
createError = data.error || 'Failed to create domain';
|
||||||
|
|
@ -93,7 +102,7 @@
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<h2>Domains</h2>
|
<h2>Domains</h2>
|
||||||
{#if isAdmin()}
|
{#if isAdmin()}
|
||||||
<button onclick={() => showModal = true}>Add Domain</button>
|
<button onclick={openModal}>Add Domain</button>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -139,25 +148,22 @@
|
||||||
</table>
|
</table>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if showModal}
|
<dialog bind:this={dialogEl}>
|
||||||
<button class="modal-backdrop" onclick={() => { showModal = false; createError = ''; }} aria-label="Close modal"></button>
|
<h3>Add Domain</h3>
|
||||||
<div class="modal">
|
<form onsubmit={(e) => { e.preventDefault(); createDomain(); }}>
|
||||||
<h3>Add Domain</h3>
|
<label>
|
||||||
<form onsubmit={(e) => { e.preventDefault(); createDomain(); }}>
|
Domain name:
|
||||||
<label>
|
<input type="text" bind:value={newDomain.name} placeholder="example.org" required />
|
||||||
Domain name:
|
</label>
|
||||||
<input type="text" bind:value={newDomain.name} placeholder="example.org" required />
|
{#if createError}
|
||||||
</label>
|
<p class="error">{createError}</p>
|
||||||
{#if createError}
|
{/if}
|
||||||
<p class="error">{createError}</p>
|
<div class="actions">
|
||||||
{/if}
|
<button type="button" class="secondary" onclick={closeModal}>Cancel</button>
|
||||||
<div class="actions">
|
<button type="submit">Create</button>
|
||||||
<button type="button" onclick={() => { showModal = false; createError = ''; }}>Cancel</button>
|
</div>
|
||||||
<button type="submit">Create</button>
|
</form>
|
||||||
</div>
|
</dialog>
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.header {
|
.header {
|
||||||
|
|
@ -236,54 +242,48 @@
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.secondary {
|
||||||
|
background: #95a5a6;
|
||||||
|
}
|
||||||
|
|
||||||
button.danger {
|
button.danger {
|
||||||
background: #e74c3c;
|
background: #e74c3c;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-backdrop {
|
dialog {
|
||||||
position: fixed;
|
|
||||||
inset: 0;
|
|
||||||
background: rgba(0,0,0,0.5);
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal {
|
|
||||||
position: fixed;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
background: white;
|
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
min-width: 400px;
|
border: none;
|
||||||
z-index: 101;
|
box-shadow: 0 4px 20px rgba(0,0,0,0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal h3 {
|
dialog::backdrop {
|
||||||
margin-top: 0;
|
background: rgba(0,0,0,0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal form {
|
dialog h3 {
|
||||||
|
margin: 0 0 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog form {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal label {
|
dialog label {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal input {
|
dialog input {
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal .error {
|
dialog .error {
|
||||||
color: #e74c3c;
|
color: #e74c3c;
|
||||||
background: #fdecea;
|
background: #fdecea;
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue