<%# Data table: sortable headers, row-select, typed cells, badges, kebab row actions. Mirrors the html-css-foundation markup; zero-JS (sort = links, select highlight = CSS). Config: caption?, selectable?, actions? sr-only caption; toggle the check / kebab columns columns: { label, sortable?, sort?: "asc"|"desc", href?, className? }[] rows: { name?, cells: Cell[], actions?: Action[] }[] Cell ∈ string | { text, className? } | { user:{name,initials} } | { rowHeader:{text,href?} } | { badge:{tone,label} } | { html, className? } user + rowHeader cells render as — they identify the row (the row header). Action = { label, icon?, href?, danger?, separatorBefore? } %><% const caption = locals.caption; const selectable = !!locals.selectable; const withActions = !!locals.actions; const columns = locals.columns || []; const rows = locals.rows || []; -%>
<% if (caption) { -%> <% } -%> <% if (selectable) { -%> <% } -%> <% columns.forEach((col) => { -%> <% if (col.sortable) { -%> <% } else { -%> <% } -%> <% }) -%> <% if (withActions) { -%> <% } -%> <% rows.forEach((row) => { -%> <% if (selectable) { -%> <% } -%> <% (row.cells || []).forEach((cell) => { -%> <% if (typeof cell === "string") { -%> <% } else if (cell.user) { -%> <% } else if (cell.rowHeader) { -%> <% } else if (cell.badge) { -%> <% } else if (cell.html != null) { -%> class="<%= cell.className %>"<% } %>><%- cell.html %> <% } else { -%> class="<%= cell.className %>"<% } %>><%= cell.text %> <% } -%> <% }) -%> <% if (withActions) { -%> <% if ((row.actions || []).length) { -%> <% } else { -%> <% } -%> <% } -%> <% }) -%>
<%= caption %>
aria-sort="ascending"<% } else if (col.sort === "desc") { %> aria-sort="descending"<% } %><% if (col.className) { %> class="<%= col.className %>"<% } %>><%= col.label %> "/> class="<%= col.className %>"<% } %>><%= col.label %>Actions
"><%= cell %><%= cell.user.name %><% if (cell.rowHeader.href) { %><%= cell.rowHeader.text %><% } else { %><%= cell.rowHeader.text %><% } %><%= cell.badge.label %>