Basic table
import { html, renderApp } from 'mates';
import { Avatar, Badge, Chip, Table } from 'mates-ui';
const USERS = [
{ id: "1", name: "Alice Chen", email: "alice@example.com", role: "Admin", status: "active", joined: "Jan 2024" },
{ id: "2", name: "Bob Smith", email: "bob@example.com", role: "Editor", status: "inactive", joined: "Feb 2024" },
{ id: "3", name: "Carol Jones", email: "carol@example.com", role: "Viewer", status: "pending", joined: "Mar 2024" },
{ id: "4", name: "David Brown", email: "david@example.com", role: "Admin", status: "active", joined: "Apr 2024" },
{ id: "5", name: "Eve Johnson", email: "eve@example.com", role: "Editor", status: "inactive", joined: "May 2024" },
];
const STATUS_BADGE = { active: "success", inactive: "neutral", pending: "warning" };
const COLUMNS = [
{ id: "name", header: "Name", render: (r) => html`<div style="display:flex;align-items:center;gap:var(--md-space-2);">${Avatar({ name: r.name, size: "sm" })}<span>${r.name}</span></div>` },
{ id: "email", header: "Email", render: (r) => html`<span style="color:var(--md-color-text-muted);">${r.email}</span>` },
{ id: "role", header: "Role", render: (r) => Chip(r.role, { variant: "assist" }) },
{ id: "status", header: "Status", render: (r) => Badge(r.status, { variant: STATUS_BADGE[r.status] }) },
{ id: "joined", header: "Joined", render: (r) => html`<span style="color:var(--md-color-text-muted);">${r.joined}</span>` },
];
const App = () => () => html`
${Table({ columns: COLUMNS, rows: USERS, rowKey: (r) => r.id })}
`;
renderApp(App, document.getElementById('app'));
Variants — default / bordered / striped
import { html, renderApp } from 'mates';
import { Avatar, Badge, Chip, Table, Text } from 'mates-ui';
const USERS = [
{ id: "1", name: "Alice Chen", email: "alice@example.com", role: "Admin", status: "active", joined: "Jan 2024" },
{ id: "2", name: "Bob Smith", email: "bob@example.com", role: "Editor", status: "inactive", joined: "Feb 2024" },
{ id: "3", name: "Carol Jones", email: "carol@example.com", role: "Viewer", status: "pending", joined: "Mar 2024" },
];
const STATUS_BADGE = { active: "success", inactive: "neutral", pending: "warning" };
const COLUMNS = [
{ id: "name", header: "Name", render: (r) => html`<div style="display:flex;align-items:center;gap:var(--md-space-2);">${Avatar({ name: r.name, size: "sm" })}<span>${r.name}</span></div>` },
{ id: "email", header: "Email", render: (r) => html`<span style="color:var(--md-color-text-muted);">${r.email}</span>` },
{ id: "status", header: "Status", render: (r) => Badge(r.status, { variant: STATUS_BADGE[r.status] }) },
];
const App = () => () => html`
<x-col gap="var(--md-space-5)">
${Text("default", { type: "label-sm", color: "muted" })}${Table({ columns: COLUMNS, rows: USERS, rowKey: (r) => r.id })}
${Text("bordered", { type: "label-sm", color: "muted" })}${Table({ columns: COLUMNS, rows: USERS, rowKey: (r) => r.id, variant: "bordered" })}
${Text("striped", { type: "label-sm", color: "muted" })}${Table({ columns: COLUMNS, rows: USERS, rowKey: (r) => r.id, variant: "striped" })}
</x-col>
`;
renderApp(App, document.getElementById('app'));
Sortable columns
import { html, atom, renderApp } from 'mates';
import { Avatar, Badge, Chip, Table } from 'mates-ui';
const USERS = [
{ id: "1", name: "Alice Chen", email: "alice@example.com", role: "Admin", status: "active", joined: "Jan 2024" },
{ id: "2", name: "Bob Smith", email: "bob@example.com", role: "Editor", status: "inactive", joined: "Feb 2024" },
{ id: "3", name: "Carol Jones", email: "carol@example.com", role: "Viewer", status: "pending", joined: "Mar 2024" },
{ id: "4", name: "David Brown", email: "david@example.com", role: "Admin", status: "active", joined: "Apr 2024" },
];
const STATUS_BADGE = { active: "success", inactive: "neutral", pending: "warning" };
const COLUMNS = [
{ id: "name", header: "Name", sortable: true, render: (r) => html`<div style="display:flex;align-items:center;gap:var(--md-space-2);">${Avatar({ name: r.name, size: "sm" })}<span>${r.name}</span></div>` },
{ id: "email", header: "Email", sortable: true, render: (r) => html`<span style="color:var(--md-color-text-muted);">${r.email}</span>` },
{ id: "role", header: "Role", sortable: true, render: (r) => Chip(r.role, { variant: "assist" }) },
{ id: "status", header: "Status", render: (r) => Badge(r.status, { variant: STATUS_BADGE[r.status] }) },
];
const App = () => {
const sortBy = atom("name");
const sortDir = atom("asc");
return () => html`
${Table({ columns: COLUMNS, rows: USERS, rowKey: (r) => r.id, sortBy: sortBy(), sortDir: sortDir(), onSort: (col, dir) => { sortBy.set(col); sortDir.set(dir); } })}
`;
};
renderApp(App, document.getElementById('app'));
Dense mode
import { html, renderApp } from 'mates';
import { Avatar, Badge, Chip, Table } from 'mates-ui';
const USERS = [
{ id: "1", name: "Alice Chen", role: "Admin", status: "active" },
{ id: "2", name: "Bob Smith", role: "Editor", status: "inactive" },
{ id: "3", name: "Carol Jones", role: "Viewer", status: "pending" },
{ id: "4", name: "David Brown", role: "Admin", status: "active" },
];
const STATUS_BADGE = { active: "success", inactive: "neutral", pending: "warning" };
const COLUMNS = [
{ id: "name", header: "Name", render: (r) => html`<div style="display:flex;align-items:center;gap:var(--md-space-2);">${Avatar({ name: r.name, size: "sm" })}<span>${r.name}</span></div>` },
{ id: "role", header: "Role", render: (r) => Chip(r.role, { variant: "assist" }) },
{ id: "status", header: "Status", render: (r) => Badge(r.status, { variant: STATUS_BADGE[r.status] }) },
];
const App = () => () => html`
${Table({ columns: COLUMNS, rows: USERS, rowKey: (r) => r.id, dense: true })}
`;
renderApp(App, document.getElementById('app'));
Row selection
import { html, atom, renderApp } from 'mates';
import { Avatar, Badge, Chip, Table, Text } from 'mates-ui';
const USERS = [
{ id: "1", name: "Alice Chen", role: "Admin", status: "active" },
{ id: "2", name: "Bob Smith", role: "Editor", status: "inactive" },
{ id: "3", name: "Carol Jones", role: "Viewer", status: "pending" },
{ id: "4", name: "David Brown", role: "Admin", status: "active" },
];
const STATUS_BADGE = { active: "success", inactive: "neutral", pending: "warning" };
const COLUMNS = [
{ id: "name", header: "Name", render: (r) => html`<div style="display:flex;align-items:center;gap:var(--md-space-2);">${Avatar({ name: r.name, size: "sm" })}<span>${r.name}</span></div>` },
{ id: "role", header: "Role", render: (r) => Chip(r.role, { variant: "assist" }) },
{ id: "status", header: "Status", render: (r) => Badge(r.status, { variant: STATUS_BADGE[r.status] }) },
];
const App = () => {
const sel = atom([]);
return () => html`
<x-col gap="var(--md-space-2)">
${sel().length > 0 ? Text(sel().length + " row(s) selected", { type: "body-sm", color: "muted" }) : Text("Click checkboxes to select rows.", { type: "body-sm", color: "muted" })}
${Table({ columns: COLUMNS, rows: USERS, rowKey: (r) => r.id, selectable: true, selected: sel(), onSelect: (keys) => sel.set(keys) })}
</x-col>
`;
};
renderApp(App, document.getElementById('app'));
Nested rows (tree expand)
import { html, atom, renderApp } from 'mates';
import { Avatar, Badge, Chip, Table, Text } from 'mates-ui';
const USERS = [
{ id: "eng", name: "Engineering", role: "Dept", status: "active", children: [
{ id: "1", name: "Alice Chen", role: "Admin", status: "active" },
{ id: "2", name: "Bob Smith", role: "Editor", status: "inactive" },
],
},
{ id: "des", name: "Design", role: "Dept", status: "active", children: [
{ id: "3", name: "Carol Jones", role: "Viewer", status: "pending" },
],
},
];
const STATUS_BADGE = { active: "success", inactive: "neutral", pending: "warning" };
const COLUMNS = [
{ id: "name", header: "Name", render: (r) => html`<div style="display:flex;align-items:center;gap:var(--md-space-2);">${Avatar({ name: r.name, size: "sm" })}<span>${r.name}</span></div>` },
{ id: "role", header: "Role", render: (r) => Chip(r.role, { variant: "assist" }) },
{ id: "status", header: "Status", render: (r) => Badge(r.status, { variant: STATUS_BADGE[r.status] }) },
];
const App = () => {
const expanded = atom([]);
const lastLabel = atom("");
return () => html`
<x-col gap="var(--md-space-2)">
${lastLabel() ? Text("Last expanded: " + lastLabel(), { type: "body-sm", color: "muted" }) : Text("Expand a department to see members.", { type: "body-sm", color: "muted" })}
${Table({ columns: COLUMNS, rows: USERS, rowKey: (r) => r.id, variant: "striped", nestedRows: true, expandedKeys: expanded(), onExpandedChange: (keys) => expanded.set(keys), onRowExpand: (r) => lastLabel.set(r.name) })}
</x-col>
`;
};
renderApp(App, document.getElementById('app'));
Virtualized body
import { html, atom, renderApp } from 'mates';
import { TableVirtualized, Text } from 'mates-ui';
const ROWS = Array.from({ length: 500 }, (_, i) => ({
id: String(i + 1),
name: "User " + (i + 1),
email: "user" + (i + 1) + "@example.com",
role: (["Admin", "Editor", "Viewer"])[i % 3],
status: (["active", "inactive", "pending"])[i % 3],
}));
const COLUMNS = [
{ id: "name", header: "Name", width: 192, minWidth: 120, accessor: (r) => r.name, sortable: true },
{ id: "email", header: "Email", width: 256, minWidth: 160, accessor: (r) => r.email, sortable: true },
{ id: "role", header: "Role", width: 128, minWidth: 80, accessor: (r) => r.role, sortable: true },
{ id: "status", header: "Status", width: 112, minWidth: 80, accessor: (r) => r.status, sortable: true },
];
const App = () => {
const sortBy = atom("name");
const sortDir = atom("asc");
return () => html`
<x-col gap="var(--md-space-2)" style="width:100%;">
${Text("500 rows — only visible rows are in the DOM.", { type: "body-sm", color: "muted" })}
${TableVirtualized({ columns: COLUMNS, rows: ROWS, rowKey: (r) => r.id, bodyHeight: "22.5rem", variant: "striped", stickyHeader: true, sortBy: sortBy(), sortDir: sortDir(), onSort: (col, dir) => { sortBy.set(col); sortDir.set(dir); } })}
</x-col>
`;
};
renderApp(App, document.getElementById('app'));