import { ui } from "@rezi-ui/core";
import { createNodeApp } from "@rezi-ui/node";
type User = { id: string; name: string; email: string; role: string };
type SortDirection = "asc" | "desc";
type State = {
users: User[];
sort: { column: string; direction: SortDirection };
filter: string;
selection: string[];
};
function filterUsers(users: User[], filter: string): User[] {
if (!filter) return users;
const lower = filter.toLowerCase();
return users.filter(
(u) =>
u.name.toLowerCase().includes(lower) ||
u.email.toLowerCase().includes(lower)
);
}
function sortUsers(
users: User[],
column: string,
direction: SortDirection
): User[] {
const sorted = [...users].sort((a, b) => {
const aVal = a[column as keyof User];
const bVal = b[column as keyof User];
if (aVal < bVal) return direction === "asc" ? -1 : 1;
if (aVal > bVal) return direction === "asc" ? 1 : -1;
return 0;
});
return sorted;
}
const app = createNodeApp<State>({
initialState: {
users: [
{ id: "1", name: "Ada Lovelace", email: "[email protected]", role: "Admin" },
{ id: "2", name: "Linus Torvalds", email: "[email protected]", role: "User" },
{ id: "3", name: "Grace Hopper", email: "[email protected]", role: "User" },
{ id: "4", name: "Alan Turing", email: "[email protected]", role: "Admin" },
],
sort: { column: "name", direction: "asc" },
filter: "",
selection: [],
},
});
app.view((state) => {
const filtered = filterUsers(state.users, state.filter);
const sorted = sortUsers(filtered, state.sort.column, state.sort.direction);
return ui.page({ p: 1 }, [
ui.panel("Users", [
ui.row({ gap: 1, pb: 1 }, [
ui.text("Search:", { variant: "label" }),
ui.input({
id: "filter",
value: state.filter,
placeholder: "Filter by name or email...",
onInput: (value) => app.update((s) => ({ ...s, filter: value })),
}),
]),
ui.table({
id: "users",
data: sorted,
getRowKey: (u) => u.id,
columns: [
{ key: "name", header: "Name", flex: 1, sortable: true },
{ key: "email", header: "Email", flex: 2, sortable: true },
{ key: "role", header: "Role", width: 10, sortable: true },
],
selectionMode: "multi",
selection: state.selection,
onSelectionChange: (keys) =>
app.update((s) => ({ ...s, selection: [...keys] })),
sortColumn: state.sort.column,
sortDirection: state.sort.direction,
onSort: (column, direction) =>
app.update((s) => ({ ...s, sort: { column, direction } })),
stripedRows: true,
}),
state.selection.length > 0 &&
ui.row({ gap: 1, pt: 1 }, [
ui.text(`${state.selection.length} selected`, { variant: "caption" }),
ui.button({
id: "clear-selection",
label: "Clear",
intent: "secondary",
onPress: () => app.update((s) => ({ ...s, selection: [] })),
}),
]),
]),
]);
});
app.keys({
"ctrl+c": () => app.stop(),
q: () => app.stop(),
});
await app.start();