@tailor-platform/app-shell 1.7.0 → 1.9.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.
- package/dist/app-shell-core.css +1 -1
- package/dist/app-shell.d.ts +787 -9
- package/dist/app-shell.js +5913 -3812
- package/package.json +4 -2
- package/skills/app-shell-patterns/SKILL.md +32 -20
- package/skills/app-shell-patterns/references/fundamental/components.md +125 -25
- package/skills/app-shell-patterns/references/fundamental/design-system.md +24 -0
- package/skills/app-shell-patterns/references/fundamental/graphql.md +1 -1
- package/skills/app-shell-patterns/references/patterns/detail-hero-with-actions.md +51 -7
- package/skills/app-shell-patterns/references/patterns/list-dense-scan.md +49 -14
|
@@ -52,11 +52,13 @@ export type Order = {
|
|
|
52
52
|
createdAt: string;
|
|
53
53
|
};
|
|
54
54
|
|
|
55
|
+
// Primary status column — filled semantic variants (one per row).
|
|
56
|
+
// Secondary status columns (e.g. delivery, billing) use outline-* instead.
|
|
55
57
|
const statusVariant = {
|
|
56
58
|
draft: "neutral",
|
|
57
|
-
confirmed: "
|
|
58
|
-
shipped: "
|
|
59
|
-
delivered: "
|
|
59
|
+
confirmed: "info",
|
|
60
|
+
shipped: "warning",
|
|
61
|
+
delivered: "success",
|
|
60
62
|
} as const;
|
|
61
63
|
|
|
62
64
|
export const columns: Column<Order>[] = [
|
|
@@ -67,7 +69,10 @@ export const columns: Column<Order>[] = [
|
|
|
67
69
|
render: (row) => <Badge variant={statusVariant[row.status]}>{row.status}</Badge>,
|
|
68
70
|
},
|
|
69
71
|
{
|
|
72
|
+
// Numeric columns right-align so digits line up. `type: "money" | "number"`
|
|
73
|
+
// auto-right; with a custom `render` set `align` explicitly.
|
|
70
74
|
label: "Amount",
|
|
75
|
+
align: "right",
|
|
71
76
|
render: (row) => `${row.amount.toLocaleString()}`,
|
|
72
77
|
},
|
|
73
78
|
{ label: "Created", accessor: (row) => row.createdAt },
|
|
@@ -78,7 +83,7 @@ export const columns: Column<Order>[] = [
|
|
|
78
83
|
|
|
79
84
|
```tsx
|
|
80
85
|
/* pattern: list/dense-scan */
|
|
81
|
-
import { DataTable, useDataTable, Button, Input } from "@tailor-platform/app-shell";
|
|
86
|
+
import { DataTable, Layout, useDataTable, Button, Input } from "@tailor-platform/app-shell";
|
|
82
87
|
import type { Order } from "./columns";
|
|
83
88
|
import { columns } from "./columns";
|
|
84
89
|
import type { DataTableData } from "@tailor-platform/app-shell";
|
|
@@ -92,19 +97,46 @@ export default function DenseScanList({ data, onCreateClick }: Props) {
|
|
|
92
97
|
const table = useDataTable({ data, columns });
|
|
93
98
|
|
|
94
99
|
return (
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
<
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
// `fill` pins the page chrome for table-first pages: the title, toolbar,
|
|
101
|
+
// column header row, and pagination footer stay visible at every viewport
|
|
102
|
+
// height — only the table's rows region scrolls. Omit `fill` on pages
|
|
103
|
+
// that should flow and scroll naturally (forms, dashboards, articles).
|
|
104
|
+
<Layout fill>
|
|
105
|
+
<Layout.Header
|
|
106
|
+
title="Orders"
|
|
107
|
+
actions={[
|
|
108
|
+
<Button key="create" onClick={onCreateClick}>
|
|
109
|
+
Create Order
|
|
110
|
+
</Button>,
|
|
111
|
+
]}
|
|
112
|
+
/>
|
|
113
|
+
<Layout.Column>
|
|
114
|
+
<DataTable.Root value={table}>
|
|
115
|
+
<DataTable.Toolbar>
|
|
116
|
+
<Input placeholder="Search orders..." className="max-w-sm" />
|
|
117
|
+
</DataTable.Toolbar>
|
|
118
|
+
<DataTable.Table />
|
|
119
|
+
<DataTable.Footer>
|
|
120
|
+
<DataTable.Pagination />
|
|
121
|
+
</DataTable.Footer>
|
|
122
|
+
</DataTable.Root>
|
|
123
|
+
</Layout.Column>
|
|
124
|
+
</Layout>
|
|
104
125
|
);
|
|
105
126
|
}
|
|
106
127
|
```
|
|
107
128
|
|
|
129
|
+
## Page Layout & Internal Scrolling
|
|
130
|
+
|
|
131
|
+
Table-first pages should pin their chrome and scroll only the rows region. Wrap the page in `<Layout fill>`:
|
|
132
|
+
|
|
133
|
+
- `fill` stretches the layout to the available height and bounds the column row, so the `DataTable` shrinks to fit instead of growing past the viewport
|
|
134
|
+
- The `Layout.Header` (title/actions), `DataTable.Toolbar`, the column header row (sticky), and `DataTable.Footer` (pagination) stay visible at every viewport height — only the rows scroll vertically
|
|
135
|
+
- When the current page of rows fits, nothing stretches and no scrollbar appears — short tables render identically with or without `fill`
|
|
136
|
+
- Requires no extra styling on the page: the height chain (`AppShell` content area → `Layout fill` → `Layout.Column` → `DataTable.Root`) is wired by the components
|
|
137
|
+
|
|
138
|
+
Omit `fill` on pages that should flow and scroll naturally (forms, dashboards, articles) — the AppShell content area scrolls those.
|
|
139
|
+
|
|
108
140
|
## Variants
|
|
109
141
|
|
|
110
142
|
- **Toolbar chips only (`DataTable.Filters`)** — best when filters map cleanly to typed column metadata / enum facets
|
|
@@ -117,7 +149,9 @@ export default function DenseScanList({ data, onCreateClick }: Props) {
|
|
|
117
149
|
|
|
118
150
|
- Column count: 4-8 recommended
|
|
119
151
|
- Must include pagination — never render unbounded lists
|
|
120
|
-
-
|
|
152
|
+
- Table-first pages use `<Layout fill>` so title/toolbar/header/footer stay pinned and only rows scroll
|
|
153
|
+
- Handle every state: `DataTable` renders the loading skeleton and error row; always provide a **labelled empty state** (what the list is + how to add the first record) rather than a bare empty table
|
|
154
|
+
- Status Badge colors must use design system tokens (variant prop): the **primary** status column uses **filled** semantic variants; **secondary** status columns (delivery, billing) use **`outline-*`** (see `design-system.md` → Composition & emphasis rules)
|
|
121
155
|
- Bulk actions toolbar appears only when ≥1 row is selected
|
|
122
156
|
- Whole row is clickable via `onClickRow`; no per-row "View" / "Open" buttons
|
|
123
157
|
- Per-row `Menu` (overflow `…`) is reserved for non-navigation actions (Archive, Duplicate, Delete)
|
|
@@ -125,6 +159,7 @@ export default function DenseScanList({ data, onCreateClick }: Props) {
|
|
|
125
159
|
## Anti-patterns
|
|
126
160
|
|
|
127
161
|
- Building a bespoke table + custom pagination instead of `DataTable` + `useCollectionVariables`
|
|
162
|
+
- Hand-rolled `max-height`/`overflow` wrappers around `DataTable` to contain scrolling — use `<Layout fill>` instead
|
|
128
163
|
- Tabs that mutate only local UI state while pagination/filters assume the full server set
|
|
129
164
|
- Using `<table>` directly instead of `<DataTable>` for live collections
|
|
130
165
|
- Client-side filtering on 1000+ records without server-side support
|