@squadbase/vantage 0.6.2-dev1.b332e5a → 0.6.2-dev1.d7c0911

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 (212) hide show
  1. package/dist/add-4GHF2TX2.js +6 -0
  2. package/dist/add-4GHF2TX2.js.map +1 -0
  3. package/dist/{build-TVKFIVFQ.js → build-X5NVCQKT.js} +7 -7
  4. package/dist/{build-TVKFIVFQ.js.map → build-X5NVCQKT.js.map} +1 -1
  5. package/dist/{check-3KHZXZMQ.js → check-ZBIH422T.js} +5 -5
  6. package/dist/{check-3KHZXZMQ.js.map → check-ZBIH422T.js.map} +1 -1
  7. package/dist/chunk-2ICNHUG6.js +1820 -0
  8. package/dist/chunk-2ICNHUG6.js.map +1 -0
  9. package/dist/{chunk-5ZOP4SQR.js → chunk-2N72FKLB.js} +12 -7
  10. package/dist/chunk-2N72FKLB.js.map +1 -0
  11. package/dist/{chunk-P7W4LR3Z.js → chunk-7DZGJQ3P.js} +3 -3
  12. package/dist/{chunk-P7W4LR3Z.js.map → chunk-7DZGJQ3P.js.map} +1 -1
  13. package/dist/{chunk-U2M6HGKQ.js → chunk-AABMKHEU.js} +4 -4
  14. package/dist/{chunk-U2M6HGKQ.js.map → chunk-AABMKHEU.js.map} +1 -1
  15. package/dist/{chunk-QI3XQKHS.js → chunk-GNXCX4WE.js} +10 -3
  16. package/dist/chunk-GNXCX4WE.js.map +1 -0
  17. package/dist/{chunk-PXRX5PEC.js → chunk-PDOBHMUQ.js} +3 -3
  18. package/dist/chunk-PDOBHMUQ.js.map +1 -0
  19. package/dist/{chunk-ZVANIULZ.js → chunk-RNTDBX6S.js} +4 -4
  20. package/dist/{chunk-ZVANIULZ.js.map → chunk-RNTDBX6S.js.map} +1 -1
  21. package/dist/{chunk-A2UUGASH.js → chunk-U736PVHY.js} +2 -2
  22. package/dist/chunk-U736PVHY.js.map +1 -0
  23. package/dist/{chunk-PVTHHV7X.js → chunk-V3LF7BE7.js} +27 -5
  24. package/dist/chunk-V3LF7BE7.js.map +1 -0
  25. package/dist/{chunk-LMUACBMA.js → chunk-WAMZWZJB.js} +53 -8
  26. package/dist/chunk-WAMZWZJB.js.map +1 -0
  27. package/dist/chunk-WRSX2MDK.js +7866 -0
  28. package/dist/chunk-WRSX2MDK.js.map +1 -0
  29. package/dist/{chunk-RPZAALCX.js → chunk-XPQSTZUU.js} +254 -164
  30. package/dist/chunk-XPQSTZUU.js.map +1 -0
  31. package/dist/{chunk-DTDVSFRY.js → chunk-XQUSGIDG.js} +17 -3
  32. package/dist/chunk-XQUSGIDG.js.map +1 -0
  33. package/dist/cli.js +12 -11
  34. package/dist/cli.js.map +1 -1
  35. package/dist/client/index.d.ts +32 -3
  36. package/dist/client/index.js +205 -12
  37. package/dist/client/index.js.map +1 -1
  38. package/dist/components/index.d.ts +1975 -28
  39. package/dist/components/index.js +4 -4
  40. package/dist/{dev-GDKATCC7.js → dev-4XEDTELV.js} +8 -8
  41. package/dist/dev-4XEDTELV.js.map +1 -0
  42. package/dist/doc/index.js +2 -2
  43. package/dist/doc/index.js.map +1 -1
  44. package/dist/{docs-EJJ3BECT.js → docs-JVP4RPKT.js} +3 -3
  45. package/dist/{docs-EJJ3BECT.js.map → docs-JVP4RPKT.js.map} +1 -1
  46. package/dist/{doctor-WQXPXP6K.js → doctor-5E2VEZPK.js} +5 -5
  47. package/dist/{doctor-WQXPXP6K.js.map → doctor-5E2VEZPK.js.map} +1 -1
  48. package/dist/markdown/index.js +1 -1
  49. package/dist/markdown/index.js.map +1 -1
  50. package/dist/{preview-OGMCNQUO.js → preview-T6CLSQVX.js} +3 -3
  51. package/dist/{preview-OGMCNQUO.js.map → preview-T6CLSQVX.js.map} +1 -1
  52. package/dist/query/index.d.ts +0 -11
  53. package/dist/query/index.js +2 -2
  54. package/dist/{routes-MWBWWOJ2.js → routes-3SGAFDMC.js} +4 -4
  55. package/dist/{routes-MWBWWOJ2.js.map → routes-3SGAFDMC.js.map} +1 -1
  56. package/dist/{search-6Y422SV6.js → search-5LC4XPB5.js} +3 -3
  57. package/dist/{search-6Y422SV6.js.map → search-5LC4XPB5.js.map} +1 -1
  58. package/dist/table/index.js +10 -9
  59. package/dist/table/index.js.map +1 -1
  60. package/dist/ui/index.d.ts +144 -11
  61. package/dist/ui/index.js +1339 -87
  62. package/dist/ui/index.js.map +1 -1
  63. package/dist/{upgrade-HCANX4GQ.js → upgrade-JHXZBGQS.js} +6 -6
  64. package/dist/{upgrade-HCANX4GQ.js.map → upgrade-JHXZBGQS.js.map} +1 -1
  65. package/dist/vite/index.d.ts +10 -0
  66. package/dist/vite/index.js +3 -3
  67. package/docs/en/components.md +29 -15
  68. package/docs/en/parts/data-table.md +22 -0
  69. package/docs/en/parts/delta.md +50 -0
  70. package/docs/en/parts/format.md +43 -0
  71. package/docs/en/parts/kpi-card.md +57 -0
  72. package/docs/en/parts/metric-value.md +3 -2
  73. package/docs/en/parts/sparkline.md +1 -1
  74. package/docs/en/parts/trend-indicator.md +17 -1
  75. package/docs/en/ui-and-theming.md +1 -1
  76. package/docs/index.json +366 -76
  77. package/docs/ja/changelog.md +31 -0
  78. package/docs/ja/components.md +57 -101
  79. package/docs/ja/page-types/csv.md +83 -0
  80. package/docs/ja/page-types/doc.md +83 -0
  81. package/docs/ja/page-types/excel.md +82 -0
  82. package/docs/ja/page-types/slide.md +132 -0
  83. package/docs/ja/page-types.md +47 -0
  84. package/docs/ja/parts/area-chart.md +61 -0
  85. package/docs/ja/parts/bar-chart.md +69 -0
  86. package/docs/ja/parts/bar-list.md +35 -0
  87. package/docs/ja/parts/category-bar.md +36 -0
  88. package/docs/ja/parts/data-table.md +22 -0
  89. package/docs/ja/parts/delta.md +50 -0
  90. package/docs/ja/parts/donut-chart.md +47 -0
  91. package/docs/ja/parts/echart.md +3 -0
  92. package/docs/ja/parts/format.md +44 -0
  93. package/docs/ja/parts/kpi-card.md +55 -0
  94. package/docs/ja/parts/line-chart.md +58 -0
  95. package/docs/ja/parts/metric-value.md +3 -2
  96. package/docs/ja/parts/progress-circle.md +34 -0
  97. package/docs/ja/parts/sparkline.md +2 -2
  98. package/docs/ja/parts/tracker.md +32 -0
  99. package/docs/ja/parts/trend-indicator.md +17 -1
  100. package/docs/ja/routing.md +8 -86
  101. package/docs/ja/ui/alert-dialog.md +41 -0
  102. package/docs/ja/ui/avatar.md +31 -0
  103. package/docs/ja/ui/combobox.md +74 -0
  104. package/docs/ja/ui/drawer.md +41 -0
  105. package/docs/ja/ui/field.md +42 -0
  106. package/docs/ja/ui/hover-card.md +21 -0
  107. package/docs/ja/ui/kbd.md +16 -0
  108. package/docs/ja/ui/pagination.md +36 -0
  109. package/docs/ja/ui/radio-group.md +21 -0
  110. package/docs/ja/ui/toast.md +46 -0
  111. package/docs/ja/ui-and-theming.md +14 -7
  112. package/package.json +13 -4
  113. package/registry/ui/accordion.tsx +76 -0
  114. package/registry/ui/alert-dialog.tsx +189 -0
  115. package/registry/ui/alert.tsx +80 -0
  116. package/registry/ui/app-shell.tsx +287 -0
  117. package/registry/ui/area-chart.tsx +3639 -0
  118. package/registry/ui/avatar.tsx +110 -0
  119. package/registry/ui/badge.tsx +56 -0
  120. package/registry/ui/bar-chart.tsx +3636 -0
  121. package/registry/ui/bar-list.tsx +3680 -0
  122. package/registry/ui/breadcrumb.tsx +128 -0
  123. package/registry/ui/button.tsx +62 -0
  124. package/registry/ui/calendar.tsx +223 -0
  125. package/registry/ui/card.tsx +107 -0
  126. package/registry/ui/category-bar.tsx +1255 -0
  127. package/registry/ui/chart-frame.tsx +1358 -0
  128. package/registry/ui/checkbox.tsx +30 -0
  129. package/registry/ui/collapsible.tsx +23 -0
  130. package/registry/ui/combobox.tsx +310 -0
  131. package/registry/ui/command.tsx +198 -0
  132. package/registry/ui/dashboard-card.tsx +217 -0
  133. package/registry/ui/data-table.tsx +693 -146
  134. package/registry/ui/date-range-picker.tsx +390 -0
  135. package/registry/ui/delta.tsx +188 -0
  136. package/registry/ui/dialog.tsx +161 -0
  137. package/registry/ui/donut-chart.tsx +3688 -0
  138. package/registry/ui/drawer.tsx +229 -0
  139. package/registry/ui/dropdown-menu.tsx +274 -0
  140. package/registry/ui/echart.tsx +890 -0
  141. package/registry/ui/field.tsx +240 -0
  142. package/registry/ui/filter-bar.tsx +392 -0
  143. package/registry/ui/funnel-steps.tsx +3541 -0
  144. package/registry/ui/heatmap.tsx +1752 -0
  145. package/registry/ui/hover-card.tsx +52 -0
  146. package/registry/ui/input-group.tsx +160 -0
  147. package/registry/ui/input.tsx +24 -0
  148. package/registry/ui/kbd.tsx +30 -0
  149. package/registry/ui/kpi-card.tsx +677 -0
  150. package/registry/ui/label.tsx +22 -0
  151. package/registry/ui/legend.tsx +3500 -0
  152. package/registry/ui/line-chart.tsx +3637 -0
  153. package/registry/ui/metric-value.tsx +62 -0
  154. package/registry/ui/multi-select.tsx +232 -0
  155. package/registry/ui/page-shell.tsx +282 -0
  156. package/registry/ui/pagination.tsx +140 -0
  157. package/registry/ui/placeholder.tsx +36 -0
  158. package/registry/ui/popover.tsx +92 -0
  159. package/registry/ui/progress-circle.tsx +867 -0
  160. package/registry/ui/progress.tsx +85 -0
  161. package/registry/ui/radio-group.tsx +39 -0
  162. package/registry/ui/sample-data.tsx +86 -0
  163. package/registry/ui/scatter-chart.tsx +4150 -0
  164. package/registry/ui/scroll-area.tsx +56 -0
  165. package/registry/ui/searchable-select.tsx +214 -0
  166. package/registry/ui/section-header.tsx +43 -0
  167. package/registry/ui/segmented-control.tsx +76 -0
  168. package/registry/ui/select.tsx +255 -0
  169. package/registry/ui/separator.tsx +27 -0
  170. package/registry/ui/sheet.tsx +139 -0
  171. package/registry/ui/sidebar.tsx +725 -0
  172. package/registry/ui/skeleton.tsx +17 -0
  173. package/registry/ui/slider.tsx +56 -0
  174. package/registry/ui/sparkline.tsx +230 -0
  175. package/registry/ui/spinner.tsx +14 -0
  176. package/registry/ui/states.tsx +90 -0
  177. package/registry/ui/status-badge.tsx +121 -0
  178. package/registry/ui/switch.tsx +34 -0
  179. package/registry/ui/table.tsx +118 -0
  180. package/registry/ui/tabs.tsx +84 -0
  181. package/registry/ui/textarea.tsx +22 -0
  182. package/registry/ui/toast.tsx +250 -0
  183. package/registry/ui/toggle-group.tsx +91 -0
  184. package/registry/ui/toggle.tsx +47 -0
  185. package/registry/ui/tooltip.tsx +68 -0
  186. package/registry/ui/tracker.tsx +843 -0
  187. package/registry/ui/trend-indicator.tsx +74 -0
  188. package/registry/ui/updated-at.tsx +122 -0
  189. package/registry/ui/waterfall-chart.tsx +4186 -0
  190. package/skills/vantage-add-feature/SKILL.md +9 -7
  191. package/skills/vantage-pitfalls/SKILL.md +5 -1
  192. package/templates/AGENTS.md +22 -1
  193. package/templates/agents/csv.md +2 -1
  194. package/templates/agents/doc.md +4 -3
  195. package/templates/agents/page.md +20 -6
  196. package/templates/agents/xlsx.md +2 -1
  197. package/theme.css +237 -136
  198. package/dist/add-DY4AXANK.js +0 -6
  199. package/dist/add-DY4AXANK.js.map +0 -1
  200. package/dist/chunk-5ZOP4SQR.js.map +0 -1
  201. package/dist/chunk-A2UUGASH.js.map +0 -1
  202. package/dist/chunk-DTDVSFRY.js.map +0 -1
  203. package/dist/chunk-LMUACBMA.js.map +0 -1
  204. package/dist/chunk-NKFBFDRZ.js +0 -2241
  205. package/dist/chunk-NKFBFDRZ.js.map +0 -1
  206. package/dist/chunk-PVTHHV7X.js.map +0 -1
  207. package/dist/chunk-PXRX5PEC.js.map +0 -1
  208. package/dist/chunk-QI3XQKHS.js.map +0 -1
  209. package/dist/chunk-RPZAALCX.js.map +0 -1
  210. package/dist/chunk-VA776YE5.js +0 -316
  211. package/dist/chunk-VA776YE5.js.map +0 -1
  212. package/dist/dev-GDKATCC7.js.map +0 -1
@@ -0,0 +1,17 @@
1
+ // Copied by `vantage add ui skeleton`. This file is yours to edit.
2
+ // Generated from @squadbase/squadkit registry/ui/skeleton.tsx by `npm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
+ "use client"
4
+
5
+ import { cn } from "@squadbase/vantage/ui"
6
+
7
+ function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
8
+ return (
9
+ <div
10
+ data-slot="skeleton"
11
+ className={cn("animate-pulse motion-reduce:animate-none rounded-md bg-muted", className)}
12
+ {...props}
13
+ />
14
+ )
15
+ }
16
+
17
+ export { Skeleton }
@@ -0,0 +1,56 @@
1
+ // Copied by `vantage add ui slider`. This file is yours to edit.
2
+ // Generated from @squadbase/squadkit registry/ui/slider.tsx by `npm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
+ "use client"
4
+
5
+ import { Slider as SliderPrimitive } from "@base-ui/react/slider"
6
+
7
+ import { cn } from "@squadbase/vantage/ui"
8
+
9
+ function Slider({
10
+ className,
11
+ defaultValue,
12
+ value,
13
+ min = 0,
14
+ max = 100,
15
+ ...props
16
+ }: SliderPrimitive.Root.Props) {
17
+ const _values = Array.isArray(value)
18
+ ? value
19
+ : Array.isArray(defaultValue)
20
+ ? defaultValue
21
+ : [min, max]
22
+
23
+ return (
24
+ <SliderPrimitive.Root
25
+ className={cn("data-horizontal:w-full data-vertical:h-full", className)}
26
+ data-slot="slider"
27
+ defaultValue={defaultValue}
28
+ value={value}
29
+ min={min}
30
+ max={max}
31
+ thumbAlignment="edge"
32
+ {...props}
33
+ >
34
+ <SliderPrimitive.Control className="relative flex w-full touch-none items-center select-none data-disabled:opacity-50 data-vertical:h-full data-vertical:min-h-40 data-vertical:w-auto data-vertical:flex-col">
35
+ <SliderPrimitive.Track
36
+ data-slot="slider-track"
37
+ className="relative grow overflow-hidden rounded-full bg-muted select-none data-horizontal:h-1 data-horizontal:w-full data-vertical:h-full data-vertical:w-1"
38
+ >
39
+ <SliderPrimitive.Indicator
40
+ data-slot="slider-range"
41
+ className="bg-primary select-none data-horizontal:h-full data-vertical:w-full"
42
+ />
43
+ </SliderPrimitive.Track>
44
+ {Array.from({ length: _values.length }, (_, index) => (
45
+ <SliderPrimitive.Thumb
46
+ data-slot="slider-thumb"
47
+ key={index}
48
+ className="relative block size-3 shrink-0 rounded-full border border-ring bg-white ring-ring/50 transition-[color,box-shadow] select-none after:absolute after:-inset-2 hover:ring-3 focus-visible:ring-3 focus-visible:outline-hidden active:ring-3 disabled:pointer-events-none disabled:opacity-50"
49
+ />
50
+ ))}
51
+ </SliderPrimitive.Control>
52
+ </SliderPrimitive.Root>
53
+ )
54
+ }
55
+
56
+ export { Slider }
@@ -0,0 +1,230 @@
1
+ // Copied by `vantage add ui sparkline`. This file is yours to edit.
2
+ // Generated from @squadbase/squadkit registry/charts/sparkline.tsx by `npm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
+ "use client"
4
+
5
+ /**
6
+ * Sparkline — an inline trend micro-chart. Deliberately *not* an `EChart`: a
7
+ * sparkline sits inside table cells and KPI tiles by the dozen, where one
8
+ * ECharts instance per cell would be wasteful. This renders a single stateless
9
+ * `<svg>` instead, so hundreds of them stay cheap.
10
+ */
11
+ import { forwardRef, useEffect, useState, type SVGAttributes } from "react"
12
+
13
+ import { cn } from "@squadbase/vantage/ui"
14
+ import type { ChartHighlight } from "@squadbase/vantage/components"
15
+
16
+ export type SparklineVariant = "line" | "bar"
17
+
18
+ export interface SparklineDataPoint {
19
+ value: number
20
+ label?: string
21
+ }
22
+
23
+ export interface SparklineProps extends SVGAttributes<SVGSVGElement> {
24
+ data: SparklineDataPoint[]
25
+ variant?: SparklineVariant
26
+ height?: number
27
+ /** Tailwind text-colour class; drives both stroke and fill via `currentColor`. */
28
+ color?: string
29
+ area?: boolean
30
+ /** マウント時に線を引く / 棒を伸ばす。既定 `false`(`prefers-reduced-motion` では常に無効)。 */
31
+ animate?: boolean
32
+ /**
33
+ * 点の強調(Tufte 流)。`"auto"` は最新・最大・最小の 3 点だけに小さな点を打つ
34
+ * (最新は系列の色、最大・最小は muted)。`"latest"` は最新の 1 点だけ、`"none"` は線だけ。`variant="line"` のときだけ効く。既定 `"auto"`。
35
+ */
36
+ highlight?: ChartHighlight | "latest"
37
+ /** 指定すると `role="img"` で読み上げ対象にする。省略時は装飾として `aria-hidden`。 */
38
+ "aria-label"?: string
39
+ className?: string
40
+ }
41
+
42
+ const VB_WIDTH = 100
43
+
44
+ /** Map raw values onto SVG y-coordinates (inverted, padded). */
45
+ function normalizeData(values: number[], height: number, vPad = 2): number[] {
46
+ const min = Math.min(...values)
47
+ const max = Math.max(...values)
48
+ const range = max - min
49
+ const drawHeight = height - vPad * 2
50
+ if (range === 0) return values.map(() => vPad + drawHeight / 2)
51
+ return values.map((v) => height - vPad - ((v - min) / range) * drawHeight)
52
+ }
53
+
54
+ function buildLinePath(ys: number[]): string {
55
+ if (ys.length === 0) return ""
56
+ const step = ys.length > 1 ? VB_WIDTH / (ys.length - 1) : 0
57
+ return ys
58
+ .map((y, i) => `${i === 0 ? "M" : "L"}${(i * step).toFixed(2)},${y.toFixed(2)}`)
59
+ .join(" ")
60
+ }
61
+
62
+ function buildAreaPath(ys: number[], height: number): string {
63
+ if (ys.length === 0) return ""
64
+ const step = ys.length > 1 ? VB_WIDTH / (ys.length - 1) : 0
65
+ const lastX = ((ys.length - 1) * step).toFixed(2)
66
+ return `${buildLinePath(ys)} L${lastX},${height} L0,${height} Z`
67
+ }
68
+
69
+ export type SparklinePointKind = "latest" | "max" | "min"
70
+
71
+ export interface SparklinePoint {
72
+ kind: SparklinePointKind
73
+ index: number
74
+ }
75
+
76
+ /**
77
+ * The points a Tufte-style sparkline marks: the latest, the max and the min.
78
+ * A point that is both latest and max/min is drawn once, as `latest`; a flat
79
+ * series marks only the latest. Ties pick the most recent occurrence.
80
+ */
81
+ export function sparklineHighlightPoints(values: number[]): SparklinePoint[] {
82
+ if (values.length === 0) return []
83
+ const last = values.length - 1
84
+ const points: SparklinePoint[] = [{ kind: "latest", index: last }]
85
+ let maxI = 0
86
+ let minI = 0
87
+ let max = values[0] as number
88
+ let min = max
89
+ values.forEach((v, i) => {
90
+ if (v >= max) [max, maxI] = [v, i]
91
+ if (v <= min) [min, minI] = [v, i]
92
+ })
93
+ const latest = values[last] as number
94
+ if (max === min) return points
95
+ if (max !== latest) points.push({ kind: "max", index: maxI })
96
+ if (min !== latest) points.push({ kind: "min", index: minI })
97
+ return points
98
+ }
99
+
100
+ interface BarRect {
101
+ x: number
102
+ y: number
103
+ w: number
104
+ h: number
105
+ }
106
+
107
+ function buildBarRects(ys: number[], height: number, gap = 1): BarRect[] {
108
+ const barW = Math.max(1, VB_WIDTH / ys.length - gap)
109
+ return ys.map((y, i) => ({ x: i * (barW + gap), y, w: barW, h: height - y }))
110
+ }
111
+
112
+ /** Rows not given yet (a draft fills props in steps): draw the empty state, keep the same array between renders. */
113
+ const NO_ROWS: never[] = []
114
+
115
+ export const Sparkline = forwardRef<SVGSVGElement, SparklineProps>(function Sparkline(
116
+ {
117
+ data = NO_ROWS,
118
+ variant = "line",
119
+ height = 40,
120
+ color = "text-chart-1",
121
+ area = false,
122
+ animate = false,
123
+ highlight = "auto",
124
+ "aria-label": ariaLabel,
125
+ className,
126
+ ...props
127
+ },
128
+ ref,
129
+ ) {
130
+ const [mounted, setMounted] = useState(false)
131
+
132
+ useEffect(() => {
133
+ if (animate) setMounted(true)
134
+ }, [animate])
135
+
136
+ if (data.length === 0) return null
137
+
138
+ const values = data.map((d) => d.value)
139
+ const ys = normalizeData(values, height)
140
+ const step = ys.length > 1 ? VB_WIDTH / (ys.length - 1) : 0
141
+ const points =
142
+ variant !== "line"
143
+ ? []
144
+ : highlight === "auto"
145
+ ? sparklineHighlightPoints(values)
146
+ : highlight === "latest"
147
+ ? [{ kind: "latest" as const, index: values.length - 1 }]
148
+ : []
149
+
150
+ return (
151
+ <svg
152
+ ref={ref}
153
+ data-slot="sparkline"
154
+ viewBox={`0 0 ${VB_WIDTH} ${height}`}
155
+ preserveAspectRatio="none"
156
+ width="100%"
157
+ height={height}
158
+ role={ariaLabel ? "img" : undefined}
159
+ aria-label={ariaLabel}
160
+ aria-hidden={ariaLabel ? undefined : true}
161
+ className={cn("overflow-visible", className)}
162
+ {...props}
163
+ >
164
+ {variant === "line" && (
165
+ <>
166
+ {area && (
167
+ <path
168
+ d={buildAreaPath(ys, height)}
169
+ strokeWidth={0}
170
+ className={cn("fill-current opacity-15", color)}
171
+ />
172
+ )}
173
+ <path
174
+ d={buildLinePath(ys)}
175
+ fill="none"
176
+ strokeWidth={1.5}
177
+ strokeLinecap="round"
178
+ strokeLinejoin="round"
179
+ vectorEffect="non-scaling-stroke"
180
+ pathLength={animate ? 1 : undefined}
181
+ strokeDasharray={animate ? 1 : undefined}
182
+ strokeDashoffset={animate ? (mounted ? 0 : 1) : undefined}
183
+ className={cn("stroke-current", animate && "motion-reduce:transition-none!", color)}
184
+ style={animate ? { transition: "stroke-dashoffset 0.6s ease-in-out" } : undefined}
185
+ />
186
+ {/* A zero-length round-capped stroke with a non-scaling width stays a
187
+ circle even though the viewBox stretches (`preserveAspectRatio="none"`). */}
188
+ {points.map((p) => {
189
+ const x = (p.index * step).toFixed(2)
190
+ const y = (ys[p.index] ?? 0).toFixed(2)
191
+ return (
192
+ <path
193
+ key={p.kind}
194
+ data-slot="sparkline-point"
195
+ data-point={p.kind}
196
+ d={`M${x},${y} L${x},${y}`}
197
+ fill="none"
198
+ strokeWidth={p.kind === "latest" ? 5 : 4}
199
+ strokeLinecap="round"
200
+ vectorEffect="non-scaling-stroke"
201
+ className={cn("stroke-current", p.kind === "latest" ? color : "text-muted-foreground")}
202
+ />
203
+ )
204
+ })}
205
+ </>
206
+ )}
207
+
208
+ {variant === "bar" &&
209
+ buildBarRects(ys, height).map((rect, i) => (
210
+ <rect
211
+ key={i}
212
+ x={rect.x}
213
+ y={animate ? (mounted ? rect.y : height) : rect.y}
214
+ width={rect.w}
215
+ height={animate ? (mounted ? rect.h : 0) : rect.h}
216
+ rx={0.5}
217
+ className={cn("fill-current", animate && "motion-reduce:transition-none!", color)}
218
+ style={
219
+ animate
220
+ ? {
221
+ transition: `height 0.4s ease-out ${i * 0.02}s, y 0.4s ease-out ${i * 0.02}s`,
222
+ }
223
+ : undefined
224
+ }
225
+ />
226
+ ))}
227
+ </svg>
228
+ )
229
+ })
230
+ Sparkline.displayName = "Sparkline"
@@ -0,0 +1,14 @@
1
+ // Copied by `vantage add ui spinner`. This file is yours to edit.
2
+ // Generated from @squadbase/squadkit registry/ui/spinner.tsx by `npm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
+ "use client"
4
+
5
+ import { cn } from "@squadbase/vantage/ui"
6
+ import { Loader2Icon } from "lucide-react"
7
+
8
+ function Spinner({ className, ...props }: React.ComponentProps<"svg">) {
9
+ return (
10
+ <Loader2Icon data-slot="spinner" role="status" aria-label="Loading" className={cn("size-4 animate-spin", className)} {...props} />
11
+ )
12
+ }
13
+
14
+ export { Spinner }
@@ -0,0 +1,90 @@
1
+ // Copied by `vantage add ui states`. This file is yours to edit.
2
+ // Generated from @squadbase/squadkit registry/ui/states.tsx by `npm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
+ "use client"
4
+
5
+ /**
6
+ * Loading / Empty / ErrorState — the three non-data states of a panel, worded for
7
+ * business users. Defaults are Japanese and say what is happening, not why the
8
+ * system failed: keep raw errors ("upstream timeout after 30s") in `details`,
9
+ * which stays folded behind "詳細を表示".
10
+ *
11
+ * @example
12
+ * import { ErrorState } from "@/components/ui/states"
13
+ * // Vantage: import { ErrorState } from "@squadbase/vantage/ui"
14
+ *
15
+ * <ErrorState
16
+ * title="売上を読み込めませんでした"
17
+ * message="データソースから時間内に応答がありませんでした。しばらくしてから再試行してください。"
18
+ * details={error.message}
19
+ * action={<Button variant="outline" onClick={retry}>再試行する</Button>}
20
+ * />
21
+ */
22
+ import type { ReactNode } from "react"
23
+ import { cn } from "@squadbase/vantage/ui"
24
+
25
+ export function Loading({ label = "読み込んでいます…", className }: { label?: string; className?: string }) {
26
+ return (
27
+ <div className={cn("flex min-h-[30vh] items-center justify-center", className)} role="status" aria-live="polite">
28
+ <div className="flex items-center gap-3 text-sm text-muted-foreground">
29
+ <span className="h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent" />
30
+ {label}
31
+ </div>
32
+ </div>
33
+ )
34
+ }
35
+
36
+ export function Empty({
37
+ title = "表示できるデータがありません",
38
+ description,
39
+ icon,
40
+ action,
41
+ className,
42
+ }: {
43
+ title?: string
44
+ description?: string
45
+ icon?: ReactNode
46
+ action?: ReactNode
47
+ className?: string
48
+ }) {
49
+ return (
50
+ <div className={cn("flex flex-col items-center justify-center gap-2 p-10 text-center", className)}>
51
+ {icon ? <div className="text-3xl text-muted-foreground">{icon}</div> : null}
52
+ <p className="font-medium">{title}</p>
53
+ {description ? <p className="max-w-sm text-sm text-muted-foreground">{description}</p> : null}
54
+ {action ? <div className="mt-2">{action}</div> : null}
55
+ </div>
56
+ )
57
+ }
58
+
59
+ export function ErrorState({
60
+ title = "データを読み込めませんでした",
61
+ message = "時間をおいて再試行してください。解決しない場合は管理者にお問い合わせください。",
62
+ details,
63
+ action,
64
+ className,
65
+ }: {
66
+ title?: string
67
+ /** What happened and what to do next, in plain words. */
68
+ message?: ReactNode
69
+ /** Technical detail (error message, status code), folded behind "詳細を表示". */
70
+ details?: ReactNode
71
+ action?: ReactNode
72
+ className?: string
73
+ }) {
74
+ return (
75
+ <div
76
+ className={cn("rounded-xl border border-destructive/30 bg-destructive/5 p-6", className)}
77
+ role="alert"
78
+ >
79
+ <p className="font-semibold text-destructive">{title}</p>
80
+ {message ? <p className="mt-1 text-sm text-muted-foreground">{message}</p> : null}
81
+ {details ? (
82
+ <details className="mt-2 text-xs text-muted-foreground">
83
+ <summary className="cursor-pointer select-none">詳細を表示</summary>
84
+ <div className="mt-1 font-mono break-all">{details}</div>
85
+ </details>
86
+ ) : null}
87
+ {action ? <div className="mt-3">{action}</div> : null}
88
+ </div>
89
+ )
90
+ }
@@ -0,0 +1,121 @@
1
+ // Copied by `vantage add ui status-badge`. This file is yours to edit.
2
+ // Generated from @squadbase/squadkit registry/components/status-badge.tsx by `npm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
+ "use client"
4
+
5
+ /**
6
+ * StatusBadge — a dot-plus-label badge for row/record status. Statuses map to a
7
+ * tone through `defaultColorMap`; pass `colorMap` to extend or override it.
8
+ *
9
+ * Tones are the theme's semantic tokens — `positive` | `negative` | `warning` |
10
+ * `info` | `neutral` — drawn as a `--<tone>` dot and text on a `--<tone>-subtle`
11
+ * background. The legacy colour names `green` / `red` / `yellow` / `blue` /
12
+ * `gray` are still accepted and map to positive / negative / warning / info /
13
+ * neutral. Unknown statuses and tones fall back to `neutral`.
14
+ *
15
+ * The default label comes from `STATUS_VOCABULARY` (one table of status, 日本語ラベル and tone), so the same
16
+ * state is worded the same everywhere (Tracker legends import it too):
17
+ *
18
+ * | status | ラベル | 色調 |
19
+ * |---|---|---|
20
+ * | `success` | 正常 | positive |
21
+ * | `partial` | 一部失敗 | warning |
22
+ * | `failed` | 失敗 | negative |
23
+ * | `delayed` | 遅延 | warning |
24
+ * | `running` | 実行中 | info |
25
+ * | `stopped` | 停止中 | neutral |
26
+ * | `active` / `inactive` | 有効 / 無効 | positive / neutral |
27
+ * | `warning` / `error` / `pending` | 警告 / エラー / 保留中 | warning / negative / info |
28
+ */
29
+ import { forwardRef } from "react"
30
+
31
+ import { cn } from "@squadbase/vantage/ui"
32
+ import { Badge } from "@squadbase/vantage/ui"
33
+
34
+ type StatusTone = "positive" | "negative" | "warning" | "info" | "neutral"
35
+
36
+ /**
37
+ * 状態の語彙の正本: status → 日本語ラベルと色調。StatusBadge の既定の表示と、Tracker の凡例など
38
+ * 同じ状態を語る他の場所が、同じ語を使うためにここから参照する。
39
+ */
40
+ export const STATUS_VOCABULARY = {
41
+ success: { label: "正常", tone: "positive" },
42
+ partial: { label: "一部失敗", tone: "warning" },
43
+ failed: { label: "失敗", tone: "negative" },
44
+ delayed: { label: "遅延", tone: "warning" },
45
+ running: { label: "実行中", tone: "info" },
46
+ stopped: { label: "停止中", tone: "neutral" },
47
+ active: { label: "有効", tone: "positive" },
48
+ inactive: { label: "無効", tone: "neutral" },
49
+ warning: { label: "警告", tone: "warning" },
50
+ error: { label: "エラー", tone: "negative" },
51
+ pending: { label: "保留中", tone: "info" },
52
+ } as const satisfies Record<string, { label: string; tone: StatusTone }>
53
+
54
+ export type StatusKey = keyof typeof STATUS_VOCABULARY
55
+
56
+ /** 状態の日本語ラベル。語彙に無い status は渡された文字列のまま返す。 */
57
+ export function statusLabel(status: string): string {
58
+ return Object.hasOwn(STATUS_VOCABULARY, status) ? STATUS_VOCABULARY[status as StatusKey].label : status
59
+ }
60
+
61
+ const defaultColorMap: Record<string, string> = Object.fromEntries(
62
+ Object.entries(STATUS_VOCABULARY).map(([key, { tone }]) => [key, tone]),
63
+ )
64
+
65
+ /** Legacy colour names → tones. */
66
+ const legacyColorTones: Record<string, StatusTone> = {
67
+ green: "positive",
68
+ gray: "neutral",
69
+ yellow: "warning",
70
+ red: "negative",
71
+ blue: "info",
72
+ }
73
+
74
+ /**
75
+ * One literal class set per tone — Tailwind only emits classes it can see
76
+ * literally in the source.
77
+ */
78
+ const toneStyles: Record<StatusTone, { dot: string; bg: string; text: string }> = {
79
+ positive: { dot: "bg-positive", bg: "bg-positive-subtle", text: "text-positive" },
80
+ negative: { dot: "bg-negative", bg: "bg-negative-subtle", text: "text-negative" },
81
+ warning: { dot: "bg-warning", bg: "bg-warning-subtle", text: "text-warning" },
82
+ info: { dot: "bg-info", bg: "bg-info-subtle", text: "text-info" },
83
+ neutral: { dot: "bg-neutral", bg: "bg-neutral-subtle", text: "text-neutral" },
84
+ }
85
+
86
+ const resolveTone = (color: string | undefined): StatusTone => {
87
+ if (color === undefined) return "neutral"
88
+ if (Object.hasOwn(toneStyles, color)) return color as StatusTone
89
+ return legacyColorTones[color] ?? "neutral"
90
+ }
91
+
92
+ export interface StatusBadgeProps {
93
+ status: string
94
+ label?: string
95
+ /** status → tone (`positive` | `negative` | `warning` | `info` | `neutral`) or legacy colour name. */
96
+ colorMap?: Partial<Record<string, string>>
97
+ className?: string
98
+ }
99
+
100
+ export const StatusBadge = forwardRef<HTMLSpanElement, StatusBadgeProps>(function StatusBadge(
101
+ { status, label, colorMap, className },
102
+ ref,
103
+ ) {
104
+ const mergedColorMap = { ...defaultColorMap, ...colorMap }
105
+ const style = toneStyles[resolveTone(mergedColorMap[status])]
106
+ const displayLabel = label ?? statusLabel(status)
107
+
108
+ return (
109
+ <Badge
110
+ ref={ref}
111
+ data-slot="status-badge"
112
+ variant="outline"
113
+ className={cn("gap-1.5 border-transparent", style.bg, style.text, className)}
114
+ aria-label={displayLabel}
115
+ >
116
+ <span className={cn("inline-block size-2 rounded-full", style.dot)} aria-hidden="true" />
117
+ {displayLabel}
118
+ </Badge>
119
+ )
120
+ })
121
+ StatusBadge.displayName = "StatusBadge"
@@ -0,0 +1,34 @@
1
+ // Copied by `vantage add ui switch`. This file is yours to edit.
2
+ // Generated from @squadbase/squadkit registry/ui/switch.tsx by `npm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
+ "use client"
4
+
5
+ import { Switch as SwitchPrimitive } from "@base-ui/react/switch"
6
+
7
+ import { cn } from "@squadbase/vantage/ui"
8
+
9
+ function Switch({
10
+ className,
11
+ size = "default",
12
+ ...props
13
+ }: SwitchPrimitive.Root.Props & {
14
+ size?: "sm" | "default"
15
+ }) {
16
+ return (
17
+ <SwitchPrimitive.Root
18
+ data-slot="switch"
19
+ data-size={size}
20
+ className={cn(
21
+ "peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50",
22
+ className
23
+ )}
24
+ {...props}
25
+ >
26
+ <SwitchPrimitive.Thumb
27
+ data-slot="switch-thumb"
28
+ className="pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] dark:data-checked:bg-primary-foreground group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground"
29
+ />
30
+ </SwitchPrimitive.Root>
31
+ )
32
+ }
33
+
34
+ export { Switch }
@@ -0,0 +1,118 @@
1
+ // Copied by `vantage add ui table`. This file is yours to edit.
2
+ // Generated from @squadbase/squadkit registry/ui/table.tsx by `npm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
+ "use client"
4
+
5
+ import * as React from "react"
6
+
7
+ import { cn } from "@squadbase/vantage/ui"
8
+
9
+ function Table({ className, ...props }: React.ComponentProps<"table">) {
10
+ return (
11
+ <div
12
+ data-slot="table-container"
13
+ className="relative w-full overflow-x-auto"
14
+ >
15
+ <table
16
+ data-slot="table"
17
+ className={cn("w-full caption-bottom text-sm", className)}
18
+ {...props}
19
+ />
20
+ </div>
21
+ )
22
+ }
23
+
24
+ function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
25
+ return (
26
+ <thead
27
+ data-slot="table-header"
28
+ className={cn("[&_tr]:border-b", className)}
29
+ {...props}
30
+ />
31
+ )
32
+ }
33
+
34
+ function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
35
+ return (
36
+ <tbody
37
+ data-slot="table-body"
38
+ className={cn("[&_tr:last-child]:border-0", className)}
39
+ {...props}
40
+ />
41
+ )
42
+ }
43
+
44
+ function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
45
+ return (
46
+ <tfoot
47
+ data-slot="table-footer"
48
+ className={cn(
49
+ "border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
50
+ className
51
+ )}
52
+ {...props}
53
+ />
54
+ )
55
+ }
56
+
57
+ function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
58
+ return (
59
+ <tr
60
+ data-slot="table-row"
61
+ className={cn(
62
+ "border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted",
63
+ className
64
+ )}
65
+ {...props}
66
+ />
67
+ )
68
+ }
69
+
70
+ function TableHead({ className, ...props }: React.ComponentProps<"th">) {
71
+ return (
72
+ <th
73
+ data-slot="table-head"
74
+ className={cn(
75
+ "h-10 px-2 text-left align-middle text-xs font-medium whitespace-nowrap text-muted-foreground [&:has([role=checkbox])]:pr-0",
76
+ className
77
+ )}
78
+ {...props}
79
+ />
80
+ )
81
+ }
82
+
83
+ function TableCell({ className, ...props }: React.ComponentProps<"td">) {
84
+ return (
85
+ <td
86
+ data-slot="table-cell"
87
+ className={cn(
88
+ "p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0",
89
+ className
90
+ )}
91
+ {...props}
92
+ />
93
+ )
94
+ }
95
+
96
+ function TableCaption({
97
+ className,
98
+ ...props
99
+ }: React.ComponentProps<"caption">) {
100
+ return (
101
+ <caption
102
+ data-slot="table-caption"
103
+ className={cn("mt-4 text-sm text-muted-foreground", className)}
104
+ {...props}
105
+ />
106
+ )
107
+ }
108
+
109
+ export {
110
+ Table,
111
+ TableHeader,
112
+ TableBody,
113
+ TableFooter,
114
+ TableHead,
115
+ TableRow,
116
+ TableCell,
117
+ TableCaption,
118
+ }