Table
Displays data in a tabular format
<sa-table>
<sa-table-caption>Total Active Value includes Confirmed and Pending bookings only.</sa-table-caption>
<sa-table-header>
<sa-table-row>
<sa-table-head class="w-[100px]">Booking #</sa-table-head>
<sa-table-head>Status</sa-table-head>
<sa-table-head>Destination</sa-table-head>
<sa-table-head class="text-right">Amount</sa-table-head>
</sa-table-row>
</sa-table-header>
<sa-table-body>
@foreach (var booking in StaticData.Bookings)
{
<sa-table-row>
<sa-table-cell class="font-medium">@booking.Id</sa-table-cell>
<sa-table-cell>
<sa-badge variant="@GetBadgeVariant(booking.Status)">
@booking.Status.ToString()
</sa-badge>
</sa-table-cell>
<sa-table-cell>@booking.Destination</sa-table-cell>
<sa-table-cell class="text-right">
@booking.Amount.ToString("N")
</sa-table-cell>
</sa-table-row>
}
</sa-table-body>
<sa-table-footer>
<sa-table-row>
<sa-table-cell colspan="3">
Total Active Value
</sa-table-cell>
<sa-table-cell class="text-right">
@{
var activeValue = StaticData.Bookings
.Where(b => b.Status != BookingStatus.Cancelled)
.Select(b => b.Amount)
.Sum();
}
@activeValue.ToString("N")
</sa-table-cell>
</sa-table-row>
</sa-table-footer>
</sa-table>
@functions
{
BadgeVariant GetBadgeVariant(BookingStatus status) => status switch
{
BookingStatus.Cancelled => BadgeVariant.Destructive,
BookingStatus.Pending => BadgeVariant.Secondary,
_ => BadgeVariant.Default
};
}Usage
Compose the table from the caption, header, body and footer tags, mirroring the native table structure.
<sa-table>
<sa-table-caption>...</sa-table-caption>
<sa-table-header>
<sa-table-row>
<sa-table-head>...</sa-table-head>
</sa-table-row>
</sa-table-header>
<sa-table-body>
<sa-table-row>
<sa-table-cell>...</sa-table-cell>
</sa-table-row>
</sa-table-body>
<sa-table-footer>
<sa-table-row>
<sa-table-cell>...</sa-table-cell>
</sa-table-row>
</sa-table-footer>
</sa-table>Examples
With border
Wrap the table in a bordered container to give it an outer border.
<div class="w-full overflow-hidden rounded-md border">
<sa-table>
<sa-table-header>
<sa-table-row>
<sa-table-head class="w-[100px]">Booking #</sa-table-head>
<sa-table-head>Status</sa-table-head>
<sa-table-head>Destination</sa-table-head>
<sa-table-head class="text-right">Amount</sa-table-head>
</sa-table-row>
</sa-table-header>
<sa-table-body>
@foreach (var booking in StaticData.Bookings)
{
<sa-table-row>
<sa-table-cell class="font-medium">@booking.Id</sa-table-cell>
<sa-table-cell>
<sa-badge variant="@GetBadgeVariant(booking.Status)">
@booking.Status.ToString()
</sa-badge>
</sa-table-cell>
<sa-table-cell>@booking.Destination</sa-table-cell>
<sa-table-cell class="text-right">
@booking.Amount.ToString("N")
</sa-table-cell>
</sa-table-row>
}
</sa-table-body>
<sa-table-footer>
<sa-table-row>
<sa-table-cell colspan="3">
Total Active Value
</sa-table-cell>
<sa-table-cell class="text-right">
@{
var activeValue = StaticData.Bookings
.Where(b => b.Status != BookingStatus.Cancelled)
.Select(b => b.Amount)
.Sum();
}
@activeValue.ToString("N")
</sa-table-cell>
</sa-table-row>
</sa-table-footer>
</sa-table>
</div>
@functions
{
BadgeVariant GetBadgeVariant(BookingStatus status) => status switch
{
BookingStatus.Cancelled => BadgeVariant.Destructive,
BookingStatus.Pending => BadgeVariant.Secondary,
_ => BadgeVariant.Default
};
}With select
Wrap the table in <sa-table-selection> and add checkboxes to the header and body rows to make rows selectable.
<sa-table>
<sa-table-caption>Total Active Value includes Confirmed and Pending bookings only.</sa-table-caption>
<sa-table-header>
<sa-table-row>
<sa-table-head class="w-[100px]">Booking #</sa-table-head>
<sa-table-head>Status</sa-table-head>
<sa-table-head>Destination</sa-table-head>
<sa-table-head class="text-right">Amount</sa-table-head>
</sa-table-row>
</sa-table-header>
<sa-table-body>
@foreach (var booking in StaticData.Bookings)
{
<sa-table-row>
<sa-table-cell class="font-medium">@booking.Id</sa-table-cell>
<sa-table-cell>
<sa-select size="SelectSize.Small">
@foreach (var selectListItem in bookingStatusList)
{
var selected = BookingStatus.TryParse(selectListItem.Value, out BookingStatus status) && booking.Status == status;
<option value="@selectListItem.Value" selected="@(selected)">@selectListItem.Text</option>
}
</sa-select>
</sa-table-cell>
<sa-table-cell>@booking.Destination</sa-table-cell>
<sa-table-cell class="text-right">
@booking.Amount.ToString("N")
</sa-table-cell>
</sa-table-row>
}
</sa-table-body>
<sa-table-footer>
<sa-table-row>
<sa-table-cell colspan="3">
Total Active Value
</sa-table-cell>
<sa-table-cell class="text-right">
@{
var activeValue = StaticData.Bookings
.Where(b => b.Status != BookingStatus.Cancelled)
.Select(b => b.Amount)
.Sum();
}
@activeValue.ToString("N")
</sa-table-cell>
</sa-table-row>
</sa-table-footer>
</sa-table>Row selection
Wrap the table in <sa-table-selection> to add row selection. A checkbox in the table's header row acts as the select-all, a checkbox in a body row selects that row, and selected rows are highlighted. The selection posts as ordinary checkbox form data, so no extra wiring is needed to submit it.
<sa-table-selection id="booking-selection">
<sa-table>
<sa-table-header>
<sa-table-row>
<sa-table-head class="w-12">
<sa-input type="checkbox" aria-label="Select all bookings"/>
</sa-table-head>
<sa-table-head class="w-[100px]">Booking #</sa-table-head>
<sa-table-head>Status</sa-table-head>
<sa-table-head>Destination</sa-table-head>
<sa-table-head class="text-right">Amount</sa-table-head>
</sa-table-row>
</sa-table-header>
<sa-table-body>
@foreach (var booking in StaticData.Bookings)
{
<sa-table-row>
<sa-table-cell>
<sa-input type="checkbox" name="selectedBookings" value="@booking.Id"
checked="@(booking.Status == BookingStatus.Pending)"
aria-label="Select booking @booking.Id"/>
</sa-table-cell>
<sa-table-cell class="font-medium">@booking.Id</sa-table-cell>
<sa-table-cell>@booking.Status.ToString()</sa-table-cell>
<sa-table-cell>@booking.Destination</sa-table-cell>
<sa-table-cell class="text-right">@booking.Amount.ToString("N2")</sa-table-cell>
</sa-table-row>
}
</sa-table-body>
</sa-table>
</sa-table-selection>
<sa-button id="booking-selection-delete" variant="ButtonVariant.Destructive" class="mt-4" hidden>
Delete selected
</sa-button>
<script>
(() => {
const selection = document.getElementById("booking-selection");
const deleteButton = document.getElementById("booking-selection-delete");
const syncButton = () => deleteButton.hidden = selection.selectedValues.length === 0;
// selection-change fires on every user-driven change; the initial
// server-rendered selection is read once the component is defined.
selection.addEventListener("selection-change", syncButton);
customElements.whenDefined("sel-table-selection").then(syncButton);
deleteButton.addEventListener("click", () =>
alert(`Delete bookings:\n${selection.selectedValues.join("\n")}`));
})();
</script>API Reference
<sa-table>
Renders a <table> element wrapped in a scrollable <div> container. The class attribute is applied to the <table> element, and other attributes are applied to the container.
<sa-table-caption>
Renders a <caption> element.
<sa-table-header>
Renders a <thead> element.
<sa-table-body>
Renders a <tbody> element.
<sa-table-footer>
Renders a <tfoot> element.
<sa-table-row>
Renders a <tr> element.
<sa-table-head>
Renders a <th> element, and forwards standard <th> attributes such as colspan and scope.
<sa-table-cell>
Renders a <td> element, and forwards standard <td> attributes such as colspan.
<sa-table-selection>
Adds row selection to the table it wraps, rendered as a sel-table-selection web component. The checkbox in the table's header row acts as the select-all, and a checkbox in a body row selects that row. Selected rows get data-state="selected", the select-all reflects the checked/indeterminate state, and the selection posts as ordinary checkbox form data. From JavaScript, read the current selection from the element's selectedValues property, or listen for its bubbling selection-change event.