@sarunyu/system-one 4.0.4 → 4.1.1

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.
@@ -41,6 +41,8 @@ export interface TableCellProps extends TdHTMLAttributes<HTMLTableCellElement> {
41
41
  export declare const Table: import('react').ForwardRefExoticComponent<TableProps & import('react').RefAttributes<HTMLTableElement>>;
42
42
  export interface TableRowProps extends HTMLAttributes<HTMLTableRowElement> {
43
43
  selected?: boolean;
44
+ /** Fires when a `<TableCell type="checkbox" />` inside this row is toggled. Set this together with `selected` to make rows selectable — the cell's checkbox is wired automatically. */
45
+ onSelectedChange?: (next: boolean) => void;
44
46
  /** Enables built-in hover state propagation to cells in this row. */
45
47
  hoverable?: boolean;
46
48
  }
@@ -1 +1 @@
1
- {"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../../src/components/table.tsx"],"names":[],"mappings":"AAGA,OAAO,EAQL,KAAK,cAAc,EAEnB,KAAK,SAAS,EACd,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACtB,MAAM,OAAO,CAAC;AAEf,OAAO,EAAY,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAO,KAAK,UAAU,EAAE,MAAM,OAAO,CAAC;AAG7C,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AACxD,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,OAAO,GAAG,UAAU,CAAC;AAChE,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,WAAW,GAAG,YAAY,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,CAAC;AAC5G,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,MAAM,GAAG,kBAAkB,CAAC;AAEzE,MAAM,WAAW,UAAW,SAAQ,mBAAmB,CAAC,gBAAgB,CAAC;IACvE,sFAAsF;IACtF,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,oBAAqB,SAAQ,gBAAgB,CAAC,oBAAoB,CAAC;IAClF,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;IACxC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,KAAK,IAAI,CAAC;IACvD,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,cAAe,SAAQ,gBAAgB,CAAC,oBAAoB,CAAC;IAC5E,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAChC;AAsED,eAAO,MAAM,KAAK,yGA0DhB,CAAC;AAEH,MAAM,WAAW,aAAc,SAAQ,cAAc,CAAC,mBAAmB,CAAC;IACxE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qEAAqE;IACrE,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,eAAO,MAAM,QAAQ,+GAkCpB,CAAC;AAEF,eAAO,MAAM,eAAe,uHAwH3B,CAAC;AAEF,eAAO,MAAM,SAAS,iHA0JpB,CAAC"}
1
+ {"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../../src/components/table.tsx"],"names":[],"mappings":"AAGA,OAAO,EAQL,KAAK,cAAc,EAEnB,KAAK,SAAS,EACd,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACtB,MAAM,OAAO,CAAC;AAEf,OAAO,EAAY,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAO,KAAK,UAAU,EAAE,MAAM,OAAO,CAAC;AAG7C,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AACxD,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,OAAO,GAAG,UAAU,CAAC;AAChE,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,WAAW,GAAG,YAAY,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,CAAC;AAC5G,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,MAAM,GAAG,kBAAkB,CAAC;AAEzE,MAAM,WAAW,UAAW,SAAQ,mBAAmB,CAAC,gBAAgB,CAAC;IACvE,sFAAsF;IACtF,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,oBAAqB,SAAQ,gBAAgB,CAAC,oBAAoB,CAAC;IAClF,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;IACxC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,KAAK,IAAI,CAAC;IACvD,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,cAAe,SAAQ,gBAAgB,CAAC,oBAAoB,CAAC;IAC5E,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAChC;AAuED,eAAO,MAAM,KAAK,yGA0DhB,CAAC;AAEH,MAAM,WAAW,aAAc,SAAQ,cAAc,CAAC,mBAAmB,CAAC;IACxE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uLAAuL;IACvL,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAC3C,qEAAqE;IACrE,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,eAAO,MAAM,QAAQ,+GA0CpB,CAAC;AAEF,eAAO,MAAM,eAAe,uHAwH3B,CAAC;AAEF,eAAO,MAAM,SAAS,iHA8JpB,CAAC"}
package/llms.txt CHANGED
@@ -439,6 +439,117 @@ 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
+ **Selectable rows.** To make rows selectable with checkboxes, pair `<TableRow selected onSelectedChange>` with a `<TableCell type="checkbox" />`. The cell renders its own checkbox wired to the row — **do not put a `<Checkbox>` inside the cell yourself.** The row background turns brand-tinted automatically when `selected` is true.
504
+
505
+ ```tsx
506
+ import { useMemo, useState } from "react";
507
+ import { Table, TableRow, TableHeaderCell, TableCell } from "@sarunyu/system-one";
508
+
509
+ export function SelectableTable({ rows }: { rows: Row[] }) {
510
+ const [selected, setSelected] = useState<Set<string>>(new Set());
511
+
512
+ const toggle = (id: string) => (next: boolean) =>
513
+ setSelected(prev => {
514
+ const copy = new Set(prev);
515
+ next ? copy.add(id) : copy.delete(id);
516
+ return copy;
517
+ });
518
+
519
+ const headerState =
520
+ selected.size === 0 ? false : selected.size === rows.length ? true : "indeterminate";
521
+ const toggleAll = (next: boolean) =>
522
+ setSelected(next ? new Set(rows.map(r => r.id)) : new Set());
523
+
524
+ return (
525
+ <Table>
526
+ <TableRow header>
527
+ <TableHeaderCell type="check" checkState={headerState} onCheckChange={toggleAll} />
528
+ <TableHeaderCell>Symbol</TableHeaderCell>
529
+ <TableHeaderCell>Name</TableHeaderCell>
530
+ </TableRow>
531
+ {rows.map(r => (
532
+ <TableRow
533
+ key={r.id}
534
+ selected={selected.has(r.id)}
535
+ onSelectedChange={toggle(r.id)}
536
+ >
537
+ <TableCell type="checkbox" />
538
+ <TableCell>{r.symbol}</TableCell>
539
+ <TableCell>{r.name}</TableCell>
540
+ </TableRow>
541
+ ))}
542
+ </Table>
543
+ );
544
+ }
545
+ ```
546
+
547
+ Props:
548
+ - `Table` — `className`, native `<table>` props.
549
+ - `TableRow` — `header?`, `selected?`, `onSelectedChange?(next: boolean)` (fires when a `type="checkbox"` cell in this row is toggled), `hoverable?` (default `true`), `onClick`, `className`.
550
+ - `TableHeaderCell` — `children` (label), `type?: "text" | "icon" | "check"` (use `"check"` for select-all column), `checkState?: boolean | "indeterminate"`, `onCheckChange?(next)`, `sortable?` (default `true`, set `false` to hide the arrow), `sortDirection?: "none" | "asc" | "desc"`, `onSortChange?(next)`, `contentAlign?: "start" | "center" | "end"`, `className`.
551
+ - `TableCell` — `children` (content), `type?: "default" | "text-icon" | "text-image" | "tag" | "icon" | "button" | "checkbox"`, `contentAlign?`, `fixed?`, `className`. For `type="checkbox"` the cell renders its own checkbox — bind selection via the parent `TableRow`, not by nesting a `<Checkbox>`.
552
+
442
553
  ---
443
554
 
444
555
  ### DateInput / TimeInput
@@ -657,11 +768,42 @@ export function SettingsPage() {
657
768
  ### Data table page
658
769
 
659
770
  ```tsx
771
+ import { useMemo, useState } from "react";
660
772
  import {
661
773
  SearchInput, Button, Table, TableRow, TableHeaderCell, TableCell, StatusTag,
662
774
  } from "@sarunyu/system-one";
663
775
 
776
+ type SortDir = "none" | "asc" | "desc";
777
+ type OrderKey = "id" | "customer" | "amount";
778
+
664
779
  export function OrdersPage() {
780
+ const [q, setQ] = useState("");
781
+ const [sortKey, setSortKey] = useState<OrderKey | null>(null);
782
+ const [sortDir, setSortDir] = useState<SortDir>("none");
783
+
784
+ const filtered = useMemo(
785
+ () => orders.filter(o => o.customer.toLowerCase().includes(q.toLowerCase())),
786
+ [q],
787
+ );
788
+
789
+ const sorted = useMemo(() => {
790
+ if (!sortKey || sortDir === "none") return filtered;
791
+ const copy = [...filtered];
792
+ copy.sort((a, b) => {
793
+ const av = a[sortKey]; const bv = b[sortKey];
794
+ if (av < bv) return sortDir === "asc" ? -1 : 1;
795
+ if (av > bv) return sortDir === "asc" ? 1 : -1;
796
+ return 0;
797
+ });
798
+ return copy;
799
+ }, [filtered, sortKey, sortDir]);
800
+
801
+ const dirFor = (k: OrderKey): SortDir => (sortKey === k ? sortDir : "none");
802
+ const handleSort = (k: OrderKey) => (next: SortDir) => {
803
+ setSortKey(next === "none" ? null : k);
804
+ setSortDir(next);
805
+ };
806
+
665
807
  return (
666
808
  <main className="mx-auto w-full max-w-[1200px] px-6 py-10 flex flex-col gap-6">
667
809
  <header className="flex items-center justify-between">
@@ -673,12 +815,12 @@ export function OrdersPage() {
673
815
  </div>
674
816
  <Table>
675
817
  <TableRow header>
676
- <TableHeaderCell>Order</TableHeaderCell>
677
- <TableHeaderCell>Customer</TableHeaderCell>
678
- <TableHeaderCell>Amount</TableHeaderCell>
679
- <TableHeaderCell>Status</TableHeaderCell>
818
+ <TableHeaderCell sortDirection={dirFor("id")} onSortChange={handleSort("id")}>Order</TableHeaderCell>
819
+ <TableHeaderCell sortDirection={dirFor("customer")} onSortChange={handleSort("customer")}>Customer</TableHeaderCell>
820
+ <TableHeaderCell sortDirection={dirFor("amount")} onSortChange={handleSort("amount")}>Amount</TableHeaderCell>
821
+ <TableHeaderCell sortable={false}>Status</TableHeaderCell>
680
822
  </TableRow>
681
- {orders.map(o => (
823
+ {sorted.map(o => (
682
824
  <TableRow key={o.id}>
683
825
  <TableCell>#{o.id}</TableCell>
684
826
  <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.1",
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": [