@tailor-platform/app-shell 1.14.0 → 1.16.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/LICENSE.md +21 -0
- package/dist/app-shell-core.css +1 -1
- package/dist/app-shell.d.ts +1203 -316
- package/dist/app-shell.js +4562 -3307
- package/dist/{appshell-CtS8Y3LW.js → appshell-B0wh5Lq0.js} +850 -549
- package/dist/style-entry.d.ts +118 -98
- package/dist/testing.d.ts +118 -98
- package/dist/testing.js +1 -1
- package/dist/themes/_template.css +1 -1
- package/dist/vite-plugin.d.ts +118 -98
- package/package.json +13 -13
- package/skills/app-shell-patterns/SKILL.md +108 -24
- package/skills/app-shell-patterns/references/api/create-ai-gateway-client.md +115 -0
- package/skills/app-shell-patterns/references/api/create-typed-paths.md +117 -0
- package/skills/app-shell-patterns/references/api/define-i18n-labels.md +143 -0
- package/skills/app-shell-patterns/references/api/define-module.md +247 -0
- package/skills/app-shell-patterns/references/api/define-resource.md +307 -0
- package/skills/app-shell-patterns/references/api/guards/hidden.md +202 -0
- package/skills/app-shell-patterns/references/api/guards/overview.md +354 -0
- package/skills/app-shell-patterns/references/api/guards/pass.md +129 -0
- package/skills/app-shell-patterns/references/api/guards/redirect-to.md +307 -0
- package/skills/app-shell-patterns/references/api/router/use-location.md +240 -0
- package/skills/app-shell-patterns/references/api/router/use-navigate.md +138 -0
- package/skills/app-shell-patterns/references/api/router/use-params.md +218 -0
- package/skills/app-shell-patterns/references/api/use-ai-chat.md +110 -0
- package/skills/app-shell-patterns/references/api/use-app-shell-config.md +126 -0
- package/skills/app-shell-patterns/references/api/use-app-shell-data.md +146 -0
- package/skills/app-shell-patterns/references/api/use-app-shell.md +143 -0
- package/skills/app-shell-patterns/references/api/use-auth.md +145 -0
- package/skills/app-shell-patterns/references/api/use-override-breadcrumb.md +74 -0
- package/skills/app-shell-patterns/references/api/use-page-meta.md +96 -0
- package/skills/app-shell-patterns/references/api/use-register-command-palette-actions.md +90 -0
- package/skills/app-shell-patterns/references/api/use-resolved-locale.md +67 -0
- package/skills/app-shell-patterns/references/api/use-route-error.md +214 -0
- package/skills/app-shell-patterns/references/api/use-theme.md +108 -0
- package/skills/app-shell-patterns/references/api/use-time-zone.md +80 -0
- package/skills/app-shell-patterns/references/api/use-toast.md +107 -0
- package/skills/app-shell-patterns/references/components/action-panel.md +168 -0
- package/skills/app-shell-patterns/references/components/activity-card.md +142 -0
- package/skills/app-shell-patterns/references/components/ai-chat.md +315 -0
- package/skills/app-shell-patterns/references/components/alert.md +202 -0
- package/skills/app-shell-patterns/references/components/app-shell.md +530 -0
- package/skills/app-shell-patterns/references/components/appearance-switcher.md +87 -0
- package/skills/app-shell-patterns/references/components/attachment.md +138 -0
- package/skills/app-shell-patterns/references/components/autocomplete.md +278 -0
- package/skills/app-shell-patterns/references/components/avatar.md +80 -0
- package/skills/app-shell-patterns/references/components/badge.md +309 -0
- package/skills/app-shell-patterns/references/components/button.md +164 -0
- package/skills/app-shell-patterns/references/components/card.md +109 -0
- package/skills/app-shell-patterns/references/components/checkbox.md +169 -0
- package/skills/app-shell-patterns/references/components/combobox.md +318 -0
- package/skills/app-shell-patterns/references/components/command-palette.md +413 -0
- package/skills/app-shell-patterns/references/components/csv-importer.md +214 -0
- package/skills/app-shell-patterns/references/components/data-table.md +953 -0
- package/skills/app-shell-patterns/references/components/date-picker.md +248 -0
- package/skills/app-shell-patterns/references/components/default-header.md +75 -0
- package/skills/app-shell-patterns/references/components/default-sidebar.md +136 -0
- package/skills/app-shell-patterns/references/components/description-card.md +569 -0
- package/skills/app-shell-patterns/references/components/dialog.md +164 -0
- package/skills/app-shell-patterns/references/components/document-progress-card.md +154 -0
- package/skills/app-shell-patterns/references/components/form.md +378 -0
- package/skills/app-shell-patterns/references/components/global-header-layout.md +132 -0
- package/skills/app-shell-patterns/references/components/grid.md +184 -0
- package/skills/app-shell-patterns/references/components/input.md +83 -0
- package/skills/app-shell-patterns/references/components/layout.md +539 -0
- package/skills/app-shell-patterns/references/components/menu.md +218 -0
- package/skills/app-shell-patterns/references/components/metric-card.md +94 -0
- package/skills/app-shell-patterns/references/components/select.md +283 -0
- package/skills/app-shell-patterns/references/components/sheet.md +237 -0
- package/skills/app-shell-patterns/references/components/sidebar-group.md +466 -0
- package/skills/app-shell-patterns/references/components/sidebar-item.md +458 -0
- package/skills/app-shell-patterns/references/components/sidebar-layout.md +258 -0
- package/skills/app-shell-patterns/references/components/spinner.md +81 -0
- package/skills/app-shell-patterns/references/components/table.md +188 -0
- package/skills/app-shell-patterns/references/components/tabs.md +158 -0
- package/skills/app-shell-patterns/references/components/textarea.md +122 -0
- package/skills/app-shell-patterns/references/components/timeline.md +317 -0
- package/skills/app-shell-patterns/references/components/toolbar.md +138 -0
- package/skills/app-shell-patterns/references/components/tooltip.md +144 -0
- package/skills/app-shell-patterns/references/components/with-guard.md +445 -0
- package/skills/app-shell-patterns/references/concepts/authentication.md +301 -0
- package/skills/app-shell-patterns/references/concepts/custom-components.md +59 -0
- package/skills/app-shell-patterns/references/concepts/file-based-routing.md +350 -0
- package/skills/app-shell-patterns/references/{fundamental → concepts}/graphql.md +11 -4
- package/skills/app-shell-patterns/references/concepts/modules-and-resources.md +229 -0
- package/skills/app-shell-patterns/references/concepts/routing-navigation.md +387 -0
- package/skills/app-shell-patterns/references/concepts/sidebar-navigation.md +388 -0
- package/skills/app-shell-patterns/references/{fundamental/design-system.md → concepts/styling-theming.md} +260 -105
- package/skills/app-shell-patterns/references/migrations.md +34 -0
- package/skills/app-shell-patterns/references/pages/document-detail.md +1125 -0
- package/skills/app-shell-patterns/references/patterns/form-composer.md +44 -46
- package/skills/app-shell-patterns/references/patterns/form-modal.md +39 -100
- package/skills/app-shell-patterns/references/patterns/form-sectioned.md +12 -38
- package/skills/app-shell-patterns/references/patterns/form-single-page.md +11 -32
- package/skills/app-shell-patterns/references/patterns/form-wizard.md +12 -53
- package/skills/app-shell-patterns/references/patterns/interaction-confirm.md +11 -27
- package/skills/app-shell-patterns/references/patterns/interaction-multi-select.md +14 -28
- package/skills/app-shell-patterns/references/patterns/interaction-toast.md +10 -34
- package/skills/app-shell-patterns/references/patterns/list-dense-scan.md +11 -86
- package/skills/app-shell-patterns/references/fundamental/components.md +0 -981
- package/skills/app-shell-patterns/references/patterns/detail-hero-with-actions.md +0 -201
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: SidebarLayout
|
|
3
|
+
description: Default AppShell layout with a built-in sidebar, content header, optional app-wide top bar, and an escape-hatch body slot
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- GENERATED by docs-kit from docs-src/components/sidebar-layout.docs.outline.md — do not edit by hand. -->
|
|
7
|
+
|
|
8
|
+
# SidebarLayout
|
|
9
|
+
|
|
10
|
+
`SidebarLayout` is the default AppShell layout. By default it renders:
|
|
11
|
+
|
|
12
|
+
- `SidebarLayout.DefaultSidebar`
|
|
13
|
+
- `SidebarLayout.DefaultHeader`
|
|
14
|
+
- the current route outlet
|
|
15
|
+
|
|
16
|
+
You can replace the sidebar, replace the content header, add a full-width `topBar` above the whole shell, or take over the entire region beside the sidebar with the `body` slot.
|
|
17
|
+
|
|
18
|
+
If you want the opinionated "global app header + icon rail" mode, use [`GlobalHeaderLayout`](./global-header-layout.md), which is built on top of `SidebarLayout`.
|
|
19
|
+
|
|
20
|
+
## Import
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
import { SidebarLayout } from "@tailor-platform/app-shell";
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Basic usage
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import { AppShell, SidebarLayout } from "@tailor-platform/app-shell";
|
|
30
|
+
|
|
31
|
+
function App() {
|
|
32
|
+
return (
|
|
33
|
+
<AppShell modules={modules}>
|
|
34
|
+
<SidebarLayout />
|
|
35
|
+
</AppShell>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
This gives you:
|
|
41
|
+
|
|
42
|
+
- ✅ Responsive sidebar with auto-generated navigation from modules
|
|
43
|
+
- ✅ Breadcrumb navigation
|
|
44
|
+
- ✅ Theme toggle via the built-in header
|
|
45
|
+
- ✅ Mobile-friendly collapse behavior
|
|
46
|
+
|
|
47
|
+
## Props
|
|
48
|
+
|
|
49
|
+
### Common props
|
|
50
|
+
|
|
51
|
+
| Prop | Type | Default | Description |
|
|
52
|
+
| ------------- | ----------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
53
|
+
| `sidebar` | `React.ReactNode` | `<SidebarLayout.DefaultSidebar />` | Replaces the entire sidebar region. |
|
|
54
|
+
| `defaultOpen` | `boolean` | `true` | Whether the sidebar starts expanded on desktop. |
|
|
55
|
+
| `collapsible` | `boolean` | `true` | Whether the sidebar can be collapsed. When `false`, toggle controls are hidden and `defaultOpen` is ignored. |
|
|
56
|
+
| `topBar` | `React.ReactNode` | - | Full-width bar above both the sidebar and the content region. |
|
|
57
|
+
|
|
58
|
+
### Default content mode
|
|
59
|
+
|
|
60
|
+
Use this when AppShell should keep owning the content column.
|
|
61
|
+
|
|
62
|
+
| Prop | Type | Default | Description |
|
|
63
|
+
| ---------- | --------------------------------------------------------------- | --------------------------------- | ----------------------------------------------------------- |
|
|
64
|
+
| `header` | `React.ReactNode` | `<SidebarLayout.DefaultHeader />` | Replaces the content header above the main column. |
|
|
65
|
+
| `children` | `(props: { Outlet: () => React.ReactNode }) => React.ReactNode` | current route outlet | Custom content renderer that wraps or surrounds the outlet. |
|
|
66
|
+
|
|
67
|
+
### Body-slot mode
|
|
68
|
+
|
|
69
|
+
Use this when you need to compose your own columns beside the sidebar.
|
|
70
|
+
|
|
71
|
+
| Prop | Type | Default | Description |
|
|
72
|
+
| ------ | ----------------- | -------- | ------------------------------------------------------------------------------------------------------------- |
|
|
73
|
+
| `body` | `React.ReactNode` | required | Replaces everything to the right of the sidebar. Compose it with `ContentContainer`, `Outlet`, and `Trigger`. |
|
|
74
|
+
|
|
75
|
+
`body` is mutually exclusive with `header` and `children`, because it replaces the region those props normally describe.
|
|
76
|
+
|
|
77
|
+
## Extending the built-in header
|
|
78
|
+
|
|
79
|
+
The supported "small customization" path is to pass `SidebarLayout.DefaultHeader` into the `header` slot and fill its `actions` cluster.
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
import { AppearanceSwitcher, Button, SidebarLayout } from "@tailor-platform/app-shell";
|
|
83
|
+
import { BellIcon } from "lucide-react";
|
|
84
|
+
|
|
85
|
+
<SidebarLayout
|
|
86
|
+
header={
|
|
87
|
+
<SidebarLayout.DefaultHeader
|
|
88
|
+
actions={[
|
|
89
|
+
<Button key="notifications" variant="outline" size="icon" aria-label="Notifications">
|
|
90
|
+
<BellIcon className="size-4" />
|
|
91
|
+
</Button>,
|
|
92
|
+
<AppearanceSwitcher key="appearance" />,
|
|
93
|
+
]}
|
|
94
|
+
/>
|
|
95
|
+
}
|
|
96
|
+
/>;
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`actions` replaces the entire right-hand cluster. If you still want the built-in appearance switcher, include `<AppearanceSwitcher />` yourself.
|
|
100
|
+
|
|
101
|
+
## Replacing the sidebar
|
|
102
|
+
|
|
103
|
+
Pass `sidebar` to replace the whole left-side region. The built-in component is [`SidebarLayout.DefaultSidebar`](./default-sidebar.md), also exported as `DefaultSidebar`.
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
import {
|
|
107
|
+
SidebarGroup,
|
|
108
|
+
SidebarItem,
|
|
109
|
+
SidebarLayout,
|
|
110
|
+
SidebarSeparator,
|
|
111
|
+
} from "@tailor-platform/app-shell";
|
|
112
|
+
|
|
113
|
+
<SidebarLayout
|
|
114
|
+
sidebar={
|
|
115
|
+
<SidebarLayout.DefaultSidebar>
|
|
116
|
+
<SidebarItem to="/" />
|
|
117
|
+
<SidebarSeparator />
|
|
118
|
+
<SidebarGroup title="Main">
|
|
119
|
+
<SidebarItem to="/dashboard" />
|
|
120
|
+
<SidebarItem to="/orders" />
|
|
121
|
+
</SidebarGroup>
|
|
122
|
+
</SidebarLayout.DefaultSidebar>
|
|
123
|
+
}
|
|
124
|
+
/>;
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Adding an app-wide top bar
|
|
128
|
+
|
|
129
|
+
`topBar` renders above the whole shell, spanning both the sidebar and the content region.
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
<SidebarLayout topBar={<MyGlobalTopBar />} sidebar={<SidebarLayout.DefaultSidebar hideHeader />} />
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
When `topBar` is present, the fixed sidebar is offset below it. The bar should be `3.5rem` tall (`h-14`) to match the built-in layout.
|
|
136
|
+
|
|
137
|
+
If you specifically want AppShell's opinionated global-header mode, use [`GlobalHeaderLayout`](./global-header-layout.md), which wires the top bar, icon rail, and matching default sidebar for you.
|
|
138
|
+
|
|
139
|
+
## Owning the region beside the sidebar
|
|
140
|
+
|
|
141
|
+
Use `body` when the stock content column is too restrictive and you need your own side columns.
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
<SidebarLayout
|
|
145
|
+
body={
|
|
146
|
+
<>
|
|
147
|
+
<aside className="w-64 shrink-0 overflow-y-auto border-r">
|
|
148
|
+
<TableOfContents />
|
|
149
|
+
</aside>
|
|
150
|
+
<SidebarLayout.ContentContainer header={<SidebarLayout.DefaultHeader />}>
|
|
151
|
+
<SidebarLayout.Outlet />
|
|
152
|
+
</SidebarLayout.ContentContainer>
|
|
153
|
+
<aside className="w-96 shrink-0 overflow-y-auto border-l">
|
|
154
|
+
<AssistantPanel />
|
|
155
|
+
</aside>
|
|
156
|
+
</>
|
|
157
|
+
}
|
|
158
|
+
/>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`SidebarLayout.ContentContainer` is the stock main column: inset padding, pinned header slot, scroll region, and `useAppShellScrollContainer()` support. `SidebarLayout.Trigger` exposes the built-in sidebar toggle so custom headers do not need DOM workarounds.
|
|
162
|
+
|
|
163
|
+
## Reading and controlling the sidebar
|
|
164
|
+
|
|
165
|
+
Use `useAppShellSidebar()` to read and control the sidebar state instead of observing internal DOM attributes or clicking the trigger through the DOM.
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
import { useAppShellSidebar } from "@tailor-platform/app-shell";
|
|
169
|
+
|
|
170
|
+
function CustomHeader() {
|
|
171
|
+
const { open, isMobile, toggle } = useAppShellSidebar();
|
|
172
|
+
|
|
173
|
+
return (
|
|
174
|
+
<div className="flex items-center justify-between border-b px-4 py-3">
|
|
175
|
+
<button type="button" onClick={toggle}>
|
|
176
|
+
{open ? "Hide navigation" : "Show navigation"}
|
|
177
|
+
</button>
|
|
178
|
+
<span>{isMobile ? "Mobile" : "Desktop"}</span>
|
|
179
|
+
</div>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Outside `SidebarLayout`, the hook returns a safe fallback (`open: true`, `isMobile: false`, no-op setters) rather than throwing.
|
|
185
|
+
|
|
186
|
+
## Reusing the built-in breadcrumb
|
|
187
|
+
|
|
188
|
+
`DynamicBreadcrumb` is exported when you want the same route-driven breadcrumb in a custom header or top bar:
|
|
189
|
+
|
|
190
|
+
```tsx
|
|
191
|
+
import { DynamicBreadcrumb, SidebarLayout } from "@tailor-platform/app-shell";
|
|
192
|
+
|
|
193
|
+
<header className="flex items-center gap-3 border-b px-4 py-3">
|
|
194
|
+
<SidebarLayout.Trigger />
|
|
195
|
+
<DynamicBreadcrumb />
|
|
196
|
+
</header>;
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Accessing the content scroll container
|
|
200
|
+
|
|
201
|
+
The AppShell is viewport-bounded (`h-svh`), so the **document itself does not scroll** — the content area does. Code that previously relied on `window`/document scroll (reading `window.scrollY`, listening to `window`'s `scroll` event, `window.scrollTo(...)`, or an `IntersectionObserver` with the default viewport root) should target the content scroll container instead.
|
|
202
|
+
|
|
203
|
+
Use the `useAppShellScrollContainer()` hook to get a ref to that element from any page:
|
|
204
|
+
|
|
205
|
+
```tsx
|
|
206
|
+
import { useAppShellScrollContainer } from "@tailor-platform/app-shell";
|
|
207
|
+
import { useEffect, useState } from "react";
|
|
208
|
+
|
|
209
|
+
function ReadingProgress() {
|
|
210
|
+
const scrollRef = useAppShellScrollContainer();
|
|
211
|
+
const [progress, setProgress] = useState(0);
|
|
212
|
+
|
|
213
|
+
useEffect(() => {
|
|
214
|
+
const el = scrollRef.current;
|
|
215
|
+
if (!el) return;
|
|
216
|
+
const onScroll = () => {
|
|
217
|
+
const max = el.scrollHeight - el.clientHeight;
|
|
218
|
+
setProgress(max > 0 ? el.scrollTop / max : 0);
|
|
219
|
+
};
|
|
220
|
+
onScroll();
|
|
221
|
+
el.addEventListener("scroll", onScroll, { passive: true });
|
|
222
|
+
return () => el.removeEventListener("scroll", onScroll);
|
|
223
|
+
}, [scrollRef]);
|
|
224
|
+
|
|
225
|
+
return <progress value={progress} />;
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
The same element handles imperative scrolling (`scrollRef.current?.scrollTo({ top: 0, behavior: "smooth" })`) and works as an `IntersectionObserver` root (`new IntersectionObserver(cb, { root: scrollRef.current })`).
|
|
230
|
+
|
|
231
|
+
Notes:
|
|
232
|
+
|
|
233
|
+
- The element mounts with the layout, above your page, so read `ref.current` inside an effect — it is populated by the time effects run, not during render.
|
|
234
|
+
- On a [`<Layout fill>`](./layout.md#fill-mode) page this element does **not** scroll; its children (for example a `DataTable`) manage their own scrolling.
|
|
235
|
+
- Outside a `SidebarLayout` (a fully custom layout) the returned ref's `current` is always `null` — such layouts own their own scroll region.
|
|
236
|
+
- For non-React access (CSS, tests, plain DOM) the container also carries a `data-appshell-scroll-container` attribute: `document.querySelector("[data-appshell-scroll-container]")`.
|
|
237
|
+
|
|
238
|
+
## Styling
|
|
239
|
+
|
|
240
|
+
The sidebar and layout are styled with the library's internal `astw:`-prefixed utilities. Write **plain** Tailwind utilities in your own markup — see [Styling AppShell components](../concepts/styling-theming.md#styling-appshell-components).
|
|
241
|
+
|
|
242
|
+
## Accessibility
|
|
243
|
+
|
|
244
|
+
SidebarLayout includes built-in accessibility features:
|
|
245
|
+
|
|
246
|
+
- **Keyboard navigation** for sidebar interaction
|
|
247
|
+
- **ARIA labels** on built-in controls
|
|
248
|
+
- **Focus management** when the sidebar opens on mobile
|
|
249
|
+
- **Responsive behavior** that works with keyboard, mouse, and touch input
|
|
250
|
+
|
|
251
|
+
## Related
|
|
252
|
+
|
|
253
|
+
- [AppShell](./app-shell.md) - root component
|
|
254
|
+
- [DefaultHeader](./default-header.md) - built-in content header (`SidebarLayout.DefaultHeader`)
|
|
255
|
+
- [DefaultSidebar](./default-sidebar.md) - built-in sidebar (`SidebarLayout.DefaultSidebar`)
|
|
256
|
+
- [GlobalHeaderLayout](./global-header-layout.md) - opinionated app-wide header mode built on SidebarLayout
|
|
257
|
+
- [AppearanceSwitcher](./appearance-switcher.md) - color-theme dropdown for header actions
|
|
258
|
+
- [usePageMeta](../api/use-page-meta.md) - route metadata lookup used by sidebar items
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Spinner
|
|
3
|
+
description: Lightweight loading indicator component with accessible defaults and preset sizes
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- GENERATED by docs-kit from docs-src/components/spinner.docs.outline.md — do not edit by hand. -->
|
|
7
|
+
|
|
8
|
+
# Spinner
|
|
9
|
+
|
|
10
|
+
`Spinner` is AppShell's reusable loading indicator. It renders a small rotating SVG and is used throughout the built-in UI for loading states such as `ActionPanel`, command palette search, and CSV import progress.
|
|
11
|
+
|
|
12
|
+
## Import
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { Spinner } from "@tailor-platform/app-shell";
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Basic usage
|
|
19
|
+
|
|
20
|
+
<example-preview name="basic-usage"></example-preview>
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
function BasicUsage() {
|
|
24
|
+
return <Spinner aria-label="Loading" />;
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Props
|
|
29
|
+
|
|
30
|
+
`Spinner` accepts all standard `<svg>` props plus one AppShell prop:
|
|
31
|
+
|
|
32
|
+
| Prop | Type | Default | Description |
|
|
33
|
+
| ------ | ----------------------------------- | ----------- | -------------------------------------------------------------------------- |
|
|
34
|
+
| `size` | `"xs" \| "sm" \| "default" \| "lg"` | `"default"` | Preset square size. Ignored when `width` or `height` is passed explicitly. |
|
|
35
|
+
|
|
36
|
+
Preset sizes map to:
|
|
37
|
+
|
|
38
|
+
| `size` | Pixels |
|
|
39
|
+
| ----------- | ------ |
|
|
40
|
+
| `"xs"` | `12` |
|
|
41
|
+
| `"sm"` | `14` |
|
|
42
|
+
| `"default"` | `16` |
|
|
43
|
+
| `"lg"` | `20` |
|
|
44
|
+
|
|
45
|
+
## Accessibility
|
|
46
|
+
|
|
47
|
+
`Spinner` defaults to decorative output unless you give it an accessible name:
|
|
48
|
+
|
|
49
|
+
- with `aria-label` or `aria-labelledby`, it renders with `role="status"`
|
|
50
|
+
- with `aria-hidden={true}`, it stays decorative
|
|
51
|
+
- with neither, it is treated as decorative and gets `aria-hidden="true"`
|
|
52
|
+
|
|
53
|
+
Use a label when the spinner is the primary visible loading affordance:
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
<Spinner size="sm" aria-label="Loading orders" />
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
When the surrounding control already has its own accessible name, keep the spinner decorative:
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
<Button disabled>
|
|
63
|
+
<Spinner size="sm" aria-hidden />
|
|
64
|
+
Saving
|
|
65
|
+
</Button>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Sizing and styling
|
|
69
|
+
|
|
70
|
+
Pass ordinary Tailwind utilities through `className` for color and layout, or override the dimensions directly with `width` / `height`.
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
<Spinner className="text-muted-foreground" />
|
|
74
|
+
<Spinner size="lg" className="text-primary" aria-label="Generating report" />
|
|
75
|
+
<Spinner width={24} height={24} aria-label="Loading" />
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Related
|
|
79
|
+
|
|
80
|
+
- [ActionPanel](./action-panel.md) - uses `Spinner` for per-row loading states
|
|
81
|
+
- [WithGuard](./with-guard.md) - common place to render a loading spinner while guards resolve
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Table
|
|
3
|
+
description: Semantic HTML table with pre-styled sub-components
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- GENERATED by docs-kit from docs-src/components/table.docs.outline.md — do not edit by hand. -->
|
|
7
|
+
|
|
8
|
+
# Table
|
|
9
|
+
|
|
10
|
+
The `Table` component provides a set of pre-styled, semantic HTML table sub-components. It wraps a standard `<table>` element in a horizontally scrollable container.
|
|
11
|
+
|
|
12
|
+
## Import
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { Table } from "@tailor-platform/app-shell";
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Basic Usage
|
|
19
|
+
|
|
20
|
+
<example-preview name="basic-usage"></example-preview>
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
function BasicUsage() {
|
|
24
|
+
return (
|
|
25
|
+
<Table.Root>
|
|
26
|
+
<Table.Header>
|
|
27
|
+
<Table.Row>
|
|
28
|
+
<Table.Head>Name</Table.Head>
|
|
29
|
+
<Table.Head>Email</Table.Head>
|
|
30
|
+
<Table.Head>Role</Table.Head>
|
|
31
|
+
</Table.Row>
|
|
32
|
+
</Table.Header>
|
|
33
|
+
<Table.Body>
|
|
34
|
+
<Table.Row>
|
|
35
|
+
<Table.Cell>Alice</Table.Cell>
|
|
36
|
+
<Table.Cell>alice@example.com</Table.Cell>
|
|
37
|
+
<Table.Cell>Admin</Table.Cell>
|
|
38
|
+
</Table.Row>
|
|
39
|
+
</Table.Body>
|
|
40
|
+
</Table.Root>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Sub-components
|
|
46
|
+
|
|
47
|
+
| Sub-component | HTML element | Description |
|
|
48
|
+
| --------------- | ------------ | -------------------------------------- |
|
|
49
|
+
| `Table.Root` | `<table>` | Root element with scrollable container |
|
|
50
|
+
| `Table.Header` | `<thead>` | Table header section |
|
|
51
|
+
| `Table.Body` | `<tbody>` | Table body section |
|
|
52
|
+
| `Table.Footer` | `<tfoot>` | Table footer section |
|
|
53
|
+
| `Table.Row` | `<tr>` | Table row (hover and selection styles) |
|
|
54
|
+
| `Table.Head` | `<th>` | Header cell |
|
|
55
|
+
| `Table.Cell` | `<td>` | Data cell |
|
|
56
|
+
| `Table.Caption` | `<caption>` | Table caption |
|
|
57
|
+
|
|
58
|
+
## Props
|
|
59
|
+
|
|
60
|
+
### Table.Root Props
|
|
61
|
+
|
|
62
|
+
| Prop | Type | Default | Description |
|
|
63
|
+
| -------------------- | --------------------------- | ------- | ----------------------------------------------------------------- |
|
|
64
|
+
| `className` | `string` | - | Additional CSS classes for the `<table>` element |
|
|
65
|
+
| `containerClassName` | `string` | - | Additional CSS classes for the outer scrollable `<div>` container |
|
|
66
|
+
| `containerRef` | `React.Ref<HTMLDivElement>` | - | Ref forwarded to the outer scrollable `<div>` container |
|
|
67
|
+
|
|
68
|
+
All other standard HTML `<table>` props are accepted.
|
|
69
|
+
|
|
70
|
+
All other sub-components (`Table.Header`, `Table.Body`, `Table.Footer`, `Table.Row`, `Table.Head`, `Table.Cell`, `Table.Caption`) accept `className` and their corresponding standard HTML element props.
|
|
71
|
+
|
|
72
|
+
`Table.Head` and `Table.Cell` also accept `align?: "left" | "center" | "right"` for semantic text alignment without relying on utility-class overrides.
|
|
73
|
+
|
|
74
|
+
## Examples
|
|
75
|
+
|
|
76
|
+
### With Footer
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
<Table.Root>
|
|
80
|
+
<Table.Header>
|
|
81
|
+
<Table.Row>
|
|
82
|
+
<Table.Head>Product</Table.Head>
|
|
83
|
+
<Table.Head>Qty</Table.Head>
|
|
84
|
+
<Table.Head>Price</Table.Head>
|
|
85
|
+
</Table.Row>
|
|
86
|
+
</Table.Header>
|
|
87
|
+
<Table.Body>
|
|
88
|
+
<Table.Row>
|
|
89
|
+
<Table.Cell>Widget A</Table.Cell>
|
|
90
|
+
<Table.Cell>10</Table.Cell>
|
|
91
|
+
<Table.Cell>$100.00</Table.Cell>
|
|
92
|
+
</Table.Row>
|
|
93
|
+
<Table.Row>
|
|
94
|
+
<Table.Cell>Widget B</Table.Cell>
|
|
95
|
+
<Table.Cell>5</Table.Cell>
|
|
96
|
+
<Table.Cell>$75.00</Table.Cell>
|
|
97
|
+
</Table.Row>
|
|
98
|
+
</Table.Body>
|
|
99
|
+
<Table.Footer>
|
|
100
|
+
<Table.Row>
|
|
101
|
+
<Table.Cell colSpan={2}>Total</Table.Cell>
|
|
102
|
+
<Table.Cell>$175.00</Table.Cell>
|
|
103
|
+
</Table.Row>
|
|
104
|
+
</Table.Footer>
|
|
105
|
+
</Table.Root>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### With Caption
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
<Table.Root>
|
|
112
|
+
<Table.Caption>Order line items</Table.Caption>
|
|
113
|
+
<Table.Header>
|
|
114
|
+
<Table.Row>
|
|
115
|
+
<Table.Head>Item</Table.Head>
|
|
116
|
+
<Table.Head>Status</Table.Head>
|
|
117
|
+
</Table.Row>
|
|
118
|
+
</Table.Header>
|
|
119
|
+
<Table.Body>
|
|
120
|
+
<Table.Row>
|
|
121
|
+
<Table.Cell>Item 1</Table.Cell>
|
|
122
|
+
<Table.Cell>Shipped</Table.Cell>
|
|
123
|
+
</Table.Row>
|
|
124
|
+
</Table.Body>
|
|
125
|
+
</Table.Root>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Right-aligned numeric columns
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
<Table.Root>
|
|
132
|
+
<Table.Header>
|
|
133
|
+
<Table.Row>
|
|
134
|
+
<Table.Head>Item</Table.Head>
|
|
135
|
+
<Table.Head align="right">Amount</Table.Head>
|
|
136
|
+
</Table.Row>
|
|
137
|
+
</Table.Header>
|
|
138
|
+
<Table.Body>
|
|
139
|
+
<Table.Row>
|
|
140
|
+
<Table.Cell>Widget A</Table.Cell>
|
|
141
|
+
<Table.Cell align="right">123</Table.Cell>
|
|
142
|
+
</Table.Row>
|
|
143
|
+
</Table.Body>
|
|
144
|
+
</Table.Root>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Center-aligned status column
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
<Table.Root>
|
|
151
|
+
<Table.Header>
|
|
152
|
+
<Table.Row>
|
|
153
|
+
<Table.Head>Item</Table.Head>
|
|
154
|
+
<Table.Head align="center">Status</Table.Head>
|
|
155
|
+
</Table.Row>
|
|
156
|
+
</Table.Header>
|
|
157
|
+
<Table.Body>
|
|
158
|
+
<Table.Row>
|
|
159
|
+
<Table.Cell>Widget A</Table.Cell>
|
|
160
|
+
<Table.Cell align="center">Active</Table.Cell>
|
|
161
|
+
</Table.Row>
|
|
162
|
+
</Table.Body>
|
|
163
|
+
</Table.Root>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Constrained Height with Scroll
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
<Table.Root containerClassName="max-h-64 overflow-y-auto">
|
|
170
|
+
<Table.Header>
|
|
171
|
+
<Table.Row>
|
|
172
|
+
<Table.Head>Name</Table.Head>
|
|
173
|
+
</Table.Row>
|
|
174
|
+
</Table.Header>
|
|
175
|
+
<Table.Body>
|
|
176
|
+
{items.map((item) => (
|
|
177
|
+
<Table.Row key={item.id}>
|
|
178
|
+
<Table.Cell>{item.name}</Table.Cell>
|
|
179
|
+
</Table.Row>
|
|
180
|
+
))}
|
|
181
|
+
</Table.Body>
|
|
182
|
+
</Table.Root>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Related Components
|
|
186
|
+
|
|
187
|
+
- [Badge](./badge.md) - Display status inside table cells
|
|
188
|
+
- [Layout](./layout.md) - Place tables inside page layouts
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Tabs
|
|
3
|
+
description: Tab navigation with a compound component API
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- GENERATED by docs-kit from docs-src/components/tabs.docs.outline.md — do not edit by hand. -->
|
|
7
|
+
|
|
8
|
+
# Tabs
|
|
9
|
+
|
|
10
|
+
The `Tabs` component provides tab-based navigation for toggling between related panels on the same page. It is backed by Base UI's Tabs primitive.
|
|
11
|
+
|
|
12
|
+
## Import
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { Tabs } from "@tailor-platform/app-shell";
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Basic Usage
|
|
19
|
+
|
|
20
|
+
<example-preview name="basic-usage"></example-preview>
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
function BasicUsage() {
|
|
24
|
+
return (
|
|
25
|
+
<Tabs.Root defaultValue="overview">
|
|
26
|
+
<Tabs.List>
|
|
27
|
+
<Tabs.Tab value="overview">Overview</Tabs.Tab>
|
|
28
|
+
<Tabs.Tab value="projects">Projects</Tabs.Tab>
|
|
29
|
+
<Tabs.Tab value="account">Account</Tabs.Tab>
|
|
30
|
+
</Tabs.List>
|
|
31
|
+
<Tabs.Panel value="overview">Overview content</Tabs.Panel>
|
|
32
|
+
<Tabs.Panel value="projects">Projects content</Tabs.Panel>
|
|
33
|
+
<Tabs.Panel value="account">Account content</Tabs.Panel>
|
|
34
|
+
</Tabs.Root>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Sub-components
|
|
40
|
+
|
|
41
|
+
| Sub-component | Description |
|
|
42
|
+
| ------------- | -------------------------------------------------------------- |
|
|
43
|
+
| `Tabs.Root` | Manages tab selection state |
|
|
44
|
+
| `Tabs.List` | Groups the individual tab buttons |
|
|
45
|
+
| `Tabs.Tab` | An interactive tab button that toggles the corresponding panel |
|
|
46
|
+
| `Tabs.Panel` | A panel displayed when the corresponding tab is active |
|
|
47
|
+
|
|
48
|
+
## Props
|
|
49
|
+
|
|
50
|
+
### Tabs.Root Props
|
|
51
|
+
|
|
52
|
+
| Prop | Type | Default | Description |
|
|
53
|
+
| --------------- | ----------------------------------- | ----------- | ------------------------------------------------------------- |
|
|
54
|
+
| `defaultValue` | `Tabs.Tab.Value` | `0` | Initial active tab value (uncontrolled) |
|
|
55
|
+
| `value` | `Tabs.Tab.Value` | - | Controlled active tab value |
|
|
56
|
+
| `onValueChange` | `(value: any) => void` | - | Callback when the active tab changes |
|
|
57
|
+
| `variant` | `'default' \| 'line' \| 'capsule'` | `'default'` | Visual style of the tabs |
|
|
58
|
+
| `size` | `'xs' \| 'sm' \| 'default' \| 'lg'` | `'default'` | Minimum size (`capsule` only) — mirrors Button's height tiers |
|
|
59
|
+
| `className` | `string` | - | Additional CSS classes for root |
|
|
60
|
+
| `children` | `React.ReactNode` | - | Tabs sub-components |
|
|
61
|
+
|
|
62
|
+
### Tabs.List Props
|
|
63
|
+
|
|
64
|
+
Accepts `className` and all standard HTML `<div>` props.
|
|
65
|
+
|
|
66
|
+
### Tabs.Tab Props
|
|
67
|
+
|
|
68
|
+
| Prop | Type | Default | Description |
|
|
69
|
+
| ---------- | ---------------- | ------- | ---------------------------------- |
|
|
70
|
+
| `value` | `Tabs.Tab.Value` | - | **Required.** The value of the tab |
|
|
71
|
+
| `disabled` | `boolean` | - | Whether the tab is disabled |
|
|
72
|
+
|
|
73
|
+
Also accepts `className` and all standard HTML `<button>` props.
|
|
74
|
+
|
|
75
|
+
### Tabs.Panel Props
|
|
76
|
+
|
|
77
|
+
| Prop | Type | Default | Description |
|
|
78
|
+
| ------------- | ---------------- | ------- | ------------------------------------------------------ |
|
|
79
|
+
| `value` | `Tabs.Tab.Value` | - | **Required.** The value matching the corresponding tab |
|
|
80
|
+
| `keepMounted` | `boolean` | `false` | Whether to keep the panel in the DOM when hidden |
|
|
81
|
+
|
|
82
|
+
Also accepts `className` and all standard HTML `<div>` props.
|
|
83
|
+
|
|
84
|
+
## Controlled Usage
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
const [activeTab, setActiveTab] = useState("overview");
|
|
88
|
+
|
|
89
|
+
<Tabs.Root value={activeTab} onValueChange={setActiveTab}>
|
|
90
|
+
<Tabs.List>
|
|
91
|
+
<Tabs.Tab value="overview">Overview</Tabs.Tab>
|
|
92
|
+
<Tabs.Tab value="details">Details</Tabs.Tab>
|
|
93
|
+
</Tabs.List>
|
|
94
|
+
<Tabs.Panel value="overview">Overview content</Tabs.Panel>
|
|
95
|
+
<Tabs.Panel value="details">Details content</Tabs.Panel>
|
|
96
|
+
</Tabs.Root>;
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Examples
|
|
100
|
+
|
|
101
|
+
### With Disabled Tab
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
<Tabs.Root defaultValue="active">
|
|
105
|
+
<Tabs.List>
|
|
106
|
+
<Tabs.Tab value="active">Active</Tabs.Tab>
|
|
107
|
+
<Tabs.Tab value="pending">Pending</Tabs.Tab>
|
|
108
|
+
<Tabs.Tab value="archived" disabled>
|
|
109
|
+
Archived
|
|
110
|
+
</Tabs.Tab>
|
|
111
|
+
</Tabs.List>
|
|
112
|
+
<Tabs.Panel value="active">Active items</Tabs.Panel>
|
|
113
|
+
<Tabs.Panel value="pending">Pending items</Tabs.Panel>
|
|
114
|
+
<Tabs.Panel value="archived">Archived items</Tabs.Panel>
|
|
115
|
+
</Tabs.Root>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Use Icons in Tab
|
|
119
|
+
|
|
120
|
+
In the `capsule` variant, a tab whose only child is an icon renders **square** —
|
|
121
|
+
its `min-width` matches its `min-height` — making it a natural fit for icon-only
|
|
122
|
+
view toggles (e.g. table/kanban). Use `size` to align the track with a sibling
|
|
123
|
+
`Button`: `size="default"` (36px) matches `Button`'s default height. Unsized
|
|
124
|
+
icons default to `16px`, so the square is consistent regardless of the icon.
|
|
125
|
+
|
|
126
|
+
> **Accessibility:** an icon-only tab has no visible text, so give each one an
|
|
127
|
+
> `aria-label`. Without it, screen readers announce an unnamed tab.
|
|
128
|
+
|
|
129
|
+
```tsx
|
|
130
|
+
<Tabs.Root defaultValue="overview" variant="capsule" size="default">
|
|
131
|
+
<Tabs.List>
|
|
132
|
+
<Tabs.Tab value="overview" aria-label="Overview">
|
|
133
|
+
<LayoutDashboardIcon />
|
|
134
|
+
</Tabs.Tab>
|
|
135
|
+
<Tabs.Tab value="projects" aria-label="Projects">
|
|
136
|
+
<FolderKanbanIcon />
|
|
137
|
+
</Tabs.Tab>
|
|
138
|
+
<Tabs.Tab value="settings" aria-label="Settings">
|
|
139
|
+
<SettingsIcon />
|
|
140
|
+
</Tabs.Tab>
|
|
141
|
+
</Tabs.List>
|
|
142
|
+
<Tabs.Panel value="overview">Overview content</Tabs.Panel>
|
|
143
|
+
<Tabs.Panel value="projects">Projects content</Tabs.Panel>
|
|
144
|
+
<Tabs.Panel value="settings">Settings content</Tabs.Panel>
|
|
145
|
+
</Tabs.Root>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Sizing the capsule variant
|
|
149
|
+
|
|
150
|
+
`size` mirrors `Button`'s height tiers, so a capsule can sit flush next to a
|
|
151
|
+
button. The value sets a **minimum** height — taller content still grows.
|
|
152
|
+
|
|
153
|
+
| `size` | Track height | Matches Button |
|
|
154
|
+
| --------- | ------------ | -------------- |
|
|
155
|
+
| `xs` | 28px | `size="xs"` |
|
|
156
|
+
| `sm` | 32px | `size="sm"` |
|
|
157
|
+
| `default` | 36px | default |
|
|
158
|
+
| `lg` | 40px | `size="lg"` |
|