@designa8/vibedesign-stripe-deepseek-2-176b 0.1.0 → 0.1.2

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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @designa8/vibedesign-stripe-deepseek-2-176b
2
2
 
3
- Complete design system generated by Atoms Design, version **0.1.0**.
3
+ Complete design system generated by Atoms Design, version **0.1.2**.
4
4
  Tokens, atoms, molecules, organisms, and templates — import from the package root;
5
5
  never hardcode visual values in consumer code.
6
6
 
package/dist/styles.css CHANGED
@@ -387,6 +387,9 @@
387
387
  .-top-xs {
388
388
  top: calc(var(--spacing-xs) * -1);
389
389
  }
390
+ .-top-xxs {
391
+ top: calc(var(--spacing-xxs) * -1);
392
+ }
390
393
  .top-0 {
391
394
  top: 0;
392
395
  }
@@ -408,6 +411,9 @@
408
411
  .-right-xs {
409
412
  right: calc(var(--spacing-xs) * -1);
410
413
  }
414
+ .-right-xxs {
415
+ right: calc(var(--spacing-xxs) * -1);
416
+ }
411
417
  .right-0 {
412
418
  right: 0;
413
419
  }
@@ -929,9 +935,6 @@
929
935
  .max-w-dialog {
930
936
  max-width: var(--container-dialog);
931
937
  }
932
- .max-w-drawer {
933
- max-width: var(--container-drawer);
934
- }
935
938
  .max-w-full {
936
939
  max-width: 100%;
937
940
  }
@@ -953,6 +956,9 @@
953
956
  .min-w-0 {
954
957
  min-width: 0;
955
958
  }
959
+ .min-w-4 {
960
+ min-width: calc(var(--spacing) * 4);
961
+ }
956
962
  .min-w-xl {
957
963
  min-width: var(--spacing-xl);
958
964
  }
@@ -1764,6 +1770,9 @@
1764
1770
  .px-xxl {
1765
1771
  padding-inline: var(--spacing-xxl);
1766
1772
  }
1773
+ .px-xxs {
1774
+ padding-inline: var(--spacing-xxs);
1775
+ }
1767
1776
  .py-0 {
1768
1777
  padding-block: 0;
1769
1778
  }
@@ -1830,9 +1839,6 @@
1830
1839
  .pr-md {
1831
1840
  padding-right: var(--spacing-md);
1832
1841
  }
1833
- .pr-sm {
1834
- padding-right: var(--spacing-sm);
1835
- }
1836
1842
  .pr-xl {
1837
1843
  padding-right: var(--spacing-xl);
1838
1844
  }
@@ -3337,6 +3343,16 @@
3337
3343
  padding-left: var(--spacing-xl);
3338
3344
  }
3339
3345
  }
3346
+ .md\:sticky {
3347
+ @media (width >= 768px) {
3348
+ position: sticky;
3349
+ }
3350
+ }
3351
+ .md\:top-0 {
3352
+ @media (width >= 768px) {
3353
+ top: 0;
3354
+ }
3355
+ }
3340
3356
  .md\:col-span-1 {
3341
3357
  @media (width >= 768px) {
3342
3358
  grid-column: span 1 / span 1;
@@ -3377,6 +3393,11 @@
3377
3393
  display: none;
3378
3394
  }
3379
3395
  }
3396
+ .md\:h-dvh {
3397
+ @media (width >= 768px) {
3398
+ height: 100dvh;
3399
+ }
3400
+ }
3380
3401
  .md\:w-40 {
3381
3402
  @media (width >= 768px) {
3382
3403
  width: calc(var(--spacing) * 40);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@designa8/vibedesign-stripe-deepseek-2-176b",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Complete design system \"My Project stripe-deepseek\" generated by Atoms Design.",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",
@@ -1,77 +1,228 @@
1
1
  import * as React from "react";
2
2
  import { DataTableDesktop } from "../DataTableDesktop";
3
- import type { PaymentRow } from "../DataTableDesktop";
3
+ import type {
4
+ DataTableBaseRow,
5
+ DataTableColumn,
6
+ DataTableDesktopProps,
7
+ DataTableFilter,
8
+ DataTableRange,
9
+ } from "../DataTableDesktop";
4
10
  import { DataTableMobile } from "../DataTableMobile";
5
- import type { MobilePaymentRow } from "../DataTableMobile";
11
+ import type {
12
+ DataTableMobileRowSpec,
13
+ DataTableMobileProps,
14
+ } from "../DataTableMobile";
6
15
 
7
16
  const cn = (...values: Array<string | false | null | undefined>) =>
8
17
  values.filter(Boolean).join(" ");
9
18
 
10
- export interface DataTableProps
19
+ /**
20
+ * The generic table contract. `columns` drives the wide layout and `mobileRow`
21
+ * drives the stacked one; everything else is shared, so both halves always show
22
+ * the same filtered, searched, sorted, selected and paginated slice.
23
+ */
24
+ export interface DataTableProps<T extends DataTableBaseRow = DataTableBaseRow>
11
25
  extends Omit<React.ComponentPropsWithoutRef<"div">, "onChange" | "title"> {
12
- /**
13
- * Ledger lines for the wide layout. The desktop ledger needs the fee and net
14
- * split, so it takes the richer row shape.
15
- */
16
- desktopRows?: PaymentRow[];
17
- /**
18
- * Ledger lines for the narrow layout. The stacked row shows the payment and
19
- * its amount only, so it takes its own lighter row shape.
20
- */
21
- mobileRows?: MobilePaymentRow[];
22
- /** Total payments in the ledger, used by the desktop range caption and pager. */
23
- totalCount?: number;
26
+ /** Column definitions for the wide layout. */
27
+ columns?: Array<DataTableColumn<T>>;
28
+ /** Rows to render, in both layouts. */
29
+ rows?: T[];
30
+ /** Stable row identity, defaults to `row.id`. */
31
+ getRowId?: (row: T) => string;
32
+ /** How one row collapses into the stacked mobile line. */
33
+ mobileRow?: DataTableMobileRowSpec<T>;
34
+ /** Filter chips shown above the table. */
35
+ filters?: DataTableFilter[];
36
+ filter?: string;
37
+ defaultFilter?: string;
38
+ onFilterChange?: (value: string) => void;
39
+ filterBy?: (row: T, value: string) => boolean;
40
+ searchPlaceholder?: string;
41
+ query?: string;
42
+ onQueryChange?: (value: string) => void;
43
+ searchBy?: (row: T) => string;
44
+ /** Date-range presets. The trigger is hidden when omitted. */
45
+ ranges?: DataTableRange[];
46
+ range?: string;
47
+ onRangeChange?: (value: string) => void;
48
+ /** Replaces the export / contact-sales pair. */
49
+ toolbar?: React.ReactNode;
50
+ onRowClick?: (row: T) => void;
51
+ selectable?: boolean;
52
+ selectedIds?: string[];
53
+ onSelectionChange?: (ids: string[]) => void;
54
+ bulkActions?: (ids: string[]) => React.ReactNode;
24
55
  /** Rows per page in the wide layout. */
25
56
  pageSize?: number;
26
57
  /** Rows per page in the narrow layout. */
27
58
  mobilePageSize?: number;
28
- /** Placeholder copy inside the narrow layout's search pill. */
29
- searchPlaceholder?: string;
59
+ totalCount?: number;
60
+ itemLabel?: string;
61
+ emptyTitle?: React.ReactNode;
62
+ emptyDescription?: React.ReactNode;
63
+ emptyAction?: React.ReactNode;
64
+ dense?: boolean;
65
+ title?: React.ReactNode;
66
+ description?: React.ReactNode;
67
+
68
+ /** @deprecated legacy alias for `rows` on the wide layout. */
69
+ desktopRows?: DataTableDesktopProps["rows"];
70
+ /** @deprecated legacy alias for `rows` on the narrow layout. */
71
+ mobileRows?: DataTableMobileProps["rows"];
30
72
  }
31
73
 
32
74
  /**
33
- * The payments ledger, responsive by breakpoint rather than by JavaScript.
34
- *
35
- * It is a thin composition wrapper: both the desktop and the mobile ledger stay
36
- * mounted in the tree, and only their visibility flips at this system's `md`
37
- * breakpoint (768px) — `hidden md:block` around the desktop layout, `md:hidden`
38
- * around the mobile one. No viewport measurement, no hydration mismatch, and no
39
- * layout reimplementation: each half keeps its own toolbar, rows, and pager.
75
+ * One responsive table: both halves stay mounted and only their visibility
76
+ * flips at this system's `md` breakpoint (768px) — `hidden md:block` around the
77
+ * wide layout, `md:hidden` around the stacked one. No viewport measurement and
78
+ * no hydration mismatch.
40
79
  *
41
- * Because both halves are always mounted, their local state (search text,
42
- * filters, page) survives a window resize in either direction.
80
+ * The wrapper owns the shared state — filter, query, range, selection and page
81
+ * — so resizing never loses what the reader was looking at, and a hidden half
82
+ * never paginates or filters differently from the visible one.
43
83
  */
44
- export const DataTable = React.forwardRef<HTMLDivElement, DataTableProps>(
45
- (
46
- {
47
- desktopRows,
48
- mobileRows,
49
- totalCount,
50
- pageSize,
51
- mobilePageSize,
52
- searchPlaceholder,
53
- className,
54
- ...rest
55
- },
56
- ref,
57
- ) => (
84
+ function DataTableInner<T extends DataTableBaseRow>(
85
+ props: DataTableProps<T>,
86
+ ref: React.Ref<HTMLDivElement>,
87
+ ) {
88
+ const {
89
+ columns,
90
+ rows,
91
+ getRowId,
92
+ mobileRow,
93
+ filters,
94
+ filter,
95
+ defaultFilter,
96
+ onFilterChange,
97
+ filterBy,
98
+ searchPlaceholder,
99
+ query,
100
+ onQueryChange,
101
+ searchBy,
102
+ ranges,
103
+ range,
104
+ onRangeChange,
105
+ toolbar,
106
+ onRowClick,
107
+ selectable,
108
+ selectedIds,
109
+ onSelectionChange,
110
+ bulkActions,
111
+ pageSize,
112
+ mobilePageSize,
113
+ totalCount,
114
+ itemLabel,
115
+ emptyTitle,
116
+ emptyDescription,
117
+ emptyAction,
118
+ dense,
119
+ title,
120
+ description,
121
+ desktopRows,
122
+ mobileRows,
123
+ className,
124
+ ...rest
125
+ } = props;
126
+
127
+ const legacyRows = rows === undefined && desktopRows !== undefined;
128
+
129
+ const [innerQuery, setInnerQuery] = React.useState("");
130
+ const activeQuery = query ?? innerQuery;
131
+
132
+ const [innerFilter, setInnerFilter] = React.useState(
133
+ defaultFilter ?? filters?.[0]?.value ?? "all",
134
+ );
135
+ const activeFilter = filter ?? innerFilter;
136
+
137
+ const [innerRange, setInnerRange] = React.useState(ranges?.[0]?.value ?? "");
138
+ const activeRangeValue = range ?? innerRange;
139
+
140
+ const [innerSelected, setInnerSelected] = React.useState<string[]>([]);
141
+ const activeSelected = selectedIds ?? innerSelected;
142
+
143
+ const handleQuery = (next: string) => {
144
+ if (query === undefined) setInnerQuery(next);
145
+ onQueryChange?.(next);
146
+ };
147
+
148
+ const handleFilter = (next: string) => {
149
+ if (filter === undefined) setInnerFilter(next);
150
+ onFilterChange?.(next);
151
+ };
152
+
153
+ const handleRange = (next: string) => {
154
+ if (range === undefined) setInnerRange(next);
155
+ onRangeChange?.(next);
156
+ };
157
+
158
+ const handleSelection = (next: string[]) => {
159
+ if (selectedIds === undefined) setInnerSelected(next);
160
+ onSelectionChange?.(next);
161
+ };
162
+
163
+ const shared = {
164
+ getRowId,
165
+ filters,
166
+ filter: activeFilter,
167
+ onFilterChange: handleFilter,
168
+ filterBy,
169
+ searchPlaceholder,
170
+ query: activeQuery,
171
+ onQueryChange: handleQuery,
172
+ searchBy,
173
+ ranges,
174
+ range: activeRangeValue,
175
+ onRangeChange: handleRange,
176
+ toolbar,
177
+ onRowClick,
178
+ selectable,
179
+ selectedIds: activeSelected,
180
+ onSelectionChange: handleSelection,
181
+ bulkActions,
182
+ totalCount,
183
+ itemLabel,
184
+ emptyTitle,
185
+ emptyDescription,
186
+ emptyAction,
187
+ dense,
188
+ title,
189
+ description,
190
+ };
191
+
192
+ const desktopRowData = legacyRows
193
+ ? desktopRows
194
+ : (rows ?? (desktopRows as T[] | undefined));
195
+
196
+ const mobileRowData = rows ?? (mobileRows as T[] | undefined);
197
+
198
+ return (
58
199
  <div ref={ref} className={cn("w-full", className)} {...rest}>
59
200
  <div className="hidden w-full md:block">
60
- <DataTableDesktop
61
- rows={desktopRows}
62
- totalCount={totalCount}
201
+ <DataTableDesktop<T>
202
+ {...shared}
203
+ columns={columns}
204
+ rows={desktopRowData as T[]}
63
205
  pageSize={pageSize}
64
206
  />
65
207
  </div>
66
208
  <div className="w-full md:hidden">
67
- <DataTableMobile
68
- rows={mobileRows}
209
+ <DataTableMobile<T>
210
+ {...shared}
211
+ mobileRow={mobileRow}
212
+ rows={mobileRowData as T[]}
69
213
  pageSize={mobilePageSize}
70
- searchPlaceholder={searchPlaceholder}
71
214
  />
72
215
  </div>
73
216
  </div>
74
- ),
75
- );
217
+ );
218
+ }
219
+
220
+ type DataTableComponent = <T extends DataTableBaseRow = DataTableBaseRow>(
221
+ props: DataTableProps<T> & React.RefAttributes<HTMLDivElement>,
222
+ ) => React.ReactElement | null;
223
+
224
+ export const DataTable = React.forwardRef(
225
+ DataTableInner,
226
+ ) as unknown as DataTableComponent;
76
227
 
77
- DataTable.displayName = "DataTable";
228
+ (DataTable as { displayName?: string }).displayName = "DataTable";
@@ -1,2 +1,14 @@
1
1
  export { DataTable } from "./DataTable";
2
2
  export type { DataTableProps } from "./DataTable";
3
+ export type {
4
+ DataTableAlign,
5
+ DataTableBaseRow,
6
+ DataTableColumn,
7
+ DataTableFilter,
8
+ DataTableRange,
9
+ } from "../DataTableDesktop";
10
+ export type {
11
+ DataTableMobileFilter,
12
+ DataTableMobileRange,
13
+ DataTableMobileRowSpec,
14
+ } from "../DataTableMobile";
@@ -1,15 +1,226 @@
1
1
  import * as React from "react";
2
+ import { Badge } from "../Badge";
3
+ import { Button } from "../Button";
2
4
  import { DataTable } from "./DataTable";
5
+ import type { DataTableColumn, DataTableFilter, DataTableRange } from "./DataTable";
3
6
 
4
- /**
5
- * One full-width instance. Resize the preview frame (or use the host's device
6
- * toggle) to cross the `md` breakpoint — the wide ledger replaces the stacked
7
- * one, and each half keeps the state you left it in.
8
- */
9
- const Preview = () => (
10
- <div className="w-full bg-canvas p-lg">
11
- <DataTable />
12
- </div>
13
- );
14
-
15
- export default Preview;
7
+ interface Invoice {
8
+ id: string;
9
+ customer: string;
10
+ status: "paid" | "open" | "failed";
11
+ amount: number;
12
+ issued: string;
13
+ }
14
+
15
+ const STATUS_LABEL: Record<Invoice["status"], string> = {
16
+ paid: "Paid",
17
+ open: "Open",
18
+ failed: "Failed",
19
+ };
20
+
21
+ const CUSTOMERS = [
22
+ "Northwind Co",
23
+ "Acme Shop",
24
+ "Acme Market",
25
+ "Lumen Studio",
26
+ "Harbor Freight",
27
+ "Tidewater Labs",
28
+ "Rookery Press",
29
+ "Fielding Group",
30
+ ];
31
+
32
+ const invoices: Invoice[] = Array.from({ length: 160 }, (_, index) => {
33
+ const status: Invoice["status"] =
34
+ index % 9 === 0 ? "failed" : index % 4 === 0 ? "open" : "paid";
35
+ return {
36
+ id: `inv_${(10480 + index).toString(36).toUpperCase()}`,
37
+ customer: CUSTOMERS[index % CUSTOMERS.length],
38
+ status,
39
+ amount: 120 + ((index * 137) % 8600),
40
+ issued: `Feb ${((index % 28) + 1).toString().padStart(2, "0")}, 2026`,
41
+ };
42
+ });
43
+
44
+ const filters: DataTableFilter[] = [
45
+ { value: "all", label: "All invoices", count: 160 },
46
+ { value: "paid", label: "Paid", count: 118 },
47
+ { value: "open", label: "Open", count: 32 },
48
+ { value: "failed", label: "Failed", count: 10 },
49
+ ];
50
+
51
+ const ranges: DataTableRange[] = [
52
+ { value: "30", label: "Last 30 days", detail: "Jan 26 – Feb 24, 2026" },
53
+ { value: "90", label: "Last 90 days", detail: "Nov 26 – Feb 24, 2026" },
54
+ { value: "ytd", label: "Year to date", detail: "Jan 1 – Feb 24, 2026" },
55
+ ];
56
+
57
+ const columns: Array<DataTableColumn<Invoice>> = [
58
+ {
59
+ id: "id",
60
+ header: "Invoice",
61
+ width: "min",
62
+ render: (row) => (
63
+ <span className="whitespace-nowrap text-body-sm text-ink">{row.id}</span>
64
+ ),
65
+ },
66
+ {
67
+ id: "customer",
68
+ header: "Customer",
69
+ width: "min",
70
+ render: (row) => (
71
+ <span className="whitespace-nowrap text-body-sm text-ink">
72
+ {row.customer}
73
+ </span>
74
+ ),
75
+ },
76
+ {
77
+ id: "status",
78
+ header: "Status",
79
+ width: "min",
80
+ render: (row) =>
81
+ row.status === "paid" ? (
82
+ <span className="inline-flex items-center gap-xs whitespace-nowrap text-body-sm text-ink">
83
+ <span
84
+ aria-hidden="true"
85
+ className="size-2 shrink-0 rounded-full bg-primary"
86
+ />
87
+ {STATUS_LABEL[row.status]}
88
+ </span>
89
+ ) : (
90
+ <Badge
91
+ variant="soft"
92
+ tone={row.status === "failed" ? "error" : "info"}
93
+ size="sm"
94
+ >
95
+ {STATUS_LABEL[row.status]}
96
+ </Badge>
97
+ ),
98
+ },
99
+ {
100
+ id: "amount",
101
+ header: "Amount",
102
+ align: "right",
103
+ width: "min",
104
+ sortable: true,
105
+ sortValue: (row) => row.amount,
106
+ render: (row) => (
107
+ <span className="whitespace-nowrap text-body-tabular text-ink">
108
+ {`$${row.amount.toLocaleString("en-US", {
109
+ minimumFractionDigits: 2,
110
+ maximumFractionDigits: 2,
111
+ })}`}
112
+ </span>
113
+ ),
114
+ },
115
+ {
116
+ id: "issued",
117
+ header: "Issued",
118
+ width: "min",
119
+ render: (row) => (
120
+ <span className="whitespace-nowrap text-body-sm text-ink">
121
+ {row.issued}
122
+ </span>
123
+ ),
124
+ },
125
+ ];
126
+
127
+ const mobileRow = {
128
+ title: (row: Invoice) => row.id,
129
+ subtitle: (row: Invoice) => (
130
+ <Badge
131
+ variant="soft"
132
+ tone={row.status === "failed" ? "error" : "info"}
133
+ size="sm"
134
+ showDot
135
+ >
136
+ {STATUS_LABEL[row.status]}
137
+ </Badge>
138
+ ),
139
+ trailing: (row: Invoice) =>
140
+ `$${row.amount.toLocaleString("en-US", {
141
+ minimumFractionDigits: 2,
142
+ maximumFractionDigits: 2,
143
+ })}`,
144
+ meta: (row: Invoice) => `${row.customer} · ${row.issued}`,
145
+ };
146
+
147
+ export default function Preview() {
148
+ const [selected, setSelected] = React.useState<string[]>([]);
149
+ const [filter, setFilter] = React.useState("all");
150
+ const [query, setQuery] = React.useState("");
151
+
152
+ return (
153
+ <div className="w-full max-w-5xl mx-auto p-lg space-y-lg bg-canvas">
154
+ <DataTable<Invoice>
155
+ columns={columns}
156
+ rows={invoices}
157
+ filters={filters}
158
+ filter={filter}
159
+ onFilterChange={setFilter}
160
+ filterBy={(row, value) => row.status === value}
161
+ searchPlaceholder="Search invoices"
162
+ query={query}
163
+ onQueryChange={setQuery}
164
+ searchBy={(row) => `${row.id} ${row.customer}`}
165
+ ranges={ranges}
166
+ selectedIds={selected}
167
+ onSelectionChange={setSelected}
168
+ pageSize={10}
169
+ mobilePageSize={4}
170
+ itemLabel="invoices"
171
+ dense
172
+ title="Invoices"
173
+ description="Every invoice issued this quarter, newest first."
174
+ toolbar={
175
+ <>
176
+ <Button variant="outline-soft" size="md">
177
+ Export
178
+ </Button>
179
+ <Button variant="primary" size="md">
180
+ New invoice
181
+ </Button>
182
+ </>
183
+ }
184
+ bulkActions={(ids) => (
185
+ <>
186
+ <Button variant="outline-soft" size="md">
187
+ {`Download ${ids.length}`}
188
+ </Button>
189
+ <Button variant="outline-soft" size="md">
190
+ Archive
191
+ </Button>
192
+ </>
193
+ )}
194
+ onRowClick={() => undefined}
195
+ mobileRow={mobileRow}
196
+ emptyTitle="No invoices match those filters"
197
+ emptyDescription="Try a different status or clear the search field."
198
+ emptyAction={
199
+ <Button
200
+ variant="outline-soft"
201
+ size="md"
202
+ onClick={() => {
203
+ setFilter("all");
204
+ setQuery("");
205
+ }}
206
+ >
207
+ Clear filters
208
+ </Button>
209
+ }
210
+ />
211
+
212
+ <DataTable<Invoice>
213
+ columns={columns}
214
+ rows={invoices.slice(0, 4)}
215
+ filters={filters.slice(0, 1)}
216
+ pageSize={5}
217
+ mobilePageSize={4}
218
+ itemLabel="invoices"
219
+ dense
220
+ title="Compact, no selection"
221
+ selectable={false}
222
+ mobileRow={mobileRow}
223
+ />
224
+ </div>
225
+ );
226
+ }