Skip to content

Dialog Confirm Delete

One dialog serves every row in the list. Clicking Delete stores the row's id on the dialog, fills in the project name, and calls open(). ignore-escape with no close button forces an explicit Cancel or Delete, and confirming removes the stored row.

  • Marketing site Updated 2 days ago
  • Design system Updated 5 days ago
  • Internal dashboard Updated 3 weeks ago

Delete this project?

and all of its deployments will be removed. This cannot be undone.

Code

The following example assumes that you have imported the component and set up the theme.

Confirm Delete
<div class="projects">
23 collapsed lines
<ul>
<li id="project-1">
<div class="text">
<strong>Marketing site</strong>
<span class="detail">Updated 2 days ago</span>
</div>
<button type="button">Delete</button>
</li>
<li id="project-2">
<div class="text">
<strong>Design system</strong>
<span class="detail">Updated 5 days ago</span>
</div>
<button type="button">Delete</button>
</li>
<li id="project-3">
<div class="text">
<strong>Internal dashboard</strong>
<span class="detail">Updated 3 weeks ago</span>
</div>
<button type="button">Delete</button>
</li>
</ul>
</div>
<sv-dialog id="confirm-dialog" aria-labelledby="confirm-dialog-title" ignore-escape>
9 collapsed lines
<h2 id="confirm-dialog-title">Delete this project?</h2>
<p>
<strong class="project-name"></strong>
and all of its deployments will be removed. This cannot be undone.
</p>
<div class="dialog-footer">
<button type="button" data-dialog-close="confirm-dialog">Cancel</button>
<button id="confirm-delete" type="button" class="danger">Delete project</button>
</div>
</sv-dialog>
<script type="application/x-typescript" module>
19 collapsed lines
const dialog = document.querySelector<Dialog>("#confirm-dialog")!;
const projectName = dialog.querySelector(".project-name")!;
document.querySelector(".projects ul")?.addEventListener("click", event => {
const button = (event.target as HTMLElement | null)?.closest("button");
if (!button) return;
const row = button.closest("li");
if (!row) return;
dialog.dataset.target = row.id;
projectName.textContent = row.querySelector("strong")?.textContent ?? "";
dialog.open();
});
document.querySelector("#confirm-delete")?.addEventListener("click", () => {
document.getElementById(dialog.dataset.target ?? "")?.remove();
dialog.close();
});
</script>
<style>
80 collapsed lines
.projects {
max-inline-size: 28rem;
margin-inline: auto;
ul {
display: grid;
padding: 0.25em;
margin: 0;
list-style: none;
background-color: var(--sv-gray-7);
border: 1px solid var(--sv-gray-5);
border-radius: var(--sv-bdr-rad-lg);
}
ul:not(:has(li))::after {
display: block;
padding: 1em;
color: var(--sv-gray-3);
text-align: center;
content: "No projects left.";
}
li {
display: flex;
gap: 1em;
align-items: center;
padding: 0.75em 1em;
&:not(:last-child) {
border-block-end: 1px solid var(--sv-gray-6);
}
.text {
display: grid;
flex: 1;
gap: 0.15em;
.detail {
color: var(--sv-gray-3);
}
}
button {
padding: 0.4em 0.8em;
font: inherit;
color: var(--sv-error);
cursor: pointer;
background-color: transparent;
border: 1px solid var(--sv-gray-5);
border-radius: var(--sv-bdr-rad-md);
}
}
}
#confirm-dialog {
h2 {
margin-block-start: 0;
}
.dialog-footer {
display: flex;
gap: 0.75em;
justify-content: flex-end;
margin-block-start: 1.5em;
button {
padding: 0.5em 1.2em;
font: inherit;
cursor: pointer;
background-color: var(--sv-gray-5);
border: none;
border-radius: var(--sv-bdr-rad-md);
}
.danger {
color: #fff;
background-color: crimson;
}
}
}
</style>