@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.
Files changed (167) hide show
  1. package/README.md +21 -4
  2. package/dist/chunk-A2UUGASH.js +12 -0
  3. package/dist/chunk-A2UUGASH.js.map +1 -0
  4. package/dist/{chunk-ZGDU5YLH.js → chunk-PRFBSQA4.js} +9 -5
  5. package/dist/chunk-PRFBSQA4.js.map +1 -0
  6. package/dist/chunk-WQZYXXQW.js +2249 -0
  7. package/dist/chunk-WQZYXXQW.js.map +1 -0
  8. package/dist/{chunk-73J5ZD4C.js → chunk-YLAB6UQS.js} +17 -3
  9. package/dist/chunk-YLAB6UQS.js.map +1 -0
  10. package/dist/cli.js +607 -36
  11. package/dist/cli.js.map +1 -1
  12. package/dist/components/index.d.ts +554 -0
  13. package/dist/components/index.js +1983 -0
  14. package/dist/components/index.js.map +1 -0
  15. package/dist/markdown/index.d.ts +9 -0
  16. package/dist/markdown/index.js +26 -0
  17. package/dist/markdown/index.js.map +1 -0
  18. package/dist/query/index.d.ts +27 -1
  19. package/dist/query/index.js +18 -0
  20. package/dist/query/index.js.map +1 -1
  21. package/dist/server/node.js +6 -1
  22. package/dist/server/node.js.map +1 -1
  23. package/dist/ui/index.d.ts +316 -200
  24. package/dist/ui/index.js +566 -511
  25. package/dist/ui/index.js.map +1 -1
  26. package/dist/vite/index.js +2 -2
  27. package/docs/en/agent-skills.md +89 -0
  28. package/docs/en/api-and-server.md +84 -0
  29. package/docs/en/build-and-deploy.md +280 -0
  30. package/docs/en/cli-reference.md +311 -0
  31. package/docs/en/components.md +233 -0
  32. package/docs/en/data-fetching.md +78 -0
  33. package/docs/en/environment-variables.md +69 -0
  34. package/docs/en/getting-started.md +95 -0
  35. package/docs/en/index.md +65 -0
  36. package/docs/en/markdown/markdown-renderer.md +44 -0
  37. package/docs/en/pages-and-metadata.md +56 -0
  38. package/docs/en/parts/app-shell.md +54 -0
  39. package/docs/en/parts/dashboard-card.md +64 -0
  40. package/docs/en/parts/data-table.md +86 -0
  41. package/docs/en/parts/date-range-picker.md +41 -0
  42. package/docs/en/parts/echart.md +53 -0
  43. package/docs/en/parts/filter-bar.md +66 -0
  44. package/docs/en/parts/funnel-steps.md +33 -0
  45. package/docs/en/parts/metric-value.md +23 -0
  46. package/docs/en/parts/multi-select.md +27 -0
  47. package/docs/en/parts/page-shell.md +45 -0
  48. package/docs/en/parts/searchable-select.md +47 -0
  49. package/docs/en/parts/section-header.md +20 -0
  50. package/docs/en/parts/segmented-control.md +25 -0
  51. package/docs/en/parts/status-badge.md +44 -0
  52. package/docs/en/parts/trend-indicator.md +26 -0
  53. package/docs/en/routing.md +95 -0
  54. package/docs/en/ui/accordion.md +36 -0
  55. package/docs/en/ui/alert.md +31 -0
  56. package/docs/en/ui/badge.md +23 -0
  57. package/docs/en/ui/breadcrumb.md +38 -0
  58. package/docs/en/ui/button.md +39 -0
  59. package/docs/en/ui/calendar.md +28 -0
  60. package/docs/en/ui/card.md +35 -0
  61. package/docs/en/ui/checkbox.md +34 -0
  62. package/docs/en/ui/cn.md +34 -0
  63. package/docs/en/ui/collapsible.md +21 -0
  64. package/docs/en/ui/command.md +37 -0
  65. package/docs/en/ui/dialog.md +52 -0
  66. package/docs/en/ui/dropdown-menu.md +50 -0
  67. package/docs/en/ui/empty.md +22 -0
  68. package/docs/en/ui/error-state.md +50 -0
  69. package/docs/en/ui/input-group.md +30 -0
  70. package/docs/en/ui/input.md +28 -0
  71. package/docs/en/ui/label.md +19 -0
  72. package/docs/en/ui/loading.md +19 -0
  73. package/docs/en/ui/popover.md +34 -0
  74. package/docs/en/ui/progress.md +28 -0
  75. package/docs/en/ui/scroll-area.md +23 -0
  76. package/docs/en/ui/select.md +63 -0
  77. package/docs/en/ui/separator.md +15 -0
  78. package/docs/en/ui/sheet.md +28 -0
  79. package/docs/en/ui/sidebar.md +92 -0
  80. package/docs/en/ui/skeleton.md +16 -0
  81. package/docs/en/ui/slider.md +21 -0
  82. package/docs/en/ui/spinner.md +14 -0
  83. package/docs/en/ui/switch.md +19 -0
  84. package/docs/en/ui/table.md +24 -0
  85. package/docs/en/ui/tabs.md +23 -0
  86. package/docs/en/ui/textarea.md +15 -0
  87. package/docs/en/ui/toggle-group.md +26 -0
  88. package/docs/en/ui/toggle.md +19 -0
  89. package/docs/en/ui/tooltip.md +22 -0
  90. package/docs/en/ui/use-is-mobile.md +18 -0
  91. package/docs/en/ui-and-theming.md +95 -0
  92. package/docs/index.json +1306 -0
  93. package/docs/ja/agent-skills.md +87 -0
  94. package/docs/ja/api-and-server.md +84 -0
  95. package/docs/ja/build-and-deploy.md +279 -0
  96. package/docs/ja/cli-reference.md +305 -0
  97. package/docs/ja/components.md +229 -0
  98. package/docs/ja/data-fetching.md +78 -0
  99. package/docs/ja/environment-variables.md +68 -0
  100. package/docs/ja/getting-started.md +95 -0
  101. package/docs/ja/index.md +65 -0
  102. package/docs/ja/markdown/markdown-renderer.md +43 -0
  103. package/docs/ja/pages-and-metadata.md +56 -0
  104. package/docs/ja/parts/app-shell.md +54 -0
  105. package/docs/ja/parts/dashboard-card.md +64 -0
  106. package/docs/ja/parts/data-table.md +85 -0
  107. package/docs/ja/parts/date-range-picker.md +41 -0
  108. package/docs/ja/parts/echart.md +52 -0
  109. package/docs/ja/parts/filter-bar.md +64 -0
  110. package/docs/ja/parts/funnel-steps.md +33 -0
  111. package/docs/ja/parts/metric-value.md +25 -0
  112. package/docs/ja/parts/multi-select.md +27 -0
  113. package/docs/ja/parts/page-shell.md +45 -0
  114. package/docs/ja/parts/searchable-select.md +47 -0
  115. package/docs/ja/parts/section-header.md +20 -0
  116. package/docs/ja/parts/segmented-control.md +26 -0
  117. package/docs/ja/parts/status-badge.md +44 -0
  118. package/docs/ja/parts/trend-indicator.md +26 -0
  119. package/docs/ja/routing.md +95 -0
  120. package/docs/ja/ui/accordion.md +37 -0
  121. package/docs/ja/ui/alert.md +31 -0
  122. package/docs/ja/ui/badge.md +23 -0
  123. package/docs/ja/ui/breadcrumb.md +38 -0
  124. package/docs/ja/ui/button.md +39 -0
  125. package/docs/ja/ui/calendar.md +28 -0
  126. package/docs/ja/ui/card.md +35 -0
  127. package/docs/ja/ui/checkbox.md +34 -0
  128. package/docs/ja/ui/cn.md +34 -0
  129. package/docs/ja/ui/collapsible.md +21 -0
  130. package/docs/ja/ui/command.md +36 -0
  131. package/docs/ja/ui/dialog.md +51 -0
  132. package/docs/ja/ui/dropdown-menu.md +50 -0
  133. package/docs/ja/ui/empty.md +22 -0
  134. package/docs/ja/ui/error-state.md +50 -0
  135. package/docs/ja/ui/input-group.md +30 -0
  136. package/docs/ja/ui/input.md +28 -0
  137. package/docs/ja/ui/label.md +19 -0
  138. package/docs/ja/ui/loading.md +19 -0
  139. package/docs/ja/ui/popover.md +34 -0
  140. package/docs/ja/ui/progress.md +28 -0
  141. package/docs/ja/ui/scroll-area.md +24 -0
  142. package/docs/ja/ui/select.md +64 -0
  143. package/docs/ja/ui/separator.md +15 -0
  144. package/docs/ja/ui/sheet.md +28 -0
  145. package/docs/ja/ui/sidebar.md +92 -0
  146. package/docs/ja/ui/skeleton.md +16 -0
  147. package/docs/ja/ui/slider.md +21 -0
  148. package/docs/ja/ui/spinner.md +14 -0
  149. package/docs/ja/ui/switch.md +19 -0
  150. package/docs/ja/ui/table.md +24 -0
  151. package/docs/ja/ui/tabs.md +24 -0
  152. package/docs/ja/ui/textarea.md +15 -0
  153. package/docs/ja/ui/toggle-group.md +27 -0
  154. package/docs/ja/ui/toggle.md +19 -0
  155. package/docs/ja/ui/tooltip.md +22 -0
  156. package/docs/ja/ui/use-is-mobile.md +18 -0
  157. package/docs/ja/ui-and-theming.md +95 -0
  158. package/package.json +29 -4
  159. package/registry/blocks/sales-overview.tsx +43 -19
  160. package/registry/ui/data-table.tsx +702 -102
  161. package/skills/vantage-add-feature/SKILL.md +150 -0
  162. package/skills/vantage-app/SKILL.md +258 -0
  163. package/skills/vantage-pitfalls/SKILL.md +115 -0
  164. package/templates/AGENTS.md +268 -0
  165. package/theme.css +178 -40
  166. package/dist/chunk-73J5ZD4C.js.map +0 -1
  167. 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.