Toggle Checkbox Data Table
A header checkbox with controls selects every row, and goes indeterminate when only some rows are picked. A checkedchange listener on every checkbox updates the row highlight and the bulk action bar, whether a row or the header changed it.
| | Invoice | Amount |
|---|---|---|
| INV-1042 | $1,200.00 | |
| INV-1043 | $480.00 | |
| INV-1044 | $3,150.00 |
Code
The following example assumes that you have imported the component and set up the theme.
<div class="data-table">36 collapsed lines
<div class="toolbar"> <span class="count">No rows selected</span> <button type="button" class="delete" disabled>Delete</button> </div>
<table> <thead> <tr> <th class="pick"> <sv-toggle-checkbox aria-label="Select all rows" controls="row-1 row-2 row-3"></sv-toggle-checkbox> </th> <th>Invoice</th> <th class="amount">Amount</th> </tr> </thead>
<tbody> <tr> <td class="pick"><sv-toggle-checkbox id="row-1" aria-label="Select INV-1042"></sv-toggle-checkbox></td> <td>INV-1042</td> <td class="amount">$1,200.00</td> </tr>
<tr> <td class="pick"><sv-toggle-checkbox id="row-2" aria-label="Select INV-1043"></sv-toggle-checkbox></td> <td>INV-1043</td> <td class="amount">$480.00</td> </tr>
<tr> <td class="pick"><sv-toggle-checkbox id="row-3" aria-label="Select INV-1044"></sv-toggle-checkbox></td> <td>INV-1044</td> <td class="amount">$3,150.00</td> </tr> </tbody> </table></div>
<script type="application/x-typescript" module>19 collapsed lines
const table = document.querySelector(".data-table")!; const rowCheckboxes = table.querySelectorAll<ToggleCheckbox>("tbody sv-toggle-checkbox"); const count = table.querySelector(".count")!; const deleteButton = table.querySelector<HTMLButtonElement>(".delete")!;
const update = () => { const selected = [...rowCheckboxes].filter(checkbox => checkbox.checked);
for (const checkbox of rowCheckboxes) { checkbox.closest("tr")?.classList.toggle("selected", checkbox.checked); }
count.textContent = selected.length === 0 ? "No rows selected" : `${selected.length} selected`; deleteButton.disabled = selected.length === 0; };
for (const checkbox of table.querySelectorAll<ToggleCheckbox>("sv-toggle-checkbox")) { checkbox.addEventListener("checkedchange", update); }</script>
<style>79 collapsed lines
.data-table { max-inline-size: 28rem; margin-inline: auto; overflow: hidden; background-color: var(--sv-gray-7); border: 1px solid var(--sv-gray-5); border-radius: var(--sv-bdr-rad-lg);
.toolbar { display: flex; gap: 1em; align-items: center; justify-content: space-between; padding: 0.75em 1em; border-block-end: 1px solid var(--sv-gray-5);
.count { font-size: 0.9em; color: var(--sv-gray-3); }
.delete { padding: 0.4em 0.9em; font: inherit; color: var(--sv-accent-content); cursor: pointer; background-color: var(--sv-accent); border: none; border-radius: var(--sv-bdr-rad-md);
&:disabled { cursor: not-allowed; opacity: 0.5; } } }
table { inline-size: 100%; border-collapse: collapse; }
th, td { padding: 0.7em 1em; vertical-align: middle; text-align: start; }
th { font-size: 0.85em; color: var(--sv-gray-3); text-transform: uppercase; }
.pick { inline-size: 1em; text-align: center;
sv-toggle-checkbox { display: block; margin-inline: auto; } }
.amount { font-variant-numeric: tabular-nums; text-align: end; }
tbody tr { border-block-start: 1px solid var(--sv-gray-5); transition: background-color 200ms;
&.selected { background-color: color-mix(in srgb, var(--sv-accent) 12%, transparent); } } }</style>