@squadbase/vantage 0.6.2-dev1.9d91dda → 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-TVKFIVFQ.js → build-MFWRPYKW.js} +7 -7
- package/dist/{build-TVKFIVFQ.js.map → build-MFWRPYKW.js.map} +1 -1
- package/dist/{check-3KHZXZMQ.js → check-ZBIH422T.js} +5 -5
- package/dist/{check-3KHZXZMQ.js.map → check-ZBIH422T.js.map} +1 -1
- package/dist/{chunk-P7W4LR3Z.js → chunk-7DZGJQ3P.js} +3 -3
- package/dist/{chunk-P7W4LR3Z.js.map → chunk-7DZGJQ3P.js.map} +1 -1
- 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-NKFBFDRZ.js → chunk-AG6RZ2RR.js} +981 -14
- package/dist/chunk-AG6RZ2RR.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-RPZAALCX.js → chunk-OB6UBJXF.js} +153 -153
- package/dist/{chunk-RPZAALCX.js.map → chunk-OB6UBJXF.js.map} +1 -1
- package/dist/{chunk-LMUACBMA.js → chunk-QEWCYW3I.js} +43 -8
- 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-GDKATCC7.js → dev-DCTOHQ6W.js} +8 -8
- 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-WQXPXP6K.js → doctor-5E2VEZPK.js} +5 -5
- package/dist/{doctor-WQXPXP6K.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-MWBWWOJ2.js → routes-3SGAFDMC.js} +4 -4
- package/dist/{routes-MWBWWOJ2.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/table/index.js +7 -6
- 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-HCANX4GQ.js → upgrade-DBBE6GIR.js} +6 -6
- package/dist/{upgrade-HCANX4GQ.js.map → upgrade-DBBE6GIR.js.map} +1 -1
- package/dist/vite/index.d.ts +10 -0
- package/dist/vite/index.js +3 -3
- 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 +21 -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-pitfalls/SKILL.md +5 -1
- package/templates/AGENTS.md +10 -1
- package/templates/agents/csv.md +2 -1
- package/templates/agents/doc.md +4 -3
- package/templates/agents/page.md +22 -8
- package/templates/agents/xlsx.md +2 -1
- package/dist/add-DY4AXANK.js +0 -6
- package/dist/add-DY4AXANK.js.map +0 -1
- package/dist/chunk-DTDVSFRY.js.map +0 -1
- package/dist/chunk-LMUACBMA.js.map +0 -1
- package/dist/chunk-NKFBFDRZ.js.map +0 -1
- package/dist/chunk-PVTHHV7X.js.map +0 -1
- package/dist/chunk-QI3XQKHS.js.map +0 -1
- package/dist/chunk-VA776YE5.js +0 -316
- package/dist/chunk-VA776YE5.js.map +0 -1
- package/dist/dev-GDKATCC7.js.map +0 -1
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
// Copied by `vantage add ui toast`. This file is yours to edit.
|
|
2
|
+
// Generated from @squadbase/vantage src/ui/toast.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
|
|
3
|
+
"use client"
|
|
4
|
+
|
|
5
|
+
import * as React from "react"
|
|
6
|
+
import { Toast as ToastPrimitive } from "@base-ui/react/toast"
|
|
7
|
+
import { cn } from "@squadbase/vantage/ui"
|
|
8
|
+
|
|
9
|
+
import { Button } from "@squadbase/vantage/ui"
|
|
10
|
+
import { CircleCheckIcon, InfoIcon, Loader2Icon, OctagonXIcon, TriangleAlertIcon, XIcon } from "lucide-react"
|
|
11
|
+
|
|
12
|
+
const toast = ToastPrimitive.createToastManager()
|
|
13
|
+
|
|
14
|
+
function ToastProvider({ ...props }: ToastPrimitive.Provider.Props) {
|
|
15
|
+
return <ToastPrimitive.Provider {...props} />
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function ToastPortal({ ...props }: ToastPrimitive.Portal.Props) {
|
|
19
|
+
return <ToastPrimitive.Portal data-slot="toast-portal" {...props} />
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function ToastViewport({ className, ...props }: ToastPrimitive.Viewport.Props) {
|
|
23
|
+
return (
|
|
24
|
+
<ToastPrimitive.Viewport
|
|
25
|
+
data-slot="toast-viewport"
|
|
26
|
+
className={cn(
|
|
27
|
+
"pointer-events-none fixed inset-x-4 bottom-4 z-50 mx-auto w-auto max-w-sm outline-none sm:right-4 sm:left-auto sm:mx-0 sm:w-full",
|
|
28
|
+
className
|
|
29
|
+
)}
|
|
30
|
+
{...props}
|
|
31
|
+
/>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function Toast({ className, ...props }: ToastPrimitive.Root.Props) {
|
|
36
|
+
return (
|
|
37
|
+
<ToastPrimitive.Root
|
|
38
|
+
data-slot="toast"
|
|
39
|
+
className={cn(
|
|
40
|
+
"group/toast pointer-events-auto absolute right-0 bottom-0 z-[calc(1000-var(--toast-index))] w-full origin-bottom rounded-2xl border bg-popover text-popover-foreground shadow-lg will-change-transform outline-none select-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
|
|
41
|
+
"[--gap:0.75rem] [--height:var(--toast-frontmost-height,var(--toast-height))] [--offset-y:calc(var(--toast-offset-y)*-1+calc(var(--toast-index)*var(--gap)*-1)+var(--toast-swipe-movement-y))] [--peek:0.75rem] [--scale:calc(max(0,1-(var(--toast-index)*0.1)))] [--shrink:calc(1-var(--scale))]",
|
|
42
|
+
"h-(--height) [transform:translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--toast-swipe-movement-y)-(var(--toast-index)*var(--peek))-(var(--shrink)*var(--height))))_scale(var(--scale))] [transition:transform_500ms_cubic-bezier(0.22,1,0.36,1),opacity_500ms,height_150ms]",
|
|
43
|
+
"after:absolute after:top-full after:left-0 after:h-[calc(var(--gap)+1px)] after:w-full after:content-['']",
|
|
44
|
+
"data-expanded:h-(--toast-height) data-expanded:[transform:translateX(var(--toast-swipe-movement-x))_translateY(var(--offset-y))]",
|
|
45
|
+
"data-limited:opacity-0 data-starting-style:[transform:translateY(150%)]",
|
|
46
|
+
"[&[data-ending-style]:not([data-limited]):not([data-swipe-direction])]:[transform:translateY(150%)]",
|
|
47
|
+
"data-ending-style:data-[swipe-direction=down]:[transform:translateY(calc(var(--toast-swipe-movement-y)+150%))]",
|
|
48
|
+
"data-ending-style:data-[swipe-direction=left]:[transform:translateX(calc(var(--toast-swipe-movement-x)-150%))_translateY(var(--offset-y))]",
|
|
49
|
+
"data-ending-style:data-[swipe-direction=right]:[transform:translateX(calc(var(--toast-swipe-movement-x)+150%))_translateY(var(--offset-y))]",
|
|
50
|
+
"data-ending-style:data-[swipe-direction=up]:[transform:translateY(calc(var(--toast-swipe-movement-y)-150%))]",
|
|
51
|
+
"data-expanded:data-ending-style:data-[swipe-direction=down]:[transform:translateY(calc(var(--toast-swipe-movement-y)+150%))]",
|
|
52
|
+
"data-expanded:data-ending-style:data-[swipe-direction=left]:[transform:translateX(calc(var(--toast-swipe-movement-x)-150%))_translateY(var(--offset-y))]",
|
|
53
|
+
"data-expanded:data-ending-style:data-[swipe-direction=right]:[transform:translateX(calc(var(--toast-swipe-movement-x)+150%))_translateY(var(--offset-y))]",
|
|
54
|
+
"data-expanded:data-ending-style:data-[swipe-direction=up]:[transform:translateY(calc(var(--toast-swipe-movement-y)-150%))]",
|
|
55
|
+
className
|
|
56
|
+
)}
|
|
57
|
+
{...props}
|
|
58
|
+
/>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function ToastContent({ className, ...props }: ToastPrimitive.Content.Props) {
|
|
63
|
+
return (
|
|
64
|
+
<ToastPrimitive.Content
|
|
65
|
+
data-slot="toast-content"
|
|
66
|
+
className={cn(
|
|
67
|
+
"flex h-full items-center gap-3 overflow-hidden p-4 transition-opacity duration-250 ease-[cubic-bezier(0.22,1,0.36,1)] data-behind:opacity-0 data-expanded:opacity-100",
|
|
68
|
+
className
|
|
69
|
+
)}
|
|
70
|
+
{...props}
|
|
71
|
+
/>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function ToastTitle({ className, ...props }: ToastPrimitive.Title.Props) {
|
|
76
|
+
return (
|
|
77
|
+
<ToastPrimitive.Title
|
|
78
|
+
data-slot="toast-title"
|
|
79
|
+
className={cn("text-sm font-medium", className)}
|
|
80
|
+
{...props}
|
|
81
|
+
/>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function ToastDescription({
|
|
86
|
+
className,
|
|
87
|
+
...props
|
|
88
|
+
}: ToastPrimitive.Description.Props) {
|
|
89
|
+
return (
|
|
90
|
+
<ToastPrimitive.Description
|
|
91
|
+
data-slot="toast-description"
|
|
92
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
93
|
+
{...props}
|
|
94
|
+
/>
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function ToastAction({
|
|
99
|
+
className,
|
|
100
|
+
render = <Button variant="outline" size="sm" />,
|
|
101
|
+
...props
|
|
102
|
+
}: ToastPrimitive.Action.Props) {
|
|
103
|
+
return (
|
|
104
|
+
<ToastPrimitive.Action
|
|
105
|
+
data-slot="toast-action"
|
|
106
|
+
render={render}
|
|
107
|
+
className={cn("shrink-0", className)}
|
|
108
|
+
{...props}
|
|
109
|
+
/>
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function ToastClose({
|
|
114
|
+
className,
|
|
115
|
+
children,
|
|
116
|
+
render = <Button variant="ghost" size="icon-sm" />,
|
|
117
|
+
...props
|
|
118
|
+
}: ToastPrimitive.Close.Props) {
|
|
119
|
+
return (
|
|
120
|
+
<ToastPrimitive.Close
|
|
121
|
+
data-slot="toast-close"
|
|
122
|
+
aria-label="Close toast"
|
|
123
|
+
render={render}
|
|
124
|
+
className={cn(
|
|
125
|
+
"relative shrink-0 text-muted-foreground after:absolute after:-inset-2 after:content-[''] hover:text-foreground",
|
|
126
|
+
className
|
|
127
|
+
)}
|
|
128
|
+
{...props}
|
|
129
|
+
>
|
|
130
|
+
{children ?? (
|
|
131
|
+
<XIcon
|
|
132
|
+
aria-hidden="true"
|
|
133
|
+
/>
|
|
134
|
+
)}
|
|
135
|
+
</ToastPrimitive.Close>
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function ToastIcon({ type }: { type: string | undefined }) {
|
|
140
|
+
let icon: React.ReactNode = null
|
|
141
|
+
|
|
142
|
+
if (type === "success") {
|
|
143
|
+
icon = (
|
|
144
|
+
<CircleCheckIcon
|
|
145
|
+
aria-hidden="true"
|
|
146
|
+
/>
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (type === "info") {
|
|
151
|
+
icon = (
|
|
152
|
+
<InfoIcon
|
|
153
|
+
aria-hidden="true"
|
|
154
|
+
/>
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (type === "warning") {
|
|
159
|
+
icon = (
|
|
160
|
+
<TriangleAlertIcon
|
|
161
|
+
aria-hidden="true"
|
|
162
|
+
/>
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (type === "error") {
|
|
167
|
+
icon = (
|
|
168
|
+
<OctagonXIcon
|
|
169
|
+
className="text-destructive"
|
|
170
|
+
aria-hidden="true"
|
|
171
|
+
/>
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (type === "loading") {
|
|
176
|
+
icon = (
|
|
177
|
+
<Loader2Icon
|
|
178
|
+
className="animate-spin"
|
|
179
|
+
aria-hidden="true"
|
|
180
|
+
/>
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (!icon) {
|
|
185
|
+
return null
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
<span
|
|
190
|
+
data-slot="toast-icon"
|
|
191
|
+
className="shrink-0 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4"
|
|
192
|
+
>
|
|
193
|
+
{icon}
|
|
194
|
+
</span>
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function ToastList() {
|
|
199
|
+
const { toasts } = ToastPrimitive.useToastManager()
|
|
200
|
+
|
|
201
|
+
return toasts.map((toastItem) => (
|
|
202
|
+
<Toast key={toastItem.id} toast={toastItem}>
|
|
203
|
+
<ToastContent>
|
|
204
|
+
<ToastIcon type={toastItem.type} />
|
|
205
|
+
<div className="flex min-w-0 flex-1 flex-col gap-1">
|
|
206
|
+
<ToastTitle />
|
|
207
|
+
<ToastDescription />
|
|
208
|
+
</div>
|
|
209
|
+
<ToastAction />
|
|
210
|
+
<ToastClose />
|
|
211
|
+
</ToastContent>
|
|
212
|
+
</Toast>
|
|
213
|
+
))
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function Toaster({
|
|
217
|
+
children,
|
|
218
|
+
toastManager = toast,
|
|
219
|
+
...props
|
|
220
|
+
}: ToastPrimitive.Provider.Props) {
|
|
221
|
+
return (
|
|
222
|
+
<ToastProvider toastManager={toastManager} {...props}>
|
|
223
|
+
{children}
|
|
224
|
+
<ToastPortal>
|
|
225
|
+
<ToastViewport>
|
|
226
|
+
<ToastList />
|
|
227
|
+
</ToastViewport>
|
|
228
|
+
</ToastPortal>
|
|
229
|
+
</ToastProvider>
|
|
230
|
+
)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const createToastManager = ToastPrimitive.createToastManager
|
|
234
|
+
const useToastManager = ToastPrimitive.useToastManager
|
|
235
|
+
|
|
236
|
+
export {
|
|
237
|
+
Toaster,
|
|
238
|
+
Toast,
|
|
239
|
+
ToastAction,
|
|
240
|
+
ToastClose,
|
|
241
|
+
ToastContent,
|
|
242
|
+
ToastDescription,
|
|
243
|
+
ToastPortal,
|
|
244
|
+
ToastProvider,
|
|
245
|
+
ToastTitle,
|
|
246
|
+
ToastViewport,
|
|
247
|
+
createToastManager,
|
|
248
|
+
toast,
|
|
249
|
+
useToastManager,
|
|
250
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// Copied by `vantage add ui toggle-group`. This file is yours to edit.
|
|
2
|
+
// Generated from @squadbase/vantage src/ui/toggle-group.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Toggle as TogglePrimitive } from "@base-ui/react/toggle"
|
|
5
|
+
import { ToggleGroup as ToggleGroupPrimitive } from "@base-ui/react/toggle-group"
|
|
6
|
+
import { type VariantProps } from "class-variance-authority"
|
|
7
|
+
|
|
8
|
+
import { cn } from "@squadbase/vantage/ui"
|
|
9
|
+
import { toggleVariants } from "@squadbase/vantage/ui"
|
|
10
|
+
|
|
11
|
+
const ToggleGroupContext = React.createContext<
|
|
12
|
+
VariantProps<typeof toggleVariants> & {
|
|
13
|
+
spacing?: number
|
|
14
|
+
orientation?: "horizontal" | "vertical"
|
|
15
|
+
}
|
|
16
|
+
>({
|
|
17
|
+
size: "default",
|
|
18
|
+
variant: "default",
|
|
19
|
+
spacing: 2,
|
|
20
|
+
orientation: "horizontal",
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
function ToggleGroup({
|
|
24
|
+
className,
|
|
25
|
+
variant,
|
|
26
|
+
size,
|
|
27
|
+
spacing = 2,
|
|
28
|
+
orientation = "horizontal",
|
|
29
|
+
children,
|
|
30
|
+
...props
|
|
31
|
+
}: ToggleGroupPrimitive.Props &
|
|
32
|
+
VariantProps<typeof toggleVariants> & {
|
|
33
|
+
spacing?: number
|
|
34
|
+
orientation?: "horizontal" | "vertical"
|
|
35
|
+
}) {
|
|
36
|
+
return (
|
|
37
|
+
<ToggleGroupPrimitive
|
|
38
|
+
data-slot="toggle-group"
|
|
39
|
+
data-variant={variant}
|
|
40
|
+
data-size={size}
|
|
41
|
+
data-spacing={spacing}
|
|
42
|
+
data-orientation={orientation}
|
|
43
|
+
style={{ "--gap": spacing } as React.CSSProperties}
|
|
44
|
+
className={cn(
|
|
45
|
+
"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-lg data-[size=sm]:rounded-[min(var(--radius-md),10px)] data-vertical:flex-col data-vertical:items-stretch",
|
|
46
|
+
className
|
|
47
|
+
)}
|
|
48
|
+
{...props}
|
|
49
|
+
>
|
|
50
|
+
<ToggleGroupContext.Provider
|
|
51
|
+
value={{ variant, size, spacing, orientation }}
|
|
52
|
+
>
|
|
53
|
+
{children}
|
|
54
|
+
</ToggleGroupContext.Provider>
|
|
55
|
+
</ToggleGroupPrimitive>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function ToggleGroupItem({
|
|
60
|
+
className,
|
|
61
|
+
children,
|
|
62
|
+
variant = "default",
|
|
63
|
+
size = "default",
|
|
64
|
+
...props
|
|
65
|
+
}: TogglePrimitive.Props & VariantProps<typeof toggleVariants>) {
|
|
66
|
+
const context = React.useContext(ToggleGroupContext)
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<TogglePrimitive
|
|
70
|
+
data-slot="toggle-group-item"
|
|
71
|
+
data-variant={context.variant || variant}
|
|
72
|
+
data-size={context.size || size}
|
|
73
|
+
data-spacing={context.spacing}
|
|
74
|
+
className={cn(
|
|
75
|
+
"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-lg group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-lg group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-lg group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-lg group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t",
|
|
76
|
+
toggleVariants({
|
|
77
|
+
variant: context.variant || variant,
|
|
78
|
+
size: context.size || size,
|
|
79
|
+
}),
|
|
80
|
+
className
|
|
81
|
+
)}
|
|
82
|
+
{...props}
|
|
83
|
+
>
|
|
84
|
+
{children}
|
|
85
|
+
</TogglePrimitive>
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export { ToggleGroup, ToggleGroupItem }
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Copied by `vantage add ui toggle`. This file is yours to edit.
|
|
2
|
+
// Generated from @squadbase/vantage src/ui/toggle.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
|
|
3
|
+
import { Toggle as TogglePrimitive } from "@base-ui/react/toggle"
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
5
|
+
|
|
6
|
+
import { cn } from "@squadbase/vantage/ui"
|
|
7
|
+
|
|
8
|
+
const toggleVariants = cva(
|
|
9
|
+
"group/toggle inline-flex items-center justify-center gap-1 rounded-lg text-sm font-medium whitespace-nowrap transition-all outline-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-pressed:bg-muted data-[state=on]:bg-muted dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
10
|
+
{
|
|
11
|
+
variants: {
|
|
12
|
+
variant: {
|
|
13
|
+
default: "bg-transparent",
|
|
14
|
+
outline: "border border-input bg-transparent hover:bg-muted",
|
|
15
|
+
},
|
|
16
|
+
size: {
|
|
17
|
+
default:
|
|
18
|
+
"h-8 min-w-8 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
19
|
+
sm: "h-7 min-w-7 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
|
|
20
|
+
lg: "h-9 min-w-9 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
defaultVariants: {
|
|
24
|
+
variant: "default",
|
|
25
|
+
size: "default",
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
function Toggle({
|
|
31
|
+
className,
|
|
32
|
+
variant = "default",
|
|
33
|
+
size = "default",
|
|
34
|
+
...props
|
|
35
|
+
}: TogglePrimitive.Props & VariantProps<typeof toggleVariants>) {
|
|
36
|
+
return (
|
|
37
|
+
<TogglePrimitive
|
|
38
|
+
data-slot="toggle"
|
|
39
|
+
className={cn(toggleVariants({ variant, size, className }))}
|
|
40
|
+
{...props}
|
|
41
|
+
/>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { Toggle, toggleVariants }
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Copied by `vantage add ui tooltip`. This file is yours to edit.
|
|
2
|
+
// Generated from @squadbase/vantage src/ui/tooltip.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
|
|
3
|
+
import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@squadbase/vantage/ui"
|
|
6
|
+
|
|
7
|
+
function TooltipProvider({
|
|
8
|
+
delay = 0,
|
|
9
|
+
...props
|
|
10
|
+
}: TooltipPrimitive.Provider.Props) {
|
|
11
|
+
return (
|
|
12
|
+
<TooltipPrimitive.Provider
|
|
13
|
+
data-slot="tooltip-provider"
|
|
14
|
+
delay={delay}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function Tooltip({ ...props }: TooltipPrimitive.Root.Props) {
|
|
21
|
+
return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function TooltipTrigger({ ...props }: TooltipPrimitive.Trigger.Props) {
|
|
25
|
+
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function TooltipContent({
|
|
29
|
+
className,
|
|
30
|
+
side = "top",
|
|
31
|
+
sideOffset = 4,
|
|
32
|
+
align = "center",
|
|
33
|
+
alignOffset = 0,
|
|
34
|
+
children,
|
|
35
|
+
...props
|
|
36
|
+
}: TooltipPrimitive.Popup.Props &
|
|
37
|
+
Pick<
|
|
38
|
+
TooltipPrimitive.Positioner.Props,
|
|
39
|
+
"align" | "alignOffset" | "side" | "sideOffset"
|
|
40
|
+
>) {
|
|
41
|
+
return (
|
|
42
|
+
<TooltipPrimitive.Portal>
|
|
43
|
+
<TooltipPrimitive.Positioner
|
|
44
|
+
align={align}
|
|
45
|
+
alignOffset={alignOffset}
|
|
46
|
+
side={side}
|
|
47
|
+
sideOffset={sideOffset}
|
|
48
|
+
className="isolate z-50"
|
|
49
|
+
>
|
|
50
|
+
<TooltipPrimitive.Popup
|
|
51
|
+
data-slot="tooltip-content"
|
|
52
|
+
className={cn(
|
|
53
|
+
"z-50 inline-flex w-fit max-w-xs origin-(--transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 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-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 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",
|
|
54
|
+
className
|
|
55
|
+
)}
|
|
56
|
+
{...props}
|
|
57
|
+
>
|
|
58
|
+
{children}
|
|
59
|
+
<TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground data-[side=bottom]:top-1 data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1 data-[side=inline-start]:-translate-y-1/2 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5" />
|
|
60
|
+
</TooltipPrimitive.Popup>
|
|
61
|
+
</TooltipPrimitive.Positioner>
|
|
62
|
+
</TooltipPrimitive.Portal>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
// Copied by `vantage add ui tracker`. This file is yours to edit.
|
|
2
|
+
// Generated from @squadbase/vantage src/components/tracker.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
|
|
3
|
+
/**
|
|
4
|
+
* Tracker — a row of small blocks, one per period, each coloured by status:
|
|
5
|
+
* uptime per day, batch-job results, daily target hit/miss. Hover a block to
|
|
6
|
+
* see its tooltip. Plain HTML/CSS.
|
|
7
|
+
*/
|
|
8
|
+
import { forwardRef, type ComponentProps } from "react"
|
|
9
|
+
|
|
10
|
+
import { cn } from "@squadbase/vantage/ui"
|
|
11
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "@squadbase/vantage/ui"
|
|
12
|
+
import type { ChartColor } from "@squadbase/vantage/components"
|
|
13
|
+
import type { ValueFormatter } from "@squadbase/vantage/components"
|
|
14
|
+
|
|
15
|
+
/* ---------- inlined from src/components/chart-utils.ts (internal to @squadbase/vantage) ---------- */
|
|
16
|
+
|
|
17
|
+
/** The series tokens, in palette order — the default `colors` of every typed chart. */
|
|
18
|
+
const DEFAULT_CHART_COLORS: readonly ChartColor[] = [
|
|
19
|
+
"chart-1",
|
|
20
|
+
"chart-2",
|
|
21
|
+
"chart-3",
|
|
22
|
+
"chart-4",
|
|
23
|
+
"chart-5",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
const TOKEN_NAME_RE = /^chart-[1-5]$/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* A chart colour as a CSS value: `"chart-2"` → `var(--chart-2)` (so it follows
|
|
30
|
+
* light/dark on its own), anything else passes through unchanged.
|
|
31
|
+
*/
|
|
32
|
+
function chartColorToCss(color: ChartColor): string {
|
|
33
|
+
const value = color.trim()
|
|
34
|
+
return TOKEN_NAME_RE.test(value) ? `var(--${value})` : value
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** The `i`-th colour, cycling through `colors` (the token palette when empty). */
|
|
38
|
+
function chartColorAt(colors: readonly ChartColor[] | undefined, i: number): ChartColor {
|
|
39
|
+
const palette = colors && colors.length > 0 ? colors : DEFAULT_CHART_COLORS
|
|
40
|
+
return palette[i % palette.length]!
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** `colors` extended (by cycling) to exactly `count` entries. */
|
|
44
|
+
function chartColorsFor(colors: readonly ChartColor[] | undefined, count: number): ChartColor[] {
|
|
45
|
+
return Array.from({ length: count }, (_, i) => chartColorAt(colors, i))
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** A translucent version of a CSS colour (works for `var(--…)` too). */
|
|
49
|
+
function tintCss(cssColor: string, percent: number): string {
|
|
50
|
+
return `color-mix(in oklab, ${cssColor} ${percent}%, transparent)`
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const numberFormat = new Intl.NumberFormat("ja-JP", { maximumFractionDigits: 2 })
|
|
54
|
+
|
|
55
|
+
/** Default `valueFormatter`: grouped digits, up to two decimals (`1,234.5`). */
|
|
56
|
+
const defaultValueFormatter: ValueFormatter = (value) => numberFormat.format(value)
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* A data cell as a number, or `null` for a gap. Numeric strings count — SQL
|
|
60
|
+
* `DECIMAL` / `BIGINT` columns usually arrive as strings.
|
|
61
|
+
*/
|
|
62
|
+
function toChartNumber(value: unknown): number | null {
|
|
63
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null
|
|
64
|
+
if (typeof value === "bigint") return Number(value)
|
|
65
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
66
|
+
const n = Number(value)
|
|
67
|
+
return Number.isFinite(n) ? n : null
|
|
68
|
+
}
|
|
69
|
+
return null
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Clamp `value` into `[min, max]`. */
|
|
73
|
+
function clamp(value: number, min: number, max: number): number {
|
|
74
|
+
return Math.min(max, Math.max(min, value))
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* ---------------------------------------------------------------- BarList */
|
|
78
|
+
|
|
79
|
+
type BarListSortOrder = "ascending" | "descending" | "none"
|
|
80
|
+
|
|
81
|
+
/** Sort a copy of the rows by value; `"none"` keeps the given order. */
|
|
82
|
+
function sortBarListData<T extends { value: number }>(data: readonly T[], sortOrder: BarListSortOrder): T[] {
|
|
83
|
+
const rows = [...data]
|
|
84
|
+
if (sortOrder === "ascending") rows.sort((a, b) => a.value - b.value)
|
|
85
|
+
else if (sortOrder === "descending") rows.sort((a, b) => b.value - a.value)
|
|
86
|
+
return rows
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Bar widths in percent of the largest value (at least a sliver for non-zero values). */
|
|
90
|
+
function barListWidths(values: readonly number[]): number[] {
|
|
91
|
+
const max = Math.max(0, ...values)
|
|
92
|
+
if (max === 0) return values.map(() => 0)
|
|
93
|
+
return values.map((v) => (v <= 0 ? 0 : Math.max(1, (v / max) * 100)))
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* ------------------------------------------------------------ CategoryBar */
|
|
97
|
+
|
|
98
|
+
interface CategoryBarSegment {
|
|
99
|
+
/** Width in percent of the whole bar. */
|
|
100
|
+
width: number
|
|
101
|
+
/** Where the segment starts, in percent. */
|
|
102
|
+
start: number
|
|
103
|
+
/** The running total at the segment's end — what its right-hand label shows. */
|
|
104
|
+
end: number
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Lay the values side by side; negative values count as zero. */
|
|
108
|
+
function categoryBarSegments(values: readonly number[]): CategoryBarSegment[] {
|
|
109
|
+
const clean = values.map((v) => (Number.isFinite(v) && v > 0 ? v : 0))
|
|
110
|
+
const total = clean.reduce((sum, v) => sum + v, 0)
|
|
111
|
+
let running = 0
|
|
112
|
+
return clean.map((v) => {
|
|
113
|
+
const start = total === 0 ? 0 : (running / total) * 100
|
|
114
|
+
running += v
|
|
115
|
+
return { width: total === 0 ? 0 : (v / total) * 100, start, end: running }
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
interface CategoryBarLabel {
|
|
120
|
+
/** Position in percent of the bar. */
|
|
121
|
+
position: number
|
|
122
|
+
/** The running total printed there. */
|
|
123
|
+
value: number
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Tick labels under the bar: `0` at the start, then the running total at each
|
|
128
|
+
* segment's end. A label closer than `minGap` percent to the one before is
|
|
129
|
+
* dropped so narrow segments don't print on top of each other — except the
|
|
130
|
+
* last one (the total), which replaces its neighbour instead.
|
|
131
|
+
*/
|
|
132
|
+
function categoryBarLabels(values: readonly number[], minGap = 8): CategoryBarLabel[] {
|
|
133
|
+
const segments = categoryBarSegments(values).filter((s) => s.width > 0)
|
|
134
|
+
if (segments.length === 0) return []
|
|
135
|
+
const labels: CategoryBarLabel[] = [{ position: 0, value: 0 }]
|
|
136
|
+
segments.forEach((s, i) => {
|
|
137
|
+
const label = { position: s.start + s.width, value: s.end }
|
|
138
|
+
const prev = labels[labels.length - 1]!
|
|
139
|
+
if (label.position - prev.position >= minGap) labels.push(label)
|
|
140
|
+
else if (i === segments.length - 1 && labels.length > 1) labels[labels.length - 1] = label
|
|
141
|
+
})
|
|
142
|
+
return labels
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Marker position in percent of the bar, clamped to its ends. */
|
|
146
|
+
function categoryBarMarkerPosition(value: number, values: readonly number[]): number {
|
|
147
|
+
const total = values.reduce((sum, v) => sum + (Number.isFinite(v) && v > 0 ? v : 0), 0)
|
|
148
|
+
if (total === 0) return 0
|
|
149
|
+
return clamp((value / total) * 100, 0, 100)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Index of the segment the marker sits on (the last one for a value past the end). */
|
|
153
|
+
function categoryBarMarkerSegment(value: number, values: readonly number[]): number {
|
|
154
|
+
const segments = categoryBarSegments(values)
|
|
155
|
+
const index = segments.findIndex((s) => s.width > 0 && value <= s.end)
|
|
156
|
+
if (index !== -1) return index
|
|
157
|
+
for (let i = segments.length - 1; i >= 0; i--) if (segments[i]!.width > 0) return i
|
|
158
|
+
return 0
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/* --------------------------------------------------------- ProgressCircle */
|
|
162
|
+
|
|
163
|
+
interface ProgressCircleGeometry {
|
|
164
|
+
/** SVG width/height. */
|
|
165
|
+
size: number
|
|
166
|
+
/** Radius of the stroke's centre line. */
|
|
167
|
+
normalizedRadius: number
|
|
168
|
+
circumference: number
|
|
169
|
+
/** `stroke-dashoffset` for the value arc. */
|
|
170
|
+
offset: number
|
|
171
|
+
/** The value as a fraction of `max`, clamped to `[0, 1]`. */
|
|
172
|
+
ratio: number
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function progressCircleGeometry(
|
|
176
|
+
value: number,
|
|
177
|
+
max: number,
|
|
178
|
+
radius: number,
|
|
179
|
+
strokeWidth: number,
|
|
180
|
+
): ProgressCircleGeometry {
|
|
181
|
+
const ratio = max > 0 && Number.isFinite(value) ? clamp(value / max, 0, 1) : 0
|
|
182
|
+
const normalizedRadius = Math.max(0, radius - strokeWidth / 2)
|
|
183
|
+
const circumference = 2 * Math.PI * normalizedRadius
|
|
184
|
+
return { size: radius * 2, normalizedRadius, circumference, offset: circumference * (1 - ratio), ratio }
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* ---------- end of inlined code ---------- */
|
|
188
|
+
|
|
189
|
+
export interface TrackerBlock {
|
|
190
|
+
/** React の key。省略時は位置。 */
|
|
191
|
+
key?: string | number
|
|
192
|
+
/** ブロックの色。`"chart-1"`〜`"chart-5"` か CSS カラー(`"var(--destructive)"` など)。省略時は `defaultColor`。 */
|
|
193
|
+
color?: ChartColor
|
|
194
|
+
/** ホバーで出す説明(例: `"10/12 稼働率 99.2%"`)。 */
|
|
195
|
+
tooltip?: string
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export interface TrackerProps extends Omit<ComponentProps<"div">, "children"> {
|
|
199
|
+
data: TrackerBlock[]
|
|
200
|
+
/** `color` の無いブロックの色。既定は muted-foreground を淡くした灰色。 */
|
|
201
|
+
defaultColor?: ChartColor
|
|
202
|
+
/** ホバー中のブロックを薄くする。既定 `true`。 */
|
|
203
|
+
hoverEffect?: boolean
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const DEFAULT_BLOCK_COLOR = "color-mix(in oklab, var(--muted-foreground) 35%, transparent)"
|
|
207
|
+
|
|
208
|
+
export const Tracker = forwardRef<HTMLDivElement, TrackerProps>(function Tracker(
|
|
209
|
+
{ data, defaultColor, hoverEffect = true, className, ...props },
|
|
210
|
+
ref,
|
|
211
|
+
) {
|
|
212
|
+
const fallback = defaultColor ? chartColorToCss(defaultColor) : DEFAULT_BLOCK_COLOR
|
|
213
|
+
return (
|
|
214
|
+
<div ref={ref} data-slot="tracker" className={cn("flex h-8 w-full gap-px", className)} {...props}>
|
|
215
|
+
{data.map((block, i) => {
|
|
216
|
+
const blockClass = cn(
|
|
217
|
+
"h-full min-w-0 flex-1 rounded-[1px] first:rounded-l-[4px] last:rounded-r-[4px]",
|
|
218
|
+
hoverEffect && "transition-opacity hover:opacity-60",
|
|
219
|
+
)
|
|
220
|
+
const style = { background: block.color ? chartColorToCss(block.color) : fallback }
|
|
221
|
+
return block.tooltip ? (
|
|
222
|
+
<Tooltip key={block.key ?? i}>
|
|
223
|
+
<TooltipTrigger
|
|
224
|
+
delay={0}
|
|
225
|
+
render={<div data-slot="tracker-block" aria-label={block.tooltip} className={blockClass} style={style} />}
|
|
226
|
+
/>
|
|
227
|
+
<TooltipContent>{block.tooltip}</TooltipContent>
|
|
228
|
+
</Tooltip>
|
|
229
|
+
) : (
|
|
230
|
+
<div key={block.key ?? i} data-slot="tracker-block" className={blockClass} style={style} />
|
|
231
|
+
)
|
|
232
|
+
})}
|
|
233
|
+
</div>
|
|
234
|
+
)
|
|
235
|
+
})
|
|
236
|
+
Tracker.displayName = "Tracker"
|