@squadbase/vantage 0.0.1 → 0.1.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/README.md +21 -4
- package/dist/chunk-A2UUGASH.js +12 -0
- package/dist/chunk-A2UUGASH.js.map +1 -0
- package/dist/{chunk-ZGDU5YLH.js → chunk-PRFBSQA4.js} +9 -5
- package/dist/chunk-PRFBSQA4.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/components/index.d.ts +554 -0
- package/dist/components/index.js +1983 -0
- package/dist/components/index.js.map +1 -0
- 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 +27 -1
- package/dist/query/index.js +18 -0
- package/dist/query/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 +233 -0
- package/docs/en/data-fetching.md +78 -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 +56 -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/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/status-badge.md +44 -0
- package/docs/en/parts/trend-indicator.md +26 -0
- package/docs/en/routing.md +95 -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 +1306 -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 +229 -0
- package/docs/ja/data-fetching.md +78 -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 +56 -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/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/status-badge.md +44 -0
- package/docs/ja/parts/trend-indicator.md +26 -0
- package/docs/ja/routing.md +95 -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 +150 -0
- package/skills/vantage-app/SKILL.md +258 -0
- package/skills/vantage-pitfalls/SKILL.md +115 -0
- package/templates/AGENTS.md +268 -0
- package/theme.css +178 -40
- package/dist/chunk-73J5ZD4C.js.map +0 -1
- package/dist/chunk-ZGDU5YLH.js.map +0 -1
|
@@ -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,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.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# SegmentedControl
|
|
2
|
+
|
|
3
|
+
> Single-choice segments — it hides ToggleGroup's array.
|
|
4
|
+
|
|
5
|
+
A row of two to four choices. It is built on [`ToggleGroup`](ui/toggle-group), but
|
|
6
|
+
**because it's single choice `value` is one string** — the array API is absorbed here.
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { SegmentedControl } from "@squadbase/vantage/components";
|
|
10
|
+
import type { SegmentOption } from "@squadbase/vantage/components";
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Default | Description |
|
|
14
|
+
| --- | --- | --- | --- |
|
|
15
|
+
| `options` | `SegmentOption[]` | | An array of `{ label, value, icon?, disabled? }`. |
|
|
16
|
+
| `value` | `string` | | The selected value — **a single string**. |
|
|
17
|
+
| `onChange` | `(value: string) => void` | | Fired on change. |
|
|
18
|
+
| `size` | `"sm" \| "default" \| "lg"` | `"default"` | Icon scale. |
|
|
19
|
+
| `fullWidth` | `boolean` | | Stretch to the parent's width. |
|
|
20
|
+
| `ariaLabel` | `string` | | Describes the group. |
|
|
21
|
+
|
|
22
|
+
Pass a `lucide-react` component straight to `icon`.
|
|
23
|
+
|
|
24
|
+
Beyond five options, [`Select`](ui/select) reads better; when the choices are whole
|
|
25
|
+
views, [`Tabs`](ui/tabs) fits.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# StatusBadge
|
|
2
|
+
|
|
3
|
+
> A dot-and-label status badge, coloured by name.
|
|
4
|
+
|
|
5
|
+
Shows a row's or record's state as a coloured dot with a label. A built-in map turns the status
|
|
6
|
+
string into a colour, and `colorMap` extends or overrides it.
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { StatusBadge } from "@squadbase/vantage/components";
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
| Prop | Type | Description |
|
|
13
|
+
| --- | --- | --- |
|
|
14
|
+
| `status` | `string` | The status string; it picks the colour. |
|
|
15
|
+
| `label` | `string` | The text to show; defaults to `status`. |
|
|
16
|
+
| `colorMap` | `Record<string, string>` | Status → colour name, merged over the built-in map. |
|
|
17
|
+
| `className` | `string` | Classes to add. |
|
|
18
|
+
|
|
19
|
+
## The built-in map
|
|
20
|
+
|
|
21
|
+
| `status` | Colour |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| `active` | green |
|
|
24
|
+
| `inactive` | gray |
|
|
25
|
+
| `warning` | yellow |
|
|
26
|
+
| `error` | red |
|
|
27
|
+
| `pending` | blue |
|
|
28
|
+
|
|
29
|
+
Anything unknown falls back to gray. To add your own states, map them to one of
|
|
30
|
+
`green` / `gray` / `yellow` / `red` / `blue`.
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
<StatusBadge status="reviewing" label="Reviewing" colorMap={{ reviewing: "blue" }} />
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
It drops straight into a table cell.
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
{
|
|
40
|
+
accessorKey: "status",
|
|
41
|
+
header: "Status",
|
|
42
|
+
cell: ({ row }) => <StatusBadge status={row.original.status} />,
|
|
43
|
+
}
|
|
44
|
+
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# TrendIndicator
|
|
2
|
+
|
|
3
|
+
> An arrow and a percentage; positiveIsGood decides the colour.
|
|
4
|
+
|
|
5
|
+
Shows a change as an arrow plus a percentage. **Up doesn't always mean good**, so the colour comes
|
|
6
|
+
from `direction` combined with `positiveIsGood` — pass `positiveIsGood={false}` for metrics like
|
|
7
|
+
churn, where down is the good direction.
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { TrendIndicator } from "@squadbase/vantage/components";
|
|
11
|
+
import type { TrendDirection } from "@squadbase/vantage/components";
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
| Prop | Type | Default | Description |
|
|
15
|
+
| --- | --- | --- | --- |
|
|
16
|
+
| `value` | `number` | | The change (`12.4` means 12.4%). |
|
|
17
|
+
| `direction` | `"up" \| "down" \| "neutral"` | | Which way the arrow points. **Required.** |
|
|
18
|
+
| `positiveIsGood` | `boolean` | `true` | Whether up is good; `false` flips the colours. |
|
|
19
|
+
| `className` | `string` | | Classes to add. |
|
|
20
|
+
|
|
21
|
+
> [!WARNING]
|
|
22
|
+
> `direction` is required — it is never inferred from the sign of `value`, so that "it went down, and
|
|
23
|
+
> that's good" stays expressible.
|
|
24
|
+
|
|
25
|
+
There are three colours: `--chart-1` for the good direction, `--primary` for the bad one, and
|
|
26
|
+
`--muted-foreground` for `neutral`. Change the theme tokens and these follow.
|