@squadbase/vantage 0.0.1 → 0.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.
- package/README.md +24 -4
- package/dist/{chunk-ZGDU5YLH.js → chunk-5L2MH2NG.js} +16 -7
- package/dist/chunk-5L2MH2NG.js.map +1 -0
- package/dist/chunk-7IPAXPPY.js +51 -0
- package/dist/chunk-7IPAXPPY.js.map +1 -0
- package/dist/chunk-A2UUGASH.js +12 -0
- package/dist/chunk-A2UUGASH.js.map +1 -0
- package/dist/chunk-DTDVSFRY.js +29 -0
- package/dist/chunk-DTDVSFRY.js.map +1 -0
- package/dist/chunk-WQZYXXQW.js +2249 -0
- package/dist/chunk-WQZYXXQW.js.map +1 -0
- package/dist/{chunk-73J5ZD4C.js → chunk-YLAB6UQS.js} +17 -3
- package/dist/chunk-YLAB6UQS.js.map +1 -0
- package/dist/cli.js +607 -36
- package/dist/cli.js.map +1 -1
- package/dist/client/index.d.ts +2 -1
- package/dist/client/index.js +3 -1
- package/dist/client/index.js.map +1 -1
- package/dist/components/index.d.ts +575 -0
- package/dist/components/index.js +2097 -0
- package/dist/components/index.js.map +1 -0
- package/dist/{define-page-B6y9TOfZ.d.ts → define-page-BfhrK99G.d.ts} +5 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/markdown/index.d.ts +9 -0
- package/dist/markdown/index.js +26 -0
- package/dist/markdown/index.js.map +1 -0
- package/dist/query/index.d.ts +95 -2
- package/dist/query/index.js +102 -1
- package/dist/query/index.js.map +1 -1
- package/dist/router/index.d.ts +86 -1
- package/dist/router/index.js +71 -7
- package/dist/router/index.js.map +1 -1
- package/dist/server/node.js +6 -1
- package/dist/server/node.js.map +1 -1
- package/dist/ui/index.d.ts +316 -200
- package/dist/ui/index.js +566 -511
- package/dist/ui/index.js.map +1 -1
- package/dist/vite/index.js +2 -2
- package/docs/en/agent-skills.md +89 -0
- package/docs/en/api-and-server.md +84 -0
- package/docs/en/build-and-deploy.md +280 -0
- package/docs/en/cli-reference.md +311 -0
- package/docs/en/components.md +235 -0
- package/docs/en/data-fetching.md +142 -0
- package/docs/en/environment-variables.md +69 -0
- package/docs/en/getting-started.md +95 -0
- package/docs/en/index.md +65 -0
- package/docs/en/markdown/markdown-renderer.md +44 -0
- package/docs/en/pages-and-metadata.md +59 -0
- package/docs/en/parts/app-shell.md +54 -0
- package/docs/en/parts/dashboard-card.md +64 -0
- package/docs/en/parts/data-table.md +86 -0
- package/docs/en/parts/date-range-picker.md +41 -0
- package/docs/en/parts/echart.md +53 -0
- package/docs/en/parts/filter-bar.md +66 -0
- package/docs/en/parts/funnel-steps.md +33 -0
- package/docs/en/parts/metric-value.md +23 -0
- package/docs/en/parts/multi-select.md +27 -0
- package/docs/en/parts/page-shell.md +45 -0
- package/docs/en/parts/placeholder.md +38 -0
- package/docs/en/parts/searchable-select.md +47 -0
- package/docs/en/parts/section-header.md +20 -0
- package/docs/en/parts/segmented-control.md +25 -0
- package/docs/en/parts/sparkline.md +47 -0
- package/docs/en/parts/status-badge.md +44 -0
- package/docs/en/parts/trend-indicator.md +26 -0
- package/docs/en/routing.md +177 -0
- package/docs/en/ui/accordion.md +36 -0
- package/docs/en/ui/alert.md +31 -0
- package/docs/en/ui/badge.md +23 -0
- package/docs/en/ui/breadcrumb.md +38 -0
- package/docs/en/ui/button.md +39 -0
- package/docs/en/ui/calendar.md +28 -0
- package/docs/en/ui/card.md +35 -0
- package/docs/en/ui/checkbox.md +34 -0
- package/docs/en/ui/cn.md +34 -0
- package/docs/en/ui/collapsible.md +21 -0
- package/docs/en/ui/command.md +37 -0
- package/docs/en/ui/dialog.md +52 -0
- package/docs/en/ui/dropdown-menu.md +50 -0
- package/docs/en/ui/empty.md +22 -0
- package/docs/en/ui/error-state.md +50 -0
- package/docs/en/ui/input-group.md +30 -0
- package/docs/en/ui/input.md +28 -0
- package/docs/en/ui/label.md +19 -0
- package/docs/en/ui/loading.md +19 -0
- package/docs/en/ui/popover.md +34 -0
- package/docs/en/ui/progress.md +28 -0
- package/docs/en/ui/scroll-area.md +23 -0
- package/docs/en/ui/select.md +63 -0
- package/docs/en/ui/separator.md +15 -0
- package/docs/en/ui/sheet.md +28 -0
- package/docs/en/ui/sidebar.md +92 -0
- package/docs/en/ui/skeleton.md +16 -0
- package/docs/en/ui/slider.md +21 -0
- package/docs/en/ui/spinner.md +14 -0
- package/docs/en/ui/switch.md +19 -0
- package/docs/en/ui/table.md +24 -0
- package/docs/en/ui/tabs.md +23 -0
- package/docs/en/ui/textarea.md +15 -0
- package/docs/en/ui/toggle-group.md +26 -0
- package/docs/en/ui/toggle.md +19 -0
- package/docs/en/ui/tooltip.md +22 -0
- package/docs/en/ui/use-is-mobile.md +18 -0
- package/docs/en/ui-and-theming.md +95 -0
- package/docs/index.json +1346 -0
- package/docs/ja/agent-skills.md +87 -0
- package/docs/ja/api-and-server.md +84 -0
- package/docs/ja/build-and-deploy.md +279 -0
- package/docs/ja/cli-reference.md +305 -0
- package/docs/ja/components.md +231 -0
- package/docs/ja/data-fetching.md +142 -0
- package/docs/ja/environment-variables.md +68 -0
- package/docs/ja/getting-started.md +95 -0
- package/docs/ja/index.md +65 -0
- package/docs/ja/markdown/markdown-renderer.md +43 -0
- package/docs/ja/pages-and-metadata.md +59 -0
- package/docs/ja/parts/app-shell.md +54 -0
- package/docs/ja/parts/dashboard-card.md +64 -0
- package/docs/ja/parts/data-table.md +85 -0
- package/docs/ja/parts/date-range-picker.md +41 -0
- package/docs/ja/parts/echart.md +52 -0
- package/docs/ja/parts/filter-bar.md +64 -0
- package/docs/ja/parts/funnel-steps.md +33 -0
- package/docs/ja/parts/metric-value.md +25 -0
- package/docs/ja/parts/multi-select.md +27 -0
- package/docs/ja/parts/page-shell.md +45 -0
- package/docs/ja/parts/placeholder.md +37 -0
- package/docs/ja/parts/searchable-select.md +47 -0
- package/docs/ja/parts/section-header.md +20 -0
- package/docs/ja/parts/segmented-control.md +26 -0
- package/docs/ja/parts/sparkline.md +47 -0
- package/docs/ja/parts/status-badge.md +44 -0
- package/docs/ja/parts/trend-indicator.md +26 -0
- package/docs/ja/routing.md +175 -0
- package/docs/ja/ui/accordion.md +37 -0
- package/docs/ja/ui/alert.md +31 -0
- package/docs/ja/ui/badge.md +23 -0
- package/docs/ja/ui/breadcrumb.md +38 -0
- package/docs/ja/ui/button.md +39 -0
- package/docs/ja/ui/calendar.md +28 -0
- package/docs/ja/ui/card.md +35 -0
- package/docs/ja/ui/checkbox.md +34 -0
- package/docs/ja/ui/cn.md +34 -0
- package/docs/ja/ui/collapsible.md +21 -0
- package/docs/ja/ui/command.md +36 -0
- package/docs/ja/ui/dialog.md +51 -0
- package/docs/ja/ui/dropdown-menu.md +50 -0
- package/docs/ja/ui/empty.md +22 -0
- package/docs/ja/ui/error-state.md +50 -0
- package/docs/ja/ui/input-group.md +30 -0
- package/docs/ja/ui/input.md +28 -0
- package/docs/ja/ui/label.md +19 -0
- package/docs/ja/ui/loading.md +19 -0
- package/docs/ja/ui/popover.md +34 -0
- package/docs/ja/ui/progress.md +28 -0
- package/docs/ja/ui/scroll-area.md +24 -0
- package/docs/ja/ui/select.md +64 -0
- package/docs/ja/ui/separator.md +15 -0
- package/docs/ja/ui/sheet.md +28 -0
- package/docs/ja/ui/sidebar.md +92 -0
- package/docs/ja/ui/skeleton.md +16 -0
- package/docs/ja/ui/slider.md +21 -0
- package/docs/ja/ui/spinner.md +14 -0
- package/docs/ja/ui/switch.md +19 -0
- package/docs/ja/ui/table.md +24 -0
- package/docs/ja/ui/tabs.md +24 -0
- package/docs/ja/ui/textarea.md +15 -0
- package/docs/ja/ui/toggle-group.md +27 -0
- package/docs/ja/ui/toggle.md +19 -0
- package/docs/ja/ui/tooltip.md +22 -0
- package/docs/ja/ui/use-is-mobile.md +18 -0
- package/docs/ja/ui-and-theming.md +95 -0
- package/package.json +29 -4
- package/registry/blocks/sales-overview.tsx +43 -19
- package/registry/ui/data-table.tsx +702 -102
- package/skills/vantage-add-feature/SKILL.md +153 -0
- package/skills/vantage-app/SKILL.md +291 -0
- package/skills/vantage-pitfalls/SKILL.md +139 -0
- package/templates/AGENTS.md +313 -0
- package/theme.css +178 -40
- package/dist/chunk-73J5ZD4C.js.map +0 -1
- package/dist/chunk-ATYZ45XL.js +0 -19
- package/dist/chunk-ATYZ45XL.js.map +0 -1
- package/dist/chunk-ZGDU5YLH.js.map +0 -1
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Pages & definePage
|
|
2
|
+
|
|
3
|
+
> A page's only requirement is a default export. Use definePage to attach a static title and description.
|
|
4
|
+
|
|
5
|
+
## The only requirement is a default export
|
|
6
|
+
|
|
7
|
+
A Vantage page needs just one thing: a **default-exported React component**.
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
export default function MonthlyAnalysis() {
|
|
11
|
+
return <h1>Monthly analysis</h1>;
|
|
12
|
+
}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
A page without a default export is flagged by `vantage check` with a `MISSING_DEFAULT_EXPORT` error.
|
|
16
|
+
|
|
17
|
+
## Attach metadata with definePage
|
|
18
|
+
|
|
19
|
+
To add a title or description, use `definePage`. It is optional.
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { definePage } from "@squadbase/vantage";
|
|
23
|
+
|
|
24
|
+
export const page = definePage({
|
|
25
|
+
title: "Monthly analysis · Acme Analytics",
|
|
26
|
+
navLabel: "Monthly analysis",
|
|
27
|
+
description: "A monthly summary of revenue and key metrics",
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export default function MonthlyAnalysis() {
|
|
31
|
+
return <h1>Monthly analysis</h1>;
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- `title` drives `document.title` (the deepest route match's title wins; otherwise it falls back to
|
|
36
|
+
the document's initial `<title>`).
|
|
37
|
+
- `navLabel` is the name a nav built with [`useRoutes()`](routing#build-a-nav-from-the-route-list)
|
|
38
|
+
renders. It defaults to `title`; set it separately when `title` carries the site name.
|
|
39
|
+
- `title`/`description`/`navLabel` are **read statically at generation time**. Vantage parses the
|
|
40
|
+
source rather than executing your page, so keep the values **literal**.
|
|
41
|
+
|
|
42
|
+
> [!WARNING]
|
|
43
|
+
> The **export name must be `page`** in `export const page = definePage({...})`. A different name means
|
|
44
|
+
> the metadata is not read.
|
|
45
|
+
|
|
46
|
+
## Relationship to Fast Refresh
|
|
47
|
+
|
|
48
|
+
Even with a non-component export like `page`, Vantage handles it internally so React Fast Refresh
|
|
49
|
+
survives. Editing a page body updates in place while preserving state.
|
|
50
|
+
|
|
51
|
+
## Prototype limitations
|
|
52
|
+
|
|
53
|
+
`definePage` also accepts `pendingComponent` / `errorComponent` / `validateSearch` as types, but in
|
|
54
|
+
the current prototype they are **not yet wired into lazy routes**. Per-page pending/error overrides
|
|
55
|
+
are unimplemented — the framework's shared defaults render instead.
|
|
56
|
+
|
|
57
|
+
## What to read next
|
|
58
|
+
|
|
59
|
+
- [Data fetching](data-fetching) — handle server state
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# AppShell
|
|
2
|
+
|
|
3
|
+
> The application frame — hand it your nav and you're done.
|
|
4
|
+
|
|
5
|
+
The frame around the whole app. Hand `groups` an array of nav entries and you get a collapsible
|
|
6
|
+
sidebar — or a header, for narrow screens. Use it instead of assembling
|
|
7
|
+
[`Sidebar`](ui/sidebar) yourself.
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { AppShell } from "@squadbase/vantage/components";
|
|
11
|
+
import type { NavGroup, NavItem } from "@squadbase/vantage/components";
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
| Prop | Type | Default | Description |
|
|
15
|
+
| --- | --- | --- | --- |
|
|
16
|
+
| `groups` | `NavGroup[]` | | The navigation: an array of `{ label?, items: NavItem[] }`. |
|
|
17
|
+
| `variant` | `"sidebar" \| "header"` | `"sidebar"` | A sidebar frame, or a header with a select. |
|
|
18
|
+
| `actions` | `ReactNode` | | Rendered at the top right. |
|
|
19
|
+
| `header` | `ReactNode` | | Replaces the default header wholesale. |
|
|
20
|
+
| `defaultOpen` | `boolean` | `true` | With `variant="sidebar"`, whether it starts open. |
|
|
21
|
+
| `linkComponent` | `ElementType` | | The link component used to render items. |
|
|
22
|
+
| `onNavigate` | `(href: string) => void` | | Fired when an item is chosen; defaults to `location.assign`. |
|
|
23
|
+
|
|
24
|
+
## NavItem
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
interface NavItem {
|
|
28
|
+
label: string;
|
|
29
|
+
href: string;
|
|
30
|
+
icon?: ComponentType<{ className?: string }>;
|
|
31
|
+
isActive?: boolean;
|
|
32
|
+
disabled?: boolean;
|
|
33
|
+
badge?: string;
|
|
34
|
+
children?: NavItem[]; // collapsible sub-items
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
An item with `children` becomes a disclosure.
|
|
39
|
+
|
|
40
|
+
## Wiring it to the router
|
|
41
|
+
|
|
42
|
+
Pass `onNavigate` for client-side navigation.
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import { useNavigate } from "@squadbase/vantage/router";
|
|
46
|
+
|
|
47
|
+
const navigate = useNavigate();
|
|
48
|
+
|
|
49
|
+
<AppShell groups={groups} onNavigate={(href) => navigate({ to: href })}>
|
|
50
|
+
{children}
|
|
51
|
+
</AppShell>;
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Highlighting is explicit: set `isActive` by comparing the current route's path with each `href`.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# DashboardCard
|
|
2
|
+
|
|
3
|
+
> The dashboard tile, with a preset and a matching skeleton.
|
|
4
|
+
|
|
5
|
+
The tile you line up on a dashboard. Tighter than the plain [`Card`](ui/card), with a
|
|
6
|
+
skeleton of the same shape for loading. The common arrangement is one component:
|
|
7
|
+
`DashboardCardPreset`.
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import {
|
|
11
|
+
DashboardCard,
|
|
12
|
+
DashboardCardHeader,
|
|
13
|
+
DashboardCardTitle,
|
|
14
|
+
DashboardCardDescription,
|
|
15
|
+
DashboardCardAction,
|
|
16
|
+
DashboardCardContent,
|
|
17
|
+
DashboardCardFooter,
|
|
18
|
+
DashboardCardSkeleton,
|
|
19
|
+
DashboardCardPreset,
|
|
20
|
+
} from "@squadbase/vantage/components";
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## DashboardCardPreset
|
|
24
|
+
|
|
25
|
+
| Prop | Type | Description |
|
|
26
|
+
| --- | --- | --- |
|
|
27
|
+
| `title` | `ReactNode` | The heading. |
|
|
28
|
+
| `description` | `ReactNode` | Supporting text beneath it. |
|
|
29
|
+
| `actions` | `ReactNode` | The right end of the heading row. |
|
|
30
|
+
| `footer` | `ReactNode` | The bottom row. |
|
|
31
|
+
| `children` | `ReactNode` | The body. |
|
|
32
|
+
| `headerClassName` | `string` | |
|
|
33
|
+
| `contentClassName` | `string` | |
|
|
34
|
+
| `footerClassName` | `string` | |
|
|
35
|
+
|
|
36
|
+
With no `title`, `description` or `actions` the header is dropped entirely — same for `footer`.
|
|
37
|
+
|
|
38
|
+
## Composing it yourself
|
|
39
|
+
|
|
40
|
+
When you want a different arrangement, use the parts directly.
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
<DashboardCard>
|
|
44
|
+
<DashboardCardHeader>
|
|
45
|
+
<div>
|
|
46
|
+
<DashboardCardTitle>Revenue</DashboardCardTitle>
|
|
47
|
+
<DashboardCardDescription>This month</DashboardCardDescription>
|
|
48
|
+
</div>
|
|
49
|
+
<DashboardCardAction>
|
|
50
|
+
<Button variant="ghost" size="xs">Details</Button>
|
|
51
|
+
</DashboardCardAction>
|
|
52
|
+
</DashboardCardHeader>
|
|
53
|
+
<DashboardCardContent>…</DashboardCardContent>
|
|
54
|
+
<DashboardCardFooter>+12.4% vs last month</DashboardCardFooter>
|
|
55
|
+
</DashboardCard>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## While loading
|
|
59
|
+
|
|
60
|
+
`DashboardCardSkeleton` draws a box of the same size, so the page doesn't jump when the data lands.
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
{isLoading ? <DashboardCardSkeleton /> : <DashboardCardPreset title="Revenue">…</DashboardCardPreset>}
|
|
64
|
+
```
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# DataTable
|
|
2
|
+
|
|
3
|
+
> The TanStack Table–backed table: sorting, search, pagination, selection.
|
|
4
|
+
|
|
5
|
+
A table with sorting, search, pagination, row selection and column visibility. TanStack Table sits
|
|
6
|
+
underneath, and the column definitions are TanStack's own `ColumnDef`. If you just want the whole
|
|
7
|
+
thing, `DataTablePreset` is a single component.
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import {
|
|
11
|
+
DataTable,
|
|
12
|
+
DataTableToolbar,
|
|
13
|
+
DataTableContent,
|
|
14
|
+
DataTablePagination,
|
|
15
|
+
DataTableColumnVisibility,
|
|
16
|
+
DataTablePreset,
|
|
17
|
+
useDataTable,
|
|
18
|
+
} from "@squadbase/vantage/components";
|
|
19
|
+
import type { ColumnDef, SortingState, PaginationState } from "@squadbase/vantage/components";
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## DataTablePreset
|
|
23
|
+
|
|
24
|
+
Start here. It assembles the toolbar, the table and the pager in the usual order.
|
|
25
|
+
|
|
26
|
+
| Prop | Type | Description |
|
|
27
|
+
| --- | --- | --- |
|
|
28
|
+
| `columns` | `ColumnDef<TData>[]` | TanStack Table column definitions. |
|
|
29
|
+
| `data` | `TData[]` | The rows. |
|
|
30
|
+
| `enableSorting` | `boolean` | Sort by clicking a header. |
|
|
31
|
+
| `enableFiltering` | `boolean` | Put a search box in the toolbar. |
|
|
32
|
+
| `enablePagination` | `boolean` | Show the pager. |
|
|
33
|
+
| `enableRowSelection` | `boolean` | Add a leading checkbox column. |
|
|
34
|
+
| `enableColumnVisibility` | `boolean` | Add the column-picker menu. |
|
|
35
|
+
| `columnLabels` | `Record<string, string>` | Display names for the column menu. |
|
|
36
|
+
| `toolbar` | `ReactNode` | Extra content for the toolbar. |
|
|
37
|
+
| `bulkActions` | `(selectedRows: TData[]) => ReactNode` | Actions shown while rows are selected. |
|
|
38
|
+
| `pageCount` | `number` | Total pages, when paginating on the server. |
|
|
39
|
+
|
|
40
|
+
Pass `sorting` / `columnFilters` / `globalFilter` / `pagination` / `rowSelection` /
|
|
41
|
+
`columnVisibility` together with their `on…Change` and the table becomes controlled. Omit them and
|
|
42
|
+
it keeps the state internally.
|
|
43
|
+
|
|
44
|
+
## Composing it yourself
|
|
45
|
+
|
|
46
|
+
Use the parts directly for a different arrangement. `DataTable` owns the state and the children read
|
|
47
|
+
it.
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
<DataTable columns={columns} data={data} enableSorting enableFiltering>
|
|
51
|
+
<DataTableToolbar showSearch toolbar={<FilterBarPreset … />} />
|
|
52
|
+
<DataTableContent />
|
|
53
|
+
<DataTablePagination pageSizeOptions={[10, 25, 50]} />
|
|
54
|
+
</DataTable>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`useDataTable()` gives a child component the TanStack `table` instance directly.
|
|
58
|
+
|
|
59
|
+
## Paginating on the server
|
|
60
|
+
|
|
61
|
+
Own `pagination` and `onPaginationChange`, and pass the total in `pageCount`.
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 25 });
|
|
65
|
+
|
|
66
|
+
const { data } = useQuery({
|
|
67
|
+
queryKey: ["sales", pagination],
|
|
68
|
+
queryFn: () => apiFetch(`/api/sales?page=${pagination.pageIndex}`).then((r) => r.json()),
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
<DataTablePreset
|
|
72
|
+
columns={columns}
|
|
73
|
+
data={data.rows}
|
|
74
|
+
pagination={pagination}
|
|
75
|
+
onPaginationChange={setPagination}
|
|
76
|
+
pageCount={data.pageCount}
|
|
77
|
+
/>;
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## When you want to edit the source
|
|
81
|
+
|
|
82
|
+
`DataTable` is one of the two components with a copyable source.
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
vantage add ui data-table
|
|
86
|
+
```
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# DateRangePicker
|
|
2
|
+
|
|
3
|
+
> A date-range picker with presets.
|
|
4
|
+
|
|
5
|
+
Presets such as "Today" and "Last 7 days" alongside a two-month calendar. For picking a period on a
|
|
6
|
+
dashboard, this is all you need.
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { DateRangePicker, defaultPresets } from "@squadbase/vantage/components";
|
|
10
|
+
import type { DateRange, PresetItem } from "@squadbase/vantage/components";
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Default | Description |
|
|
14
|
+
| --- | --- | --- | --- |
|
|
15
|
+
| `value` | `DateRange` | | `{ from, to }`; both are `Date \| undefined`. |
|
|
16
|
+
| `onChange` | `(range: DateRange) => void` | | Fired on selection. |
|
|
17
|
+
| `presets` | `PresetItem[]` | `defaultPresets` | The presets listed down the left. |
|
|
18
|
+
| `showPresets` | `boolean` | `true` | Whether to show that column. |
|
|
19
|
+
| `minDate` | `Date` | | Earliest selectable date. |
|
|
20
|
+
| `maxDate` | `Date` | | Latest selectable date. |
|
|
21
|
+
| `placeholder` | `string` | | Trigger text before anything is chosen. |
|
|
22
|
+
| `align` | `"start" \| "center" \| "end"` | | How the popover aligns. |
|
|
23
|
+
| `disabled` | `boolean` | | Disable interaction. |
|
|
24
|
+
|
|
25
|
+
## Replacing the presets
|
|
26
|
+
|
|
27
|
+
`defaultPresets` is Today / Yesterday / Last 7 days / Last 14 days / … . For your own, `getValue`
|
|
28
|
+
returns a `DateRange`.
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
const presets: PresetItem[] = [
|
|
32
|
+
{ label: "This quarter", getValue: () => ({ from: quarterStart, to: today }) },
|
|
33
|
+
...defaultPresets,
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
<DateRangePicker presets={presets} value={range} onChange={setRange} />;
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
> [!NOTE]
|
|
40
|
+
> `onChange` also fires mid-selection, when only `from` is set. Guard against refetching while `to` is
|
|
41
|
+
> still `undefined`.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# EChart
|
|
2
|
+
|
|
3
|
+
> A thin Apache ECharts wrapper. Set colours in your option.
|
|
4
|
+
|
|
5
|
+
A thin wrapper around Apache ECharts. It only handles the canvas lifecycle (init, resize, dispose),
|
|
6
|
+
the loading overlay, event subscriptions and PNG export. **The palette and axes do not auto-follow
|
|
7
|
+
the theme** — set colours inside `option`, or pass an ECharts theme to `theme`.
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { EChart } from "@squadbase/vantage/components";
|
|
11
|
+
import type { EChartsOption } from "@squadbase/vantage/components";
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
| Prop | Type | Description |
|
|
15
|
+
| --- | --- | --- |
|
|
16
|
+
| `option` | `EChartsOption` | The ECharts option, passed straight through. |
|
|
17
|
+
| `height` | `string \| number` | Height; a number means px. `className` works too. |
|
|
18
|
+
| `loading` | `boolean` | Show ECharts' loading overlay. |
|
|
19
|
+
| `theme` | `string \| object` | The theme passed to `echarts.init` (a name or a registered theme). |
|
|
20
|
+
| `onEvents` | `Record<string, (params: unknown) => void>` | Subscribe to ECharts events such as `click`. |
|
|
21
|
+
| `ariaLabel` | `string` | A description of the chart. |
|
|
22
|
+
| `actions` | `boolean` | Show actions such as save-as-image. |
|
|
23
|
+
| `fileName` | `string` | File name used when saving. |
|
|
24
|
+
|
|
25
|
+
## Setting the palette
|
|
26
|
+
|
|
27
|
+
Series colours come from `option.color`. To match the theme's `--chart-1` through `--chart-5`, pass
|
|
28
|
+
the resolved values — ECharts paints to canvas and cannot read CSS variables.
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
const option: EChartsOption = {
|
|
32
|
+
color: ["#3b82f6", "#f59e0b", "#10b981"],
|
|
33
|
+
series: [{ type: "bar", data: [1, 2, 3] }],
|
|
34
|
+
};
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
To reuse a palette across charts, register a theme with
|
|
38
|
+
[`echarts.registerTheme`](https://echarts.apache.org/en/api.html#echarts.registerTheme) and pass its
|
|
39
|
+
name to the `theme` prop.
|
|
40
|
+
|
|
41
|
+
## Keeping the legend off the plot
|
|
42
|
+
|
|
43
|
+
When you show a `legend`, give it `top: 0` and push the plot down with `grid.top`. Omit this and the
|
|
44
|
+
legend overlaps the plot area.
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
legend: { top: 0 },
|
|
48
|
+
grid: { top: 32, containLabel: true },
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
> [!NOTE]
|
|
52
|
+
> Don't import `echarts` directly. The seam is deliberately narrow — `EChart` plus the `EChartsOption`
|
|
53
|
+
> type — so the managed stack can move underneath you.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# FilterBar
|
|
2
|
+
|
|
3
|
+
> A row of filters driven by one value object.
|
|
4
|
+
|
|
5
|
+
A row that treats several filters as one thing. The value is a single `FilterValues` object
|
|
6
|
+
(`{ [key]: string | string[] }`), and the bar can show the active choices as chips and clear them
|
|
7
|
+
all at once.
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import {
|
|
11
|
+
FilterBar,
|
|
12
|
+
FilterBarSelect,
|
|
13
|
+
FilterBarMultiSelect,
|
|
14
|
+
FilterBarActiveChips,
|
|
15
|
+
FilterBarClearButton,
|
|
16
|
+
FilterBarPreset,
|
|
17
|
+
} from "@squadbase/vantage/components";
|
|
18
|
+
import type { FilterConfig, FilterValues, FilterOption } from "@squadbase/vantage/components";
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## FilterBarPreset
|
|
22
|
+
|
|
23
|
+
List the definitions in `filters` and you get the selects, the multi-selects, the chips and the
|
|
24
|
+
clear button.
|
|
25
|
+
|
|
26
|
+
| Prop | Type | Description |
|
|
27
|
+
| --- | --- | --- |
|
|
28
|
+
| `filters` | `FilterConfig[]` | The filter definitions. |
|
|
29
|
+
| `value` | `FilterValues` | The current values. |
|
|
30
|
+
| `onChange` | `(values: FilterValues) => void` | Fired on change. |
|
|
31
|
+
| `onClear` | `() => void` | Fired by the clear button. |
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
interface FilterConfig {
|
|
35
|
+
key: string;
|
|
36
|
+
label: string;
|
|
37
|
+
type: "select" | "multiselect" | "date" | "daterange" | "search";
|
|
38
|
+
options?: FilterOption[]; // { label, value, count?, disabled? }
|
|
39
|
+
placeholder?: string;
|
|
40
|
+
defaultValue?: string | string[];
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Give a `FilterOption` a `count` and the number shows beside the option.
|
|
45
|
+
|
|
46
|
+
## Composing it yourself
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
<FilterBar value={values} onChange={setValues} onClear={() => setValues({})}>
|
|
50
|
+
<FilterBarSelect filterKey="region" label="Region" options={regions} />
|
|
51
|
+
<FilterBarMultiSelect filterKey="status" label="Status" options={statuses} />
|
|
52
|
+
<FilterBarClearButton />
|
|
53
|
+
<FilterBarActiveChips filters={filters} />
|
|
54
|
+
</FilterBar>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Wiring it to a query
|
|
58
|
+
|
|
59
|
+
Drop `FilterValues` straight into the `queryKey` and the data refetches only when the filters change.
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
const { data } = useQuery({
|
|
63
|
+
queryKey: ["sales", values],
|
|
64
|
+
queryFn: () => apiFetch(`/api/sales?${new URLSearchParams(values as never)}`).then((r) => r.json()),
|
|
65
|
+
});
|
|
66
|
+
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# FunnelSteps
|
|
2
|
+
|
|
3
|
+
> A funnel drawn as bars, with conversion rates.
|
|
4
|
+
|
|
5
|
+
Draws each funnel stage as a bar, with the first stage at full width, and optionally the conversion
|
|
6
|
+
between stages. A light visualisation that doesn't need ECharts.
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { FunnelSteps, funnelStepsVariants } from "@squadbase/vantage/components";
|
|
10
|
+
import type { FunnelStep } from "@squadbase/vantage/components";
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Default | Description |
|
|
14
|
+
| --- | --- | --- | --- |
|
|
15
|
+
| `data` | `FunnelStep[]` | | The stages. **The first one is the full-width bar.** |
|
|
16
|
+
| `showConversion` | `boolean` | | Show the conversion between stages. |
|
|
17
|
+
| `formatValue` | `(value: number) => string` | | Format the numbers; raw by default. |
|
|
18
|
+
| `animate` | `boolean` | | Grow the bars on mount. |
|
|
19
|
+
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | |
|
|
20
|
+
| `direction` | `"vertical" \| "horizontal"` | `"vertical"` | |
|
|
21
|
+
|
|
22
|
+
## FunnelStep
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
interface FunnelStep {
|
|
26
|
+
label: string;
|
|
27
|
+
value: number;
|
|
28
|
+
color?: string; // defaults to the theme's chart colours
|
|
29
|
+
onClick?: () => void;
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
A stage with `onClick` becomes pressable — use it to drill into that stage's breakdown.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# MetricValue
|
|
2
|
+
|
|
3
|
+
> The KPI number, with MetricUnit for the unit.
|
|
4
|
+
|
|
5
|
+
A tight-tracking display for a KPI's number. Units and other trailing text go in `MetricUnit`.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { MetricValue, MetricUnit, metricValueVariants } from "@squadbase/vantage/components";
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
| Prop | Type | Default | Description |
|
|
12
|
+
| --- | --- | --- | --- |
|
|
13
|
+
| `size` | `"sm" \| "md" \| "lg" \| "xl"` | `"md"` | Type scale. |
|
|
14
|
+
| `className` | `string` | | Classes to add. |
|
|
15
|
+
|
|
16
|
+
It carries vertical margin (`my-4`) by default. Inside a card, pass `className="my-0"` to sit flush.
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
<DashboardCardPreset title="Revenue">
|
|
20
|
+
<MetricValue className="my-0">$12,400</MetricValue>
|
|
21
|
+
<TrendIndicator value={12.4} direction="up" />
|
|
22
|
+
</DashboardCardPreset>
|
|
23
|
+
```
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# MultiSelect
|
|
2
|
+
|
|
3
|
+
> Multiple choice with search; selections show as chips.
|
|
4
|
+
|
|
5
|
+
Multiple choice with a search box. Selections appear as chips on the trigger, and anything past
|
|
6
|
+
`maxCount` collapses into a "+N".
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { MultiSelect } from "@squadbase/vantage/components";
|
|
10
|
+
import type { SelectOption, SelectOptionGroup } from "@squadbase/vantage/components";
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Description |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `options` | `SelectOption[] \| SelectOptionGroup[]` | The options; an array of groups adds headings. |
|
|
16
|
+
| `value` | `string[]` | The selected values. |
|
|
17
|
+
| `onChange` | `(value: string[]) => void` | Fired on change. |
|
|
18
|
+
| `maxCount` | `number` | How many chips to show before collapsing to "+N". |
|
|
19
|
+
| `onSearch` | `(query: string) => void` | Called on every keystroke — for server-side search. |
|
|
20
|
+
| `isLoading` | `boolean` | Options are being fetched. |
|
|
21
|
+
| `placeholder` | `string` | Trigger text before anything is chosen. |
|
|
22
|
+
| `searchPlaceholder` | `string` | Placeholder for the search box. |
|
|
23
|
+
| `emptyText` | `string` | Shown when nothing matches. |
|
|
24
|
+
| `disabled` | `boolean` | Disable interaction. |
|
|
25
|
+
|
|
26
|
+
Inside a filter row, [`FilterBar`](parts/filter-bar)'s `FilterBarMultiSelect` handles
|
|
27
|
+
the value plumbing for you.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# PageShell
|
|
2
|
+
|
|
3
|
+
> The page frame: header band, summary row, content.
|
|
4
|
+
|
|
5
|
+
The frame for one page: a full-width header band, an optional row of summary cards inside it, and
|
|
6
|
+
the content below. Every part is a thin `<div>`, so use only the ones you need.
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import {
|
|
10
|
+
PageShell,
|
|
11
|
+
PageShellHeader,
|
|
12
|
+
PageShellHeading,
|
|
13
|
+
PageShellTitle,
|
|
14
|
+
PageShellDescription,
|
|
15
|
+
PageShellHeaderEnd,
|
|
16
|
+
PageShellMeta,
|
|
17
|
+
PageShellActions,
|
|
18
|
+
PageShellSummary,
|
|
19
|
+
PageShellSummaryCard,
|
|
20
|
+
PageShellContent,
|
|
21
|
+
pageShellSummaryCardVariants,
|
|
22
|
+
} from "@squadbase/vantage/components";
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Parts
|
|
26
|
+
|
|
27
|
+
| Part | Role |
|
|
28
|
+
| --- | --- |
|
|
29
|
+
| `PageShell` | The outer stack |
|
|
30
|
+
| `PageShellHeader` | The underlined header band: heading on the left, `HeaderEnd` on the right |
|
|
31
|
+
| `PageShellHeading` / `PageShellTitle` / `PageShellDescription` | The heading block |
|
|
32
|
+
| `PageShellHeaderEnd` | The right-hand stack |
|
|
33
|
+
| `PageShellMeta` | Asides such as "updated 10 minutes ago" |
|
|
34
|
+
| `PageShellActions` | The button row |
|
|
35
|
+
| `PageShellSummary` | A grid of summary cards, full width inside the header |
|
|
36
|
+
| `PageShellSummaryCard` | One summary card; `accent` colours it |
|
|
37
|
+
| `PageShellContent` | The body |
|
|
38
|
+
|
|
39
|
+
`PageShellSummaryCard`'s `accent` is `default` / `accent` / `amber` / `blue` / `emerald` / `red`.
|
|
40
|
+
`accent` uses the theme's `--chart-1`, so it follows your tokens.
|
|
41
|
+
|
|
42
|
+
## Use it from a layout
|
|
43
|
+
|
|
44
|
+
Rather than repeating it per page, put it in `_layout.tsx` and share it — see
|
|
45
|
+
[Routing](routing).
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Placeholder
|
|
2
|
+
|
|
3
|
+
> An inline marker for sample, not-yet-real data.
|
|
4
|
+
|
|
5
|
+
Marks a spot where real data isn't wired up yet. The muted colour plus `animate-pulse` says
|
|
6
|
+
**"this value is meant to be replaced"**, so a template never gets shared with sample numbers
|
|
7
|
+
mistaken for real ones.
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { Placeholder } from "@squadbase/vantage/components";
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Description |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `children` | `ReactNode` | The sample value — text or elements. |
|
|
16
|
+
| `className` | `string` | Override size and weight (e.g. `text-2xl font-medium`). |
|
|
17
|
+
|
|
18
|
+
Any remaining props go straight to the `<span>`.
|
|
19
|
+
|
|
20
|
+
## Placeholder or Skeleton
|
|
21
|
+
|
|
22
|
+
| | Use for | Element |
|
|
23
|
+
| --- | --- | --- |
|
|
24
|
+
| [`Skeleton`](ui/skeleton) | **Loading** — it disappears when data arrives | block `<div>` |
|
|
25
|
+
| `Placeholder` | **A stand-in value** — it stays until you replace it | inline `<span>` |
|
|
26
|
+
|
|
27
|
+
Being inline, it is safe **inside a sentence** — in a `<p>` or a heading. `Skeleton` is a block
|
|
28
|
+
element and breaks the line if you mix it into running text.
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
<p>
|
|
32
|
+
Revenue this month was <Placeholder>$12,400</Placeholder>.
|
|
33
|
+
</p>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
> [!TIP]
|
|
37
|
+
> When you start from a template, searching for `Placeholder` gives you the checklist of everything
|
|
38
|
+
> still unwired.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# SearchableSelect
|
|
2
|
+
|
|
3
|
+
> Single choice with search, for long option lists.
|
|
4
|
+
|
|
5
|
+
Single choice with a search box, for when there are too many options to scan in
|
|
6
|
+
[`Select`](ui/select). Pass `onSearch` to search on the server instead.
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { SearchableSelect } from "@squadbase/vantage/components";
|
|
10
|
+
import type { SelectOption, SelectOptionGroup } from "@squadbase/vantage/components";
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Description |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `options` | `SelectOption[] \| SelectOptionGroup[]` | The options; an array of groups adds headings. |
|
|
16
|
+
| `value` | `string` | The selected value. |
|
|
17
|
+
| `onChange` | `(value: string \| undefined) => void` | Fired on change. |
|
|
18
|
+
| `onSearch` | `(query: string) => void` | Called on every keystroke — for server-side search. |
|
|
19
|
+
| `clearable` | `boolean` | Allow clearing the selection. |
|
|
20
|
+
| `isLoading` | `boolean` | Options are being fetched. |
|
|
21
|
+
| `placeholder` | `string` | Trigger text before anything is chosen. |
|
|
22
|
+
| `searchPlaceholder` | `string` | Placeholder for the search box. |
|
|
23
|
+
| `emptyText` | `string` | Shown when nothing matches. |
|
|
24
|
+
| `disabled` | `boolean` | Disable interaction. |
|
|
25
|
+
|
|
26
|
+
## Searching on the server
|
|
27
|
+
|
|
28
|
+
Pass `onSearch` and replace `options` yourself. Filtering becomes the server's job, so hand back
|
|
29
|
+
whatever it returns.
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
const [query, setQuery] = useState("");
|
|
33
|
+
const { data, isFetching } = useQuery({
|
|
34
|
+
queryKey: ["customers", query],
|
|
35
|
+
queryFn: () => apiFetch(`/api/customers?q=${query}`).then((r) => r.json()),
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
<SearchableSelect
|
|
39
|
+
options={data ?? []}
|
|
40
|
+
onSearch={setQuery}
|
|
41
|
+
isLoading={isFetching}
|
|
42
|
+
value={value}
|
|
43
|
+
onChange={setValue}
|
|
44
|
+
/>;
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
To allow several selections, use [`MultiSelect`](parts/multi-select).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# SectionHeader
|
|
2
|
+
|
|
3
|
+
> A section heading with its actions.
|
|
4
|
+
|
|
5
|
+
The heading that divides a page into sections. Pick the heading level with `as`.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { SectionHeader } from "@squadbase/vantage/components";
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
| Prop | Type | Default | Description |
|
|
12
|
+
| --- | --- | --- | --- |
|
|
13
|
+
| `title` | `string` | | The heading. |
|
|
14
|
+
| `description` | `string` | | Supporting text beneath it. |
|
|
15
|
+
| `actions` | `ReactNode` | | Rendered at the right. |
|
|
16
|
+
| `as` | `"h1" \| "h2" \| "h3" \| "h4"` | `"h2"` | Heading level; the type scale follows. |
|
|
17
|
+
| `className` | `string` | | Classes to add. |
|
|
18
|
+
|
|
19
|
+
For the page's own title use [`PageShell`](parts/page-shell)'s header band.
|
|
20
|
+
`SectionHeader` is for sections within it.
|