@squadbase/vantage 0.6.2-dev1.a7c4a3e → 0.6.2-dev1.ac99e02
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/dist/add-4GHF2TX2.js +6 -0
- package/dist/add-4GHF2TX2.js.map +1 -0
- package/dist/{build-JB4VOMUI.js → build-MFWRPYKW.js} +9 -9
- package/dist/{build-JB4VOMUI.js.map → build-MFWRPYKW.js.map} +1 -1
- package/dist/{check-ZJYK6RLG.js → check-ZBIH422T.js} +7 -7
- package/dist/{check-ZJYK6RLG.js.map → check-ZBIH422T.js.map} +1 -1
- package/dist/{chunk-BCE22JSM.js → chunk-7DZGJQ3P.js} +33 -14
- package/dist/chunk-7DZGJQ3P.js.map +1 -0
- package/dist/{chunk-U2M6HGKQ.js → chunk-AABMKHEU.js} +4 -4
- package/dist/{chunk-U2M6HGKQ.js.map → chunk-AABMKHEU.js.map} +1 -1
- package/dist/{chunk-MP3QSOXL.js → chunk-AG6RZ2RR.js} +992 -16
- package/dist/chunk-AG6RZ2RR.js.map +1 -0
- package/dist/{chunk-KLEIVDUV.js → chunk-CEXAQQ5W.js} +150 -11
- package/dist/chunk-CEXAQQ5W.js.map +1 -0
- package/dist/{chunk-QI3XQKHS.js → chunk-GNXCX4WE.js} +10 -3
- package/dist/chunk-GNXCX4WE.js.map +1 -0
- package/dist/chunk-IBGOCWJB.js +1716 -0
- package/dist/chunk-IBGOCWJB.js.map +1 -0
- package/dist/{chunk-JGRMRXXZ.js → chunk-JKLLFVEU.js} +3 -3
- package/dist/{chunk-JGRMRXXZ.js.map → chunk-JKLLFVEU.js.map} +1 -1
- package/dist/{chunk-RPZAALCX.js → chunk-OB6UBJXF.js} +153 -153
- package/dist/{chunk-RPZAALCX.js.map → chunk-OB6UBJXF.js.map} +1 -1
- package/dist/{chunk-EP6JHXSR.js → chunk-QEWCYW3I.js} +44 -9
- package/dist/chunk-QEWCYW3I.js.map +1 -0
- package/dist/{chunk-ZVANIULZ.js → chunk-RNTDBX6S.js} +4 -4
- package/dist/{chunk-ZVANIULZ.js.map → chunk-RNTDBX6S.js.map} +1 -1
- package/dist/{chunk-PVTHHV7X.js → chunk-V3LF7BE7.js} +27 -5
- package/dist/chunk-V3LF7BE7.js.map +1 -0
- package/dist/{chunk-DTDVSFRY.js → chunk-XQUSGIDG.js} +17 -3
- package/dist/chunk-XQUSGIDG.js.map +1 -0
- package/dist/cli.js +12 -11
- package/dist/cli.js.map +1 -1
- package/dist/client/index.d.ts +32 -3
- package/dist/client/index.js +205 -12
- package/dist/client/index.js.map +1 -1
- package/dist/components/index.d.ts +216 -1
- package/dist/components/index.js +2 -2
- package/dist/{dev-FCEC3UKQ.js → dev-DCTOHQ6W.js} +10 -10
- package/dist/dev-DCTOHQ6W.js.map +1 -0
- package/dist/{docs-EJJ3BECT.js → docs-JVP4RPKT.js} +3 -3
- package/dist/{docs-EJJ3BECT.js.map → docs-JVP4RPKT.js.map} +1 -1
- package/dist/{doctor-WHZXPERA.js → doctor-5E2VEZPK.js} +7 -7
- package/dist/{doctor-WHZXPERA.js.map → doctor-5E2VEZPK.js.map} +1 -1
- package/dist/{preview-OGMCNQUO.js → preview-T6CLSQVX.js} +3 -3
- package/dist/{preview-OGMCNQUO.js.map → preview-T6CLSQVX.js.map} +1 -1
- package/dist/query/index.d.ts +0 -11
- package/dist/query/index.js +2 -2
- package/dist/{routes-GTFO5PUF.js → routes-3SGAFDMC.js} +6 -6
- package/dist/{routes-GTFO5PUF.js.map → routes-3SGAFDMC.js.map} +1 -1
- package/dist/{search-6Y422SV6.js → search-5LC4XPB5.js} +3 -3
- package/dist/{search-6Y422SV6.js.map → search-5LC4XPB5.js.map} +1 -1
- package/dist/slide/index.js +47 -10
- package/dist/slide/index.js.map +1 -1
- package/dist/table/index.js +8 -7
- package/dist/table/index.js.map +1 -1
- package/dist/ui/index.d.ts +140 -10
- package/dist/ui/index.js +1405 -63
- package/dist/ui/index.js.map +1 -1
- package/dist/{upgrade-AL4KZZR4.js → upgrade-DBBE6GIR.js} +7 -7
- package/dist/{upgrade-AL4KZZR4.js.map → upgrade-DBBE6GIR.js.map} +1 -1
- package/dist/vite/index.d.ts +10 -0
- package/dist/vite/index.js +4 -4
- package/docs/index.json +291 -61
- package/docs/ja/changelog.md +26 -0
- package/docs/ja/components.md +33 -89
- package/docs/ja/page-types/csv.md +83 -0
- package/docs/ja/page-types/doc.md +83 -0
- package/docs/ja/page-types/excel.md +82 -0
- package/docs/ja/page-types/slide.md +132 -0
- package/docs/ja/page-types.md +47 -0
- package/docs/ja/parts/area-chart.md +61 -0
- package/docs/ja/parts/bar-chart.md +69 -0
- package/docs/ja/parts/bar-list.md +35 -0
- package/docs/ja/parts/category-bar.md +36 -0
- package/docs/ja/parts/donut-chart.md +47 -0
- package/docs/ja/parts/echart.md +3 -0
- package/docs/ja/parts/line-chart.md +58 -0
- package/docs/ja/parts/progress-circle.md +34 -0
- package/docs/ja/parts/sparkline.md +1 -1
- package/docs/ja/parts/tracker.md +32 -0
- package/docs/ja/routing.md +8 -86
- package/docs/ja/ui/alert-dialog.md +41 -0
- package/docs/ja/ui/avatar.md +31 -0
- package/docs/ja/ui/combobox.md +74 -0
- package/docs/ja/ui/drawer.md +41 -0
- package/docs/ja/ui/field.md +42 -0
- package/docs/ja/ui/hover-card.md +21 -0
- package/docs/ja/ui/kbd.md +16 -0
- package/docs/ja/ui/pagination.md +36 -0
- package/docs/ja/ui/radio-group.md +21 -0
- package/docs/ja/ui/toast.md +46 -0
- package/docs/ja/ui-and-theming.md +14 -7
- package/package.json +25 -13
- package/registry/ui/accordion.tsx +74 -0
- package/registry/ui/alert-dialog.tsx +189 -0
- package/registry/ui/alert.tsx +78 -0
- package/registry/ui/app-shell.tsx +281 -0
- package/registry/ui/area-chart.tsx +1015 -0
- package/registry/ui/avatar.tsx +110 -0
- package/registry/ui/badge.tsx +54 -0
- package/registry/ui/bar-chart.tsx +1013 -0
- package/registry/ui/bar-list.tsx +302 -0
- package/registry/ui/breadcrumb.tsx +126 -0
- package/registry/ui/button.tsx +60 -0
- package/registry/ui/calendar.tsx +221 -0
- package/registry/ui/card.tsx +105 -0
- package/registry/ui/category-bar.tsx +281 -0
- package/registry/ui/checkbox.tsx +28 -0
- package/registry/ui/collapsible.tsx +21 -0
- package/registry/ui/combobox.tsx +310 -0
- package/registry/ui/command.tsx +196 -0
- package/registry/ui/dashboard-card.tsx +174 -0
- package/registry/ui/data-table.tsx +16 -91
- package/registry/ui/date-range-picker.tsx +209 -0
- package/registry/ui/dialog.tsx +159 -0
- package/registry/ui/donut-chart.tsx +1004 -0
- package/registry/ui/drawer.tsx +229 -0
- package/registry/ui/dropdown-menu.tsx +272 -0
- package/registry/ui/echart.tsx +486 -0
- package/registry/ui/field.tsx +240 -0
- package/registry/ui/filter-bar.tsx +365 -0
- package/registry/ui/funnel-steps.tsx +274 -0
- package/registry/ui/hover-card.tsx +52 -0
- package/registry/ui/input-group.tsx +158 -0
- package/registry/ui/input.tsx +22 -0
- package/registry/ui/kbd.tsx +28 -0
- package/registry/ui/label.tsx +20 -0
- package/registry/ui/line-chart.tsx +1007 -0
- package/registry/ui/metric-value.tsx +54 -0
- package/registry/ui/multi-select.tsx +226 -0
- package/registry/ui/page-shell.tsx +227 -0
- package/registry/ui/pagination.tsx +138 -0
- package/registry/ui/placeholder.tsx +30 -0
- package/registry/ui/popover.tsx +90 -0
- package/registry/ui/progress-circle.tsx +272 -0
- package/registry/ui/progress.tsx +83 -0
- package/registry/ui/radio-group.tsx +39 -0
- package/registry/ui/scroll-area.tsx +55 -0
- package/registry/ui/searchable-select.tsx +209 -0
- package/registry/ui/section-header.tsx +41 -0
- package/registry/ui/segmented-control.tsx +71 -0
- package/registry/ui/select.tsx +253 -0
- package/registry/ui/separator.tsx +25 -0
- package/registry/ui/sheet.tsx +137 -0
- package/registry/ui/sidebar.tsx +723 -0
- package/registry/ui/skeleton.tsx +15 -0
- package/registry/ui/slider.tsx +54 -0
- package/registry/ui/sparkline.tsx +155 -0
- package/registry/ui/spinner.tsx +12 -0
- package/registry/ui/states.tsx +61 -0
- package/registry/ui/status-badge.tsx +79 -0
- package/registry/ui/switch.tsx +32 -0
- package/registry/ui/table.tsx +116 -0
- package/registry/ui/tabs.tsx +82 -0
- package/registry/ui/textarea.tsx +20 -0
- package/registry/ui/toast.tsx +250 -0
- package/registry/ui/toggle-group.tsx +89 -0
- package/registry/ui/toggle.tsx +45 -0
- package/registry/ui/tooltip.tsx +66 -0
- package/registry/ui/tracker.tsx +236 -0
- package/registry/ui/trend-indicator.tsx +62 -0
- package/skills/vantage-add-feature/SKILL.md +9 -7
- package/skills/vantage-design/SKILL.md +3 -1
- package/skills/vantage-design/references/format.md +1 -1
- package/skills/vantage-pitfalls/SKILL.md +5 -1
- package/skills/vantage-slide/SKILL.md +17 -0
- package/templates/AGENTS.md +10 -1
- package/templates/agents/api.md +29 -0
- package/templates/agents/csv.md +2 -1
- package/templates/agents/doc.md +4 -3
- package/templates/agents/page.md +22 -8
- package/templates/agents/slide.md +13 -1
- package/templates/agents/xlsx.md +2 -1
- package/theme.css +7 -1
- package/dist/add-DY4AXANK.js +0 -6
- package/dist/add-DY4AXANK.js.map +0 -1
- package/dist/chunk-BCE22JSM.js.map +0 -1
- package/dist/chunk-DTDVSFRY.js.map +0 -1
- package/dist/chunk-EP6JHXSR.js.map +0 -1
- package/dist/chunk-KLEIVDUV.js.map +0 -1
- package/dist/chunk-MP3QSOXL.js.map +0 -1
- package/dist/chunk-PVTHHV7X.js.map +0 -1
- package/dist/chunk-QI3XQKHS.js.map +0 -1
- package/dist/chunk-T3FLAGI6.js +0 -316
- package/dist/chunk-T3FLAGI6.js.map +0 -1
- package/dist/dev-FCEC3UKQ.js.map +0 -1
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Copied by `vantage add ui popover`. This file is yours to edit.
|
|
2
|
+
// Generated from @squadbase/vantage src/ui/popover.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Popover as PopoverPrimitive } from "@base-ui/react/popover"
|
|
5
|
+
|
|
6
|
+
import { cn } from "@squadbase/vantage/ui"
|
|
7
|
+
|
|
8
|
+
function Popover({ ...props }: PopoverPrimitive.Root.Props) {
|
|
9
|
+
return <PopoverPrimitive.Root data-slot="popover" {...props} />
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function PopoverTrigger({ ...props }: PopoverPrimitive.Trigger.Props) {
|
|
13
|
+
return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function PopoverContent({
|
|
17
|
+
className,
|
|
18
|
+
align = "center",
|
|
19
|
+
alignOffset = 0,
|
|
20
|
+
side = "bottom",
|
|
21
|
+
sideOffset = 4,
|
|
22
|
+
...props
|
|
23
|
+
}: PopoverPrimitive.Popup.Props &
|
|
24
|
+
Pick<
|
|
25
|
+
PopoverPrimitive.Positioner.Props,
|
|
26
|
+
"align" | "alignOffset" | "side" | "sideOffset"
|
|
27
|
+
>) {
|
|
28
|
+
return (
|
|
29
|
+
<PopoverPrimitive.Portal>
|
|
30
|
+
<PopoverPrimitive.Positioner
|
|
31
|
+
align={align}
|
|
32
|
+
alignOffset={alignOffset}
|
|
33
|
+
side={side}
|
|
34
|
+
sideOffset={sideOffset}
|
|
35
|
+
className="isolate z-50"
|
|
36
|
+
>
|
|
37
|
+
<PopoverPrimitive.Popup
|
|
38
|
+
data-slot="popover-content"
|
|
39
|
+
className={cn(
|
|
40
|
+
"z-50 flex w-72 origin-(--transform-origin) flex-col gap-2.5 rounded-lg bg-popover p-2.5 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
|
41
|
+
className
|
|
42
|
+
)}
|
|
43
|
+
{...props}
|
|
44
|
+
/>
|
|
45
|
+
</PopoverPrimitive.Positioner>
|
|
46
|
+
</PopoverPrimitive.Portal>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function PopoverHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
51
|
+
return (
|
|
52
|
+
<div
|
|
53
|
+
data-slot="popover-header"
|
|
54
|
+
className={cn("flex flex-col gap-0.5 text-sm", className)}
|
|
55
|
+
{...props}
|
|
56
|
+
/>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function PopoverTitle({ className, ...props }: PopoverPrimitive.Title.Props) {
|
|
61
|
+
return (
|
|
62
|
+
<PopoverPrimitive.Title
|
|
63
|
+
data-slot="popover-title"
|
|
64
|
+
className={cn("font-medium", className)}
|
|
65
|
+
{...props}
|
|
66
|
+
/>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function PopoverDescription({
|
|
71
|
+
className,
|
|
72
|
+
...props
|
|
73
|
+
}: PopoverPrimitive.Description.Props) {
|
|
74
|
+
return (
|
|
75
|
+
<PopoverPrimitive.Description
|
|
76
|
+
data-slot="popover-description"
|
|
77
|
+
className={cn("text-muted-foreground", className)}
|
|
78
|
+
{...props}
|
|
79
|
+
/>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export {
|
|
84
|
+
Popover,
|
|
85
|
+
PopoverContent,
|
|
86
|
+
PopoverDescription,
|
|
87
|
+
PopoverHeader,
|
|
88
|
+
PopoverTitle,
|
|
89
|
+
PopoverTrigger,
|
|
90
|
+
}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
// Copied by `vantage add ui progress-circle`. This file is yours to edit.
|
|
2
|
+
// Generated from @squadbase/vantage src/components/progress-circle.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
|
|
3
|
+
/**
|
|
4
|
+
* ProgressCircle — a ring that fills to `value / max`, with anything you pass
|
|
5
|
+
* as children in the middle (usually the percentage). Plain SVG, coloured by
|
|
6
|
+
* a chart token, so a row of them in KPI tiles stays light.
|
|
7
|
+
*/
|
|
8
|
+
import { forwardRef, useEffect, useState, type ComponentProps } from "react"
|
|
9
|
+
|
|
10
|
+
import { cn } from "@squadbase/vantage/ui"
|
|
11
|
+
import type { ChartColor } from "@squadbase/vantage/components"
|
|
12
|
+
import type { ValueFormatter } from "@squadbase/vantage/components"
|
|
13
|
+
|
|
14
|
+
/* ---------- inlined from src/components/chart-utils.ts (internal to @squadbase/vantage) ---------- */
|
|
15
|
+
|
|
16
|
+
/** The series tokens, in palette order — the default `colors` of every typed chart. */
|
|
17
|
+
const DEFAULT_CHART_COLORS: readonly ChartColor[] = [
|
|
18
|
+
"chart-1",
|
|
19
|
+
"chart-2",
|
|
20
|
+
"chart-3",
|
|
21
|
+
"chart-4",
|
|
22
|
+
"chart-5",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
const TOKEN_NAME_RE = /^chart-[1-5]$/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* A chart colour as a CSS value: `"chart-2"` → `var(--chart-2)` (so it follows
|
|
29
|
+
* light/dark on its own), anything else passes through unchanged.
|
|
30
|
+
*/
|
|
31
|
+
function chartColorToCss(color: ChartColor): string {
|
|
32
|
+
const value = color.trim()
|
|
33
|
+
return TOKEN_NAME_RE.test(value) ? `var(--${value})` : value
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The `i`-th colour, cycling through `colors` (the token palette when empty). */
|
|
37
|
+
function chartColorAt(colors: readonly ChartColor[] | undefined, i: number): ChartColor {
|
|
38
|
+
const palette = colors && colors.length > 0 ? colors : DEFAULT_CHART_COLORS
|
|
39
|
+
return palette[i % palette.length]!
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** `colors` extended (by cycling) to exactly `count` entries. */
|
|
43
|
+
function chartColorsFor(colors: readonly ChartColor[] | undefined, count: number): ChartColor[] {
|
|
44
|
+
return Array.from({ length: count }, (_, i) => chartColorAt(colors, i))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** A translucent version of a CSS colour (works for `var(--…)` too). */
|
|
48
|
+
function tintCss(cssColor: string, percent: number): string {
|
|
49
|
+
return `color-mix(in oklab, ${cssColor} ${percent}%, transparent)`
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const numberFormat = new Intl.NumberFormat("ja-JP", { maximumFractionDigits: 2 })
|
|
53
|
+
|
|
54
|
+
/** Default `valueFormatter`: grouped digits, up to two decimals (`1,234.5`). */
|
|
55
|
+
const defaultValueFormatter: ValueFormatter = (value) => numberFormat.format(value)
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* A data cell as a number, or `null` for a gap. Numeric strings count — SQL
|
|
59
|
+
* `DECIMAL` / `BIGINT` columns usually arrive as strings.
|
|
60
|
+
*/
|
|
61
|
+
function toChartNumber(value: unknown): number | null {
|
|
62
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null
|
|
63
|
+
if (typeof value === "bigint") return Number(value)
|
|
64
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
65
|
+
const n = Number(value)
|
|
66
|
+
return Number.isFinite(n) ? n : null
|
|
67
|
+
}
|
|
68
|
+
return null
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Clamp `value` into `[min, max]`. */
|
|
72
|
+
function clamp(value: number, min: number, max: number): number {
|
|
73
|
+
return Math.min(max, Math.max(min, value))
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* ---------------------------------------------------------------- BarList */
|
|
77
|
+
|
|
78
|
+
type BarListSortOrder = "ascending" | "descending" | "none"
|
|
79
|
+
|
|
80
|
+
/** Sort a copy of the rows by value; `"none"` keeps the given order. */
|
|
81
|
+
function sortBarListData<T extends { value: number }>(data: readonly T[], sortOrder: BarListSortOrder): T[] {
|
|
82
|
+
const rows = [...data]
|
|
83
|
+
if (sortOrder === "ascending") rows.sort((a, b) => a.value - b.value)
|
|
84
|
+
else if (sortOrder === "descending") rows.sort((a, b) => b.value - a.value)
|
|
85
|
+
return rows
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Bar widths in percent of the largest value (at least a sliver for non-zero values). */
|
|
89
|
+
function barListWidths(values: readonly number[]): number[] {
|
|
90
|
+
const max = Math.max(0, ...values)
|
|
91
|
+
if (max === 0) return values.map(() => 0)
|
|
92
|
+
return values.map((v) => (v <= 0 ? 0 : Math.max(1, (v / max) * 100)))
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* ------------------------------------------------------------ CategoryBar */
|
|
96
|
+
|
|
97
|
+
interface CategoryBarSegment {
|
|
98
|
+
/** Width in percent of the whole bar. */
|
|
99
|
+
width: number
|
|
100
|
+
/** Where the segment starts, in percent. */
|
|
101
|
+
start: number
|
|
102
|
+
/** The running total at the segment's end — what its right-hand label shows. */
|
|
103
|
+
end: number
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Lay the values side by side; negative values count as zero. */
|
|
107
|
+
function categoryBarSegments(values: readonly number[]): CategoryBarSegment[] {
|
|
108
|
+
const clean = values.map((v) => (Number.isFinite(v) && v > 0 ? v : 0))
|
|
109
|
+
const total = clean.reduce((sum, v) => sum + v, 0)
|
|
110
|
+
let running = 0
|
|
111
|
+
return clean.map((v) => {
|
|
112
|
+
const start = total === 0 ? 0 : (running / total) * 100
|
|
113
|
+
running += v
|
|
114
|
+
return { width: total === 0 ? 0 : (v / total) * 100, start, end: running }
|
|
115
|
+
})
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
interface CategoryBarLabel {
|
|
119
|
+
/** Position in percent of the bar. */
|
|
120
|
+
position: number
|
|
121
|
+
/** The running total printed there. */
|
|
122
|
+
value: number
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Tick labels under the bar: `0` at the start, then the running total at each
|
|
127
|
+
* segment's end. A label closer than `minGap` percent to the one before is
|
|
128
|
+
* dropped so narrow segments don't print on top of each other — except the
|
|
129
|
+
* last one (the total), which replaces its neighbour instead.
|
|
130
|
+
*/
|
|
131
|
+
function categoryBarLabels(values: readonly number[], minGap = 8): CategoryBarLabel[] {
|
|
132
|
+
const segments = categoryBarSegments(values).filter((s) => s.width > 0)
|
|
133
|
+
if (segments.length === 0) return []
|
|
134
|
+
const labels: CategoryBarLabel[] = [{ position: 0, value: 0 }]
|
|
135
|
+
segments.forEach((s, i) => {
|
|
136
|
+
const label = { position: s.start + s.width, value: s.end }
|
|
137
|
+
const prev = labels[labels.length - 1]!
|
|
138
|
+
if (label.position - prev.position >= minGap) labels.push(label)
|
|
139
|
+
else if (i === segments.length - 1 && labels.length > 1) labels[labels.length - 1] = label
|
|
140
|
+
})
|
|
141
|
+
return labels
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Marker position in percent of the bar, clamped to its ends. */
|
|
145
|
+
function categoryBarMarkerPosition(value: number, values: readonly number[]): number {
|
|
146
|
+
const total = values.reduce((sum, v) => sum + (Number.isFinite(v) && v > 0 ? v : 0), 0)
|
|
147
|
+
if (total === 0) return 0
|
|
148
|
+
return clamp((value / total) * 100, 0, 100)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Index of the segment the marker sits on (the last one for a value past the end). */
|
|
152
|
+
function categoryBarMarkerSegment(value: number, values: readonly number[]): number {
|
|
153
|
+
const segments = categoryBarSegments(values)
|
|
154
|
+
const index = segments.findIndex((s) => s.width > 0 && value <= s.end)
|
|
155
|
+
if (index !== -1) return index
|
|
156
|
+
for (let i = segments.length - 1; i >= 0; i--) if (segments[i]!.width > 0) return i
|
|
157
|
+
return 0
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* --------------------------------------------------------- ProgressCircle */
|
|
161
|
+
|
|
162
|
+
interface ProgressCircleGeometry {
|
|
163
|
+
/** SVG width/height. */
|
|
164
|
+
size: number
|
|
165
|
+
/** Radius of the stroke's centre line. */
|
|
166
|
+
normalizedRadius: number
|
|
167
|
+
circumference: number
|
|
168
|
+
/** `stroke-dashoffset` for the value arc. */
|
|
169
|
+
offset: number
|
|
170
|
+
/** The value as a fraction of `max`, clamped to `[0, 1]`. */
|
|
171
|
+
ratio: number
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function progressCircleGeometry(
|
|
175
|
+
value: number,
|
|
176
|
+
max: number,
|
|
177
|
+
radius: number,
|
|
178
|
+
strokeWidth: number,
|
|
179
|
+
): ProgressCircleGeometry {
|
|
180
|
+
const ratio = max > 0 && Number.isFinite(value) ? clamp(value / max, 0, 1) : 0
|
|
181
|
+
const normalizedRadius = Math.max(0, radius - strokeWidth / 2)
|
|
182
|
+
const circumference = 2 * Math.PI * normalizedRadius
|
|
183
|
+
return { size: radius * 2, normalizedRadius, circumference, offset: circumference * (1 - ratio), ratio }
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/* ---------- end of inlined code ---------- */
|
|
187
|
+
|
|
188
|
+
export interface ProgressCircleProps extends ComponentProps<"div"> {
|
|
189
|
+
/** 現在値。`0`〜`max` に丸める。 */
|
|
190
|
+
value?: number
|
|
191
|
+
/** 満タンの値。既定 `100`。 */
|
|
192
|
+
max?: number
|
|
193
|
+
/** 外側の半径(px)。既定 `32`(64px 四方)。 */
|
|
194
|
+
radius?: number
|
|
195
|
+
/** 線の太さ(px)。既定 `6`。 */
|
|
196
|
+
strokeWidth?: number
|
|
197
|
+
/** 色。`"chart-1"`〜`"chart-5"` か CSS カラー。既定 `"chart-1"`。地のリングは同じ色を淡くしたもの。 */
|
|
198
|
+
color?: ChartColor
|
|
199
|
+
/** マウント時と値の変化をアニメーションする。既定 `true`。 */
|
|
200
|
+
showAnimation?: boolean
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export const ProgressCircle = forwardRef<HTMLDivElement, ProgressCircleProps>(function ProgressCircle(
|
|
204
|
+
{
|
|
205
|
+
value = 0,
|
|
206
|
+
max = 100,
|
|
207
|
+
radius = 32,
|
|
208
|
+
strokeWidth = 6,
|
|
209
|
+
color = "chart-1",
|
|
210
|
+
showAnimation = true,
|
|
211
|
+
className,
|
|
212
|
+
children,
|
|
213
|
+
...props
|
|
214
|
+
},
|
|
215
|
+
ref,
|
|
216
|
+
) {
|
|
217
|
+
const [mounted, setMounted] = useState(!showAnimation)
|
|
218
|
+
useEffect(() => {
|
|
219
|
+
if (!showAnimation) return
|
|
220
|
+
const frame = requestAnimationFrame(() => setMounted(true))
|
|
221
|
+
return () => cancelAnimationFrame(frame)
|
|
222
|
+
}, [showAnimation])
|
|
223
|
+
|
|
224
|
+
const { size, normalizedRadius, circumference, offset, ratio } = progressCircleGeometry(value, max, radius, strokeWidth)
|
|
225
|
+
const css = chartColorToCss(color)
|
|
226
|
+
|
|
227
|
+
return (
|
|
228
|
+
<div
|
|
229
|
+
ref={ref}
|
|
230
|
+
data-slot="progress-circle"
|
|
231
|
+
role="progressbar"
|
|
232
|
+
aria-valuemin={0}
|
|
233
|
+
aria-valuemax={max}
|
|
234
|
+
aria-valuenow={value}
|
|
235
|
+
className={cn("relative inline-flex shrink-0 items-center justify-center", className)}
|
|
236
|
+
style={{ width: size, height: size }}
|
|
237
|
+
{...props}
|
|
238
|
+
>
|
|
239
|
+
<svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} className="-rotate-90" aria-hidden="true">
|
|
240
|
+
<circle
|
|
241
|
+
cx={radius}
|
|
242
|
+
cy={radius}
|
|
243
|
+
r={normalizedRadius}
|
|
244
|
+
fill="none"
|
|
245
|
+
strokeWidth={strokeWidth}
|
|
246
|
+
style={{ stroke: tintCss(css, 20) }}
|
|
247
|
+
/>
|
|
248
|
+
{ratio > 0 && (
|
|
249
|
+
<circle
|
|
250
|
+
data-slot="progress-circle-value"
|
|
251
|
+
cx={radius}
|
|
252
|
+
cy={radius}
|
|
253
|
+
r={normalizedRadius}
|
|
254
|
+
fill="none"
|
|
255
|
+
strokeWidth={strokeWidth}
|
|
256
|
+
strokeLinecap="round"
|
|
257
|
+
strokeDasharray={`${circumference} ${circumference}`}
|
|
258
|
+
strokeDashoffset={mounted ? offset : circumference}
|
|
259
|
+
className={cn(showAnimation && "transition-[stroke-dashoffset] duration-700 ease-out")}
|
|
260
|
+
style={{ stroke: css }}
|
|
261
|
+
/>
|
|
262
|
+
)}
|
|
263
|
+
</svg>
|
|
264
|
+
{children != null && (
|
|
265
|
+
<div className="absolute inset-0 flex items-center justify-center text-sm font-medium text-foreground tabular-nums">
|
|
266
|
+
{children}
|
|
267
|
+
</div>
|
|
268
|
+
)}
|
|
269
|
+
</div>
|
|
270
|
+
)
|
|
271
|
+
})
|
|
272
|
+
ProgressCircle.displayName = "ProgressCircle"
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// Copied by `vantage add ui progress`. This file is yours to edit.
|
|
2
|
+
// Generated from @squadbase/vantage src/ui/progress.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
|
|
3
|
+
import { Progress as ProgressPrimitive } from "@base-ui/react/progress"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@squadbase/vantage/ui"
|
|
6
|
+
|
|
7
|
+
function Progress({
|
|
8
|
+
className,
|
|
9
|
+
children,
|
|
10
|
+
value,
|
|
11
|
+
...props
|
|
12
|
+
}: ProgressPrimitive.Root.Props) {
|
|
13
|
+
return (
|
|
14
|
+
<ProgressPrimitive.Root
|
|
15
|
+
value={value}
|
|
16
|
+
data-slot="progress"
|
|
17
|
+
className={cn("flex flex-wrap gap-3", className)}
|
|
18
|
+
{...props}
|
|
19
|
+
>
|
|
20
|
+
{children}
|
|
21
|
+
<ProgressTrack>
|
|
22
|
+
<ProgressIndicator />
|
|
23
|
+
</ProgressTrack>
|
|
24
|
+
</ProgressPrimitive.Root>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function ProgressTrack({ className, ...props }: ProgressPrimitive.Track.Props) {
|
|
29
|
+
return (
|
|
30
|
+
<ProgressPrimitive.Track
|
|
31
|
+
className={cn(
|
|
32
|
+
"relative flex h-1 w-full items-center overflow-x-hidden rounded-full bg-muted",
|
|
33
|
+
className
|
|
34
|
+
)}
|
|
35
|
+
data-slot="progress-track"
|
|
36
|
+
{...props}
|
|
37
|
+
/>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function ProgressIndicator({
|
|
42
|
+
className,
|
|
43
|
+
...props
|
|
44
|
+
}: ProgressPrimitive.Indicator.Props) {
|
|
45
|
+
return (
|
|
46
|
+
<ProgressPrimitive.Indicator
|
|
47
|
+
data-slot="progress-indicator"
|
|
48
|
+
className={cn("h-full bg-primary transition-all", className)}
|
|
49
|
+
{...props}
|
|
50
|
+
/>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function ProgressLabel({ className, ...props }: ProgressPrimitive.Label.Props) {
|
|
55
|
+
return (
|
|
56
|
+
<ProgressPrimitive.Label
|
|
57
|
+
className={cn("text-sm font-medium", className)}
|
|
58
|
+
data-slot="progress-label"
|
|
59
|
+
{...props}
|
|
60
|
+
/>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function ProgressValue({ className, ...props }: ProgressPrimitive.Value.Props) {
|
|
65
|
+
return (
|
|
66
|
+
<ProgressPrimitive.Value
|
|
67
|
+
className={cn(
|
|
68
|
+
"ml-auto text-sm text-muted-foreground tabular-nums",
|
|
69
|
+
className
|
|
70
|
+
)}
|
|
71
|
+
data-slot="progress-value"
|
|
72
|
+
{...props}
|
|
73
|
+
/>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export {
|
|
78
|
+
Progress,
|
|
79
|
+
ProgressTrack,
|
|
80
|
+
ProgressIndicator,
|
|
81
|
+
ProgressLabel,
|
|
82
|
+
ProgressValue,
|
|
83
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Copied by `vantage add ui radio-group`. This file is yours to edit.
|
|
2
|
+
// Generated from @squadbase/vantage src/ui/radio-group.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
|
|
3
|
+
"use client"
|
|
4
|
+
|
|
5
|
+
import { Radio as RadioPrimitive } from "@base-ui/react/radio"
|
|
6
|
+
import { RadioGroup as RadioGroupPrimitive } from "@base-ui/react/radio-group"
|
|
7
|
+
import { cn } from "@squadbase/vantage/ui"
|
|
8
|
+
|
|
9
|
+
function RadioGroup({ className, ...props }: RadioGroupPrimitive.Props) {
|
|
10
|
+
return (
|
|
11
|
+
<RadioGroupPrimitive
|
|
12
|
+
data-slot="radio-group"
|
|
13
|
+
className={cn("grid w-full gap-2", className)}
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function RadioGroupItem({ className, ...props }: RadioPrimitive.Root.Props) {
|
|
20
|
+
return (
|
|
21
|
+
<RadioPrimitive.Root
|
|
22
|
+
data-slot="radio-group-item"
|
|
23
|
+
className={cn(
|
|
24
|
+
"group/radio-group-item peer relative flex aspect-square size-4 shrink-0 rounded-full border border-input outline-none group-has-[:focus-visible]/field-label:ring-0 group-has-[:focus-visible]/field-label:not-data-checked:border-input after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground group-has-[:focus-visible]/field-label:data-checked:border-primary dark:data-checked:bg-primary",
|
|
25
|
+
className
|
|
26
|
+
)}
|
|
27
|
+
{...props}
|
|
28
|
+
>
|
|
29
|
+
<RadioPrimitive.Indicator
|
|
30
|
+
data-slot="radio-group-indicator"
|
|
31
|
+
className="flex size-4 items-center justify-center"
|
|
32
|
+
>
|
|
33
|
+
<span className="absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-primary-foreground" />
|
|
34
|
+
</RadioPrimitive.Indicator>
|
|
35
|
+
</RadioPrimitive.Root>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export { RadioGroup, RadioGroupItem }
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Copied by `vantage add ui scroll-area`. This file is yours to edit.
|
|
2
|
+
// Generated from @squadbase/vantage src/ui/scroll-area.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { ScrollArea as ScrollAreaPrimitive } from "@base-ui/react/scroll-area"
|
|
5
|
+
|
|
6
|
+
import { cn } from "@squadbase/vantage/ui"
|
|
7
|
+
|
|
8
|
+
function ScrollArea({
|
|
9
|
+
className,
|
|
10
|
+
children,
|
|
11
|
+
...props
|
|
12
|
+
}: ScrollAreaPrimitive.Root.Props) {
|
|
13
|
+
return (
|
|
14
|
+
<ScrollAreaPrimitive.Root
|
|
15
|
+
data-slot="scroll-area"
|
|
16
|
+
className={cn("relative", className)}
|
|
17
|
+
{...props}
|
|
18
|
+
>
|
|
19
|
+
<ScrollAreaPrimitive.Viewport
|
|
20
|
+
data-slot="scroll-area-viewport"
|
|
21
|
+
className="size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1"
|
|
22
|
+
>
|
|
23
|
+
{children}
|
|
24
|
+
</ScrollAreaPrimitive.Viewport>
|
|
25
|
+
<ScrollBar />
|
|
26
|
+
<ScrollAreaPrimitive.Corner />
|
|
27
|
+
</ScrollAreaPrimitive.Root>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function ScrollBar({
|
|
32
|
+
className,
|
|
33
|
+
orientation = "vertical",
|
|
34
|
+
...props
|
|
35
|
+
}: ScrollAreaPrimitive.Scrollbar.Props) {
|
|
36
|
+
return (
|
|
37
|
+
<ScrollAreaPrimitive.Scrollbar
|
|
38
|
+
data-slot="scroll-area-scrollbar"
|
|
39
|
+
data-orientation={orientation}
|
|
40
|
+
orientation={orientation}
|
|
41
|
+
className={cn(
|
|
42
|
+
"flex touch-none p-px transition-colors select-none data-horizontal:h-2.5 data-horizontal:flex-col data-horizontal:border-t data-horizontal:border-t-transparent data-vertical:h-full data-vertical:w-2.5 data-vertical:border-l data-vertical:border-l-transparent",
|
|
43
|
+
className
|
|
44
|
+
)}
|
|
45
|
+
{...props}
|
|
46
|
+
>
|
|
47
|
+
<ScrollAreaPrimitive.Thumb
|
|
48
|
+
data-slot="scroll-area-thumb"
|
|
49
|
+
className="relative flex-1 rounded-full bg-border"
|
|
50
|
+
/>
|
|
51
|
+
</ScrollAreaPrimitive.Scrollbar>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export { ScrollArea, ScrollBar }
|