@sarunyu/system-one 4.0.4 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/llms.txt +103 -5
  2. package/package.json +1 -1
package/llms.txt CHANGED
@@ -439,6 +439,73 @@ Data tables. Compose with TableRow + TableHeaderCell + TableCell.
439
439
 
440
440
  Align numeric columns `right`, status center. Don't stuff StatusTag inside the Table header.
441
441
 
442
+ **TableCell content** — pass content as children (`<TableCell>{u.name}</TableCell>`).
443
+ Do NOT pass a `label` prop for dynamic data — if children are omitted, the cell
444
+ shows the placeholder `"Text label"`.
445
+
446
+ **Sorting is not automatic.** `TableHeaderCell` is a UI primitive — it only
447
+ toggles its own arrow icon. The parent must own sort state AND sort the data.
448
+
449
+ ```tsx
450
+ import { useMemo, useState } from "react";
451
+ import { Table, TableRow, TableHeaderCell, TableCell } from "@sarunyu/system-one";
452
+
453
+ type SortDir = "none" | "asc" | "desc";
454
+
455
+ export function UsersTable({ users }: { users: User[] }) {
456
+ const [sortKey, setSortKey] = useState<keyof User | null>(null);
457
+ const [sortDir, setSortDir] = useState<SortDir>("none");
458
+
459
+ const sorted = useMemo(() => {
460
+ if (!sortKey || sortDir === "none") return users;
461
+ const copy = [...users];
462
+ copy.sort((a, b) => {
463
+ const av = a[sortKey]; const bv = b[sortKey];
464
+ if (av < bv) return sortDir === "asc" ? -1 : 1;
465
+ if (av > bv) return sortDir === "asc" ? 1 : -1;
466
+ return 0;
467
+ });
468
+ return copy;
469
+ }, [users, sortKey, sortDir]);
470
+
471
+ const dirFor = (k: keyof User): SortDir => (sortKey === k ? sortDir : "none");
472
+ const handleSort = (k: keyof User) => (next: SortDir) => {
473
+ setSortKey(next === "none" ? null : k);
474
+ setSortDir(next);
475
+ };
476
+
477
+ return (
478
+ <Table>
479
+ <TableRow header>
480
+ <TableHeaderCell sortDirection={dirFor("name")} onSortChange={handleSort("name")}>Name</TableHeaderCell>
481
+ <TableHeaderCell sortDirection={dirFor("role")} onSortChange={handleSort("role")}>Role</TableHeaderCell>
482
+ <TableHeaderCell sortable={false}>Status</TableHeaderCell>
483
+ </TableRow>
484
+ {sorted.map(u => (
485
+ <TableRow key={u.id}>
486
+ <TableCell>{u.name}</TableCell>
487
+ <TableCell>{u.role}</TableCell>
488
+ <TableCell><StatusTag type={u.status} /></TableCell>
489
+ </TableRow>
490
+ ))}
491
+ </Table>
492
+ );
493
+ }
494
+ ```
495
+
496
+ Key rules for sort:
497
+ - Parent owns `sortKey` + `sortDir` state.
498
+ - Parent derives `sorted` data via `useMemo` — the Table never mutates data.
499
+ - Pass `sortDirection` (controlled) + `onSortChange` to every sortable header.
500
+ - Only one column sorts at a time — when a column's `sortKey` changes, previous columns naturally read `"none"` via the `dirFor` helper.
501
+ - Columns that shouldn't sort (status, actions) → `sortable={false}`.
502
+
503
+ Props:
504
+ - `Table` — `className`, native `<table>` props.
505
+ - `TableRow` — `header?`, `selected?`, `hoverable?` (default `true`), `onClick`, `className`.
506
+ - `TableHeaderCell` — `children` (label), `sortable?` (default `true`, set `false` to hide the arrow), `sortDirection?: "none" | "asc" | "desc"`, `onSortChange?(next)`, `contentAlign?: "start" | "center" | "end"`, `className`.
507
+ - `TableCell` — `children` (content), `contentAlign?`, `fixed?`, `className`.
508
+
442
509
  ---
443
510
 
444
511
  ### DateInput / TimeInput
@@ -657,11 +724,42 @@ export function SettingsPage() {
657
724
  ### Data table page
658
725
 
659
726
  ```tsx
727
+ import { useMemo, useState } from "react";
660
728
  import {
661
729
  SearchInput, Button, Table, TableRow, TableHeaderCell, TableCell, StatusTag,
662
730
  } from "@sarunyu/system-one";
663
731
 
732
+ type SortDir = "none" | "asc" | "desc";
733
+ type OrderKey = "id" | "customer" | "amount";
734
+
664
735
  export function OrdersPage() {
736
+ const [q, setQ] = useState("");
737
+ const [sortKey, setSortKey] = useState<OrderKey | null>(null);
738
+ const [sortDir, setSortDir] = useState<SortDir>("none");
739
+
740
+ const filtered = useMemo(
741
+ () => orders.filter(o => o.customer.toLowerCase().includes(q.toLowerCase())),
742
+ [q],
743
+ );
744
+
745
+ const sorted = useMemo(() => {
746
+ if (!sortKey || sortDir === "none") return filtered;
747
+ const copy = [...filtered];
748
+ copy.sort((a, b) => {
749
+ const av = a[sortKey]; const bv = b[sortKey];
750
+ if (av < bv) return sortDir === "asc" ? -1 : 1;
751
+ if (av > bv) return sortDir === "asc" ? 1 : -1;
752
+ return 0;
753
+ });
754
+ return copy;
755
+ }, [filtered, sortKey, sortDir]);
756
+
757
+ const dirFor = (k: OrderKey): SortDir => (sortKey === k ? sortDir : "none");
758
+ const handleSort = (k: OrderKey) => (next: SortDir) => {
759
+ setSortKey(next === "none" ? null : k);
760
+ setSortDir(next);
761
+ };
762
+
665
763
  return (
666
764
  <main className="mx-auto w-full max-w-[1200px] px-6 py-10 flex flex-col gap-6">
667
765
  <header className="flex items-center justify-between">
@@ -673,12 +771,12 @@ export function OrdersPage() {
673
771
  </div>
674
772
  <Table>
675
773
  <TableRow header>
676
- <TableHeaderCell>Order</TableHeaderCell>
677
- <TableHeaderCell>Customer</TableHeaderCell>
678
- <TableHeaderCell>Amount</TableHeaderCell>
679
- <TableHeaderCell>Status</TableHeaderCell>
774
+ <TableHeaderCell sortDirection={dirFor("id")} onSortChange={handleSort("id")}>Order</TableHeaderCell>
775
+ <TableHeaderCell sortDirection={dirFor("customer")} onSortChange={handleSort("customer")}>Customer</TableHeaderCell>
776
+ <TableHeaderCell sortDirection={dirFor("amount")} onSortChange={handleSort("amount")}>Amount</TableHeaderCell>
777
+ <TableHeaderCell sortable={false}>Status</TableHeaderCell>
680
778
  </TableRow>
681
- {orders.map(o => (
779
+ {sorted.map(o => (
682
780
  <TableRow key={o.id}>
683
781
  <TableCell>#{o.id}</TableCell>
684
782
  <TableCell>{o.customer}</TableCell>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sarunyu/system-one",
3
- "version": "4.0.4",
3
+ "version": "4.1.0",
4
4
  "type": "module",
5
5
  "description": "A production-ready React design system built for AI-powered web generation tools (Figma Make, Lovable, V0). Tailwind CSS v4 + CSS custom properties for full theming support.",
6
6
  "keywords": [