@tailor-platform/app-shell 1.6.1 → 1.8.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.
@@ -0,0 +1,58 @@
1
+ @custom-variant dark (&:where(.dark, .dark *));
2
+
3
+ /**
4
+ * Tailwind v4 theme bridge — maps raw CSS custom properties to Tailwind's
5
+ * design token namespace so utilities like `bg-background`, `text-primary`,
6
+ * `shadow-sm` resolve correctly regardless of the active palette.
7
+ */
8
+ @theme inline {
9
+ --font-sans: "Inter Variable", "Inter", ui-sans-serif, system-ui, sans-serif;
10
+
11
+ --color-background: var(--background);
12
+ --color-foreground: var(--foreground);
13
+ --color-card: var(--card);
14
+ --color-card-foreground: var(--card-foreground);
15
+ --color-popover: var(--popover);
16
+ --color-popover-foreground: var(--popover-foreground);
17
+ --color-primary: var(--primary);
18
+ --color-primary-foreground: var(--primary-foreground);
19
+ --color-secondary: var(--secondary);
20
+ --color-secondary-foreground: var(--secondary-foreground);
21
+ --color-muted: var(--muted);
22
+ --color-muted-foreground: var(--muted-foreground);
23
+ --color-accent: var(--accent);
24
+ --color-accent-foreground: var(--accent-foreground);
25
+ --color-destructive: var(--destructive);
26
+ --color-destructive-foreground: var(--destructive-foreground);
27
+ --color-border: var(--border);
28
+ --color-input: var(--input);
29
+ --color-ring: var(--ring);
30
+ --color-chart-1: var(--chart-1);
31
+ --color-chart-2: var(--chart-2);
32
+ --color-chart-3: var(--chart-3);
33
+ --color-chart-4: var(--chart-4);
34
+ --color-chart-5: var(--chart-5);
35
+ --radius-sm: calc(var(--radius) - 4px);
36
+ --radius-md: calc(var(--radius) - 2px);
37
+ --radius-lg: var(--radius);
38
+ --radius-xl: calc(var(--radius) + 4px);
39
+ --color-sidebar: var(--sidebar);
40
+ --color-sidebar-foreground: var(--sidebar-foreground);
41
+ --color-sidebar-primary: var(--sidebar-primary);
42
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
43
+ --color-sidebar-accent: var(--sidebar-accent);
44
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
45
+ --color-sidebar-border: var(--sidebar-border);
46
+ --color-sidebar-ring: var(--sidebar-ring);
47
+
48
+ --color-status-default: var(--status-default);
49
+ --color-status-neutral: var(--status-neutral);
50
+ --color-status-completed: var(--status-completed);
51
+ --color-status-attention: var(--status-attention);
52
+ --color-status-danger: var(--status-danger);
53
+
54
+ --shadow-xs: var(--semantic-shadow-xs);
55
+ --shadow-sm: var(--semantic-shadow-sm);
56
+ --shadow-md: var(--semantic-shadow-md);
57
+ --shadow-lg: var(--semantic-shadow-lg);
58
+ }
package/dist/theme.css CHANGED
@@ -22,62 +22,4 @@
22
22
  * Palette selection is by CSS import; there is no runtime palette prop.
23
23
  */
24
24
  @import "./themes/default.css" layer(theme.defaults);
25
-
26
- @custom-variant dark (&:where(.dark, .dark *));
27
-
28
- /**
29
- * Tailwind v4 theme bridge — maps raw CSS custom properties to Tailwind's
30
- * design token namespace so utilities like `bg-background`, `text-primary`,
31
- * `shadow-sm` resolve correctly regardless of the active palette.
32
- */
33
- @theme inline {
34
- --font-sans: "Inter Variable", "Inter", ui-sans-serif, system-ui, sans-serif;
35
-
36
- --color-background: var(--background);
37
- --color-foreground: var(--foreground);
38
- --color-card: var(--card);
39
- --color-card-foreground: var(--card-foreground);
40
- --color-popover: var(--popover);
41
- --color-popover-foreground: var(--popover-foreground);
42
- --color-primary: var(--primary);
43
- --color-primary-foreground: var(--primary-foreground);
44
- --color-secondary: var(--secondary);
45
- --color-secondary-foreground: var(--secondary-foreground);
46
- --color-muted: var(--muted);
47
- --color-muted-foreground: var(--muted-foreground);
48
- --color-accent: var(--accent);
49
- --color-accent-foreground: var(--accent-foreground);
50
- --color-destructive: var(--destructive);
51
- --color-destructive-foreground: var(--destructive-foreground);
52
- --color-border: var(--border);
53
- --color-input: var(--input);
54
- --color-ring: var(--ring);
55
- --color-chart-1: var(--chart-1);
56
- --color-chart-2: var(--chart-2);
57
- --color-chart-3: var(--chart-3);
58
- --color-chart-4: var(--chart-4);
59
- --color-chart-5: var(--chart-5);
60
- --radius-sm: calc(var(--radius) - 4px);
61
- --radius-md: calc(var(--radius) - 2px);
62
- --radius-lg: var(--radius);
63
- --radius-xl: calc(var(--radius) + 4px);
64
- --color-sidebar: var(--sidebar);
65
- --color-sidebar-foreground: var(--sidebar-foreground);
66
- --color-sidebar-primary: var(--sidebar-primary);
67
- --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
68
- --color-sidebar-accent: var(--sidebar-accent);
69
- --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
70
- --color-sidebar-border: var(--sidebar-border);
71
- --color-sidebar-ring: var(--sidebar-ring);
72
-
73
- --color-status-default: var(--status-default);
74
- --color-status-neutral: var(--status-neutral);
75
- --color-status-completed: var(--status-completed);
76
- --color-status-attention: var(--status-attention);
77
- --color-status-danger: var(--status-danger);
78
-
79
- --shadow-xs: var(--semantic-shadow-xs);
80
- --shadow-sm: var(--semantic-shadow-sm);
81
- --shadow-md: var(--semantic-shadow-md);
82
- --shadow-lg: var(--semantic-shadow-lg);
83
- }
25
+ @import "./theme.bridge.css";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tailor-platform/app-shell",
3
- "version": "1.6.1",
3
+ "version": "1.8.0",
4
4
  "description": "An opinionated React application framework for building ERP applications on Tailor Platform",
5
5
  "keywords": [
6
6
  "app-shell",
@@ -44,6 +44,7 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "@base-ui/react": "^1.5.0",
47
+ "@internationalized/date": "3.12.2",
47
48
  "@standard-schema/spec": "^1.1.0",
48
49
  "@tailor-platform/auth-public-client": "^0.5.0",
49
50
  "change-case": "^5.4.4",
@@ -51,6 +52,7 @@
51
52
  "clsx": "^2.1.1",
52
53
  "encoding-japanese": "^2.2.0",
53
54
  "lucide-react": "^1.8.0",
55
+ "openai": "6.45.0",
54
56
  "papaparse": "^5.5.3",
55
57
  "react-router": "^7.14.1",
56
58
  "sonner": "^2.0.7",
@@ -47,7 +47,9 @@ For the full upstream API of any component, follow the link to the published ref
47
47
 
48
48
  **Import:** `import { Layout } from '@tailor-platform/app-shell'`
49
49
  **Purpose:** Standard page container with header + 1–N column body. The most-used component — every page wraps content in `<Layout>`.
50
- **API:** `Layout` (root) + `Layout.Column` + `Layout.Header`. `LayoutProps`: `columns` (number, default auto-detected from children), `gap`, `title`, `actions`, `className`, `style`.
50
+ **API:** `Layout` (root) + `Layout.Column` + `Layout.Header`. `LayoutProps`: `fill` (boolean), `columns` (number, default auto-detected from children), `gap`, `title`, `actions`, `className`, `style`.
51
+
52
+ **`fill`** — stretches the layout to the available height and bounds the column row so children can scroll internally instead of growing the page. Use on table-first pages (`<Layout fill>` + `DataTable`): the title, table toolbar, sticky column headers, and pagination footer stay pinned while only the rows scroll — see **`patterns/list-dense-scan.md`**. Omit on pages that should flow and scroll naturally (forms, dashboards).
51
53
 
52
54
  **Column-count → width rules** (column count is auto-detected from `Layout.Column` children):
53
55
 
@@ -111,15 +113,29 @@ import { Button, Layout, Tabs } from "@tailor-platform/app-shell";
111
113
 
112
114
  **Import:** `import { SidebarLayout } from '@tailor-platform/app-shell'`
113
115
  **Purpose:** Top-level layout that mounts the sidebar and renders the page outlet.
114
- **API:** `SidebarLayoutProps` — sidebar config, header config, content slot. Used in `App.tsx`.
116
+ **API:** `SidebarLayoutProps` — `sidebar`, `header`, and `children` are full-region slots (each defaults to a built-in). `sidebar` defaults to `SidebarLayout.DefaultSidebar`; `header` defaults to `SidebarLayout.DefaultHeader`. Used in `App.tsx`.
115
117
  **Used in patterns:** consumed by AppShell init, not directly by page patterns. See `project-setup.md`.
116
118
 
117
- ### `DefaultSidebar`, `SidebarGroup`, `SidebarItem`, `SidebarSeparator`
119
+ ### `SidebarLayout.DefaultHeader` (`DefaultHeader`)
120
+
121
+ **Import:** `import { SidebarLayout } from '@tailor-platform/app-shell'` → `SidebarLayout.DefaultHeader` (also top-level `DefaultHeader`)
122
+ **Purpose:** The built-in top bar (trigger + breadcrumb, plus an `actions` cluster). Drop into `SidebarLayout`'s `header` slot to extend the header without rebuilding it.
123
+ **API:** `actions?: ReactNode | ReactNode[]` — the right-hand cluster (opinionated flex row). **Defaults to `[<AppearanceSwitcher />]`, and passing `actions` REPLACES the switcher** — include `<AppearanceSwitcher />` in the array to keep it. `actions={[]}` = empty right side.
124
+ **Used in patterns:** project-level header customization (notification bell, user menu). See `project-setup.md`.
125
+
126
+ ### `SidebarLayout.DefaultSidebar` (`DefaultSidebar`), `SidebarGroup`, `SidebarItem`, `SidebarSeparator`
118
127
 
119
- **Import:** `import { DefaultSidebar, SidebarGroup, SidebarItem, SidebarSeparator } from '@tailor-platform/app-shell'`
128
+ **Import:** `import { SidebarLayout, SidebarGroup, SidebarItem, SidebarSeparator } from '@tailor-platform/app-shell'` → `SidebarLayout.DefaultSidebar` (also top-level `DefaultSidebar`)
120
129
  **Purpose:** Sidebar composition. `DefaultSidebar` auto-resolves nav items from `appShellPageProps.meta` on each page; the others let you customize manually.
121
130
  **Used in patterns:** sidebar is a project-level concern. See `project-setup.md`.
122
131
 
132
+ ### `AppearanceSwitcher`
133
+
134
+ **Import:** `import { AppearanceSwitcher } from '@tailor-platform/app-shell'`
135
+ **Purpose:** Palette-icon dropdown for Light/Dark/System color mode. The default `SidebarLayout.DefaultHeader` action; also usable standalone (e.g. a sidebar footer). Include it in `DefaultHeader`'s `actions` array when customizing the header to keep it visible.
136
+ **API:** No props — reads/writes theme via `useTheme`.
137
+ **Used in patterns:** project-level. See `project-setup.md`.
138
+
123
139
  ### `CommandPalette`
124
140
 
125
141
  **Import:** `import { CommandPalette } from '@tailor-platform/app-shell'`
@@ -78,7 +78,7 @@ export const columns: Column<Order>[] = [
78
78
 
79
79
  ```tsx
80
80
  /* pattern: list/dense-scan */
81
- import { DataTable, useDataTable, Button, Input } from "@tailor-platform/app-shell";
81
+ import { DataTable, Layout, useDataTable, Button, Input } from "@tailor-platform/app-shell";
82
82
  import type { Order } from "./columns";
83
83
  import { columns } from "./columns";
84
84
  import type { DataTableData } from "@tailor-platform/app-shell";
@@ -92,19 +92,46 @@ export default function DenseScanList({ data, onCreateClick }: Props) {
92
92
  const table = useDataTable({ data, columns });
93
93
 
94
94
  return (
95
- <div className="space-y-4">
96
- <div className="flex items-center justify-between">
97
- <Input placeholder="Search orders..." className="max-w-sm" />
98
- <Button onClick={onCreateClick}>Create Order</Button>
99
- </div>
100
- <DataTable.Root value={table}>
101
- <DataTable.Table />
102
- </DataTable.Root>
103
- </div>
95
+ // `fill` pins the page chrome for table-first pages: the title, toolbar,
96
+ // column header row, and pagination footer stay visible at every viewport
97
+ // height — only the table's rows region scrolls. Omit `fill` on pages
98
+ // that should flow and scroll naturally (forms, dashboards, articles).
99
+ <Layout fill>
100
+ <Layout.Header
101
+ title="Orders"
102
+ actions={[
103
+ <Button key="create" onClick={onCreateClick}>
104
+ Create Order
105
+ </Button>,
106
+ ]}
107
+ />
108
+ <Layout.Column>
109
+ <DataTable.Root value={table}>
110
+ <DataTable.Toolbar>
111
+ <Input placeholder="Search orders..." className="max-w-sm" />
112
+ </DataTable.Toolbar>
113
+ <DataTable.Table />
114
+ <DataTable.Footer>
115
+ <DataTable.Pagination />
116
+ </DataTable.Footer>
117
+ </DataTable.Root>
118
+ </Layout.Column>
119
+ </Layout>
104
120
  );
105
121
  }
106
122
  ```
107
123
 
124
+ ## Page Layout & Internal Scrolling
125
+
126
+ Table-first pages should pin their chrome and scroll only the rows region. Wrap the page in `<Layout fill>`:
127
+
128
+ - `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
129
+ - 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
130
+ - When the current page of rows fits, nothing stretches and no scrollbar appears — short tables render identically with or without `fill`
131
+ - Requires no extra styling on the page: the height chain (`AppShell` content area → `Layout fill` → `Layout.Column` → `DataTable.Root`) is wired by the components
132
+
133
+ Omit `fill` on pages that should flow and scroll naturally (forms, dashboards, articles) — the AppShell content area scrolls those.
134
+
108
135
  ## Variants
109
136
 
110
137
  - **Toolbar chips only (`DataTable.Filters`)** — best when filters map cleanly to typed column metadata / enum facets
@@ -117,6 +144,7 @@ export default function DenseScanList({ data, onCreateClick }: Props) {
117
144
 
118
145
  - Column count: 4-8 recommended
119
146
  - Must include pagination — never render unbounded lists
147
+ - Table-first pages use `<Layout fill>` so title/toolbar/header/footer stay pinned and only rows scroll
120
148
  - Status Badge colors must use design system tokens (variant prop)
121
149
  - Bulk actions toolbar appears only when ≥1 row is selected
122
150
  - Whole row is clickable via `onClickRow`; no per-row "View" / "Open" buttons
@@ -125,6 +153,7 @@ export default function DenseScanList({ data, onCreateClick }: Props) {
125
153
  ## Anti-patterns
126
154
 
127
155
  - Building a bespoke table + custom pagination instead of `DataTable` + `useCollectionVariables`
156
+ - Hand-rolled `max-height`/`overflow` wrappers around `DataTable` to contain scrolling — use `<Layout fill>` instead
128
157
  - Tabs that mutate only local UI state while pagination/filters assume the full server set
129
158
  - Using `<table>` directly instead of `<DataTable>` for live collections
130
159
  - Client-side filtering on 1000+ records without server-side support