@squadbase/vantage 0.6.2-dev1.3e69e88 → 0.6.2-dev1.42c4e48
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/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 +31 -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-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 +18 -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,274 @@
|
|
|
1
|
+
// Copied by `vantage add ui funnel-steps`. This file is yours to edit.
|
|
2
|
+
// Generated from @squadbase/vantage src/components/funnel-steps.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
|
|
3
|
+
/**
|
|
4
|
+
* FunnelSteps — proportional bars for a conversion funnel. Bars are sized
|
|
5
|
+
* against the first step, so pass the steps in descending order. Set
|
|
6
|
+
* `showConversion` to print the step-to-step rate between them.
|
|
7
|
+
*/
|
|
8
|
+
import { forwardRef, Fragment, useEffect, useState, type ComponentProps, type KeyboardEvent } from "react"
|
|
9
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
10
|
+
|
|
11
|
+
import { cn } from "@squadbase/vantage/ui"
|
|
12
|
+
|
|
13
|
+
export interface FunnelStep {
|
|
14
|
+
/** ステップのラベル(例: "ページビュー") */
|
|
15
|
+
label: string
|
|
16
|
+
/** このステップの数値 */
|
|
17
|
+
value: number
|
|
18
|
+
/** Tailwind bg カラークラス(例: "bg-chart-1")。未指定時は自動割り当て */
|
|
19
|
+
color?: string
|
|
20
|
+
/** クリック時のハンドラ */
|
|
21
|
+
onClick?: () => void
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const DEFAULT_COLORS = [
|
|
25
|
+
"bg-chart-1",
|
|
26
|
+
"bg-chart-2",
|
|
27
|
+
"bg-chart-3",
|
|
28
|
+
"bg-chart-4",
|
|
29
|
+
"bg-chart-5",
|
|
30
|
+
] as const
|
|
31
|
+
|
|
32
|
+
export const funnelStepsVariants = cva("flex w-full", {
|
|
33
|
+
variants: {
|
|
34
|
+
size: {
|
|
35
|
+
sm: "",
|
|
36
|
+
md: "",
|
|
37
|
+
lg: "",
|
|
38
|
+
},
|
|
39
|
+
direction: {
|
|
40
|
+
vertical: "flex-col",
|
|
41
|
+
horizontal: "flex-row",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
compoundVariants: [
|
|
45
|
+
{ direction: "vertical", size: "sm", className: "gap-1" },
|
|
46
|
+
{ direction: "vertical", size: "md", className: "gap-1.5" },
|
|
47
|
+
{ direction: "vertical", size: "lg", className: "gap-2" },
|
|
48
|
+
{ direction: "horizontal", size: "sm", className: "gap-2" },
|
|
49
|
+
{ direction: "horizontal", size: "md", className: "gap-3" },
|
|
50
|
+
{ direction: "horizontal", size: "lg", className: "gap-4" },
|
|
51
|
+
],
|
|
52
|
+
defaultVariants: {
|
|
53
|
+
size: "md",
|
|
54
|
+
direction: "vertical",
|
|
55
|
+
},
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
const funnelBarVariants = cva("rounded-sm transition-[width] ease-out", {
|
|
59
|
+
variants: {
|
|
60
|
+
size: {
|
|
61
|
+
sm: "h-6",
|
|
62
|
+
md: "h-8",
|
|
63
|
+
lg: "h-10",
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
defaultVariants: {
|
|
67
|
+
size: "md",
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
const funnelBarContainerHorizontalVariants = cva(
|
|
72
|
+
"relative flex w-full items-end overflow-hidden rounded-sm bg-muted",
|
|
73
|
+
{
|
|
74
|
+
variants: {
|
|
75
|
+
size: {
|
|
76
|
+
sm: "h-20",
|
|
77
|
+
md: "h-28",
|
|
78
|
+
lg: "h-36",
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
defaultVariants: {
|
|
82
|
+
size: "md",
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
const funnelLabelVariants = cva("flex w-full items-center justify-between text-muted-foreground", {
|
|
88
|
+
variants: {
|
|
89
|
+
size: {
|
|
90
|
+
sm: "text-xs",
|
|
91
|
+
md: "text-sm",
|
|
92
|
+
lg: "text-sm",
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
defaultVariants: {
|
|
96
|
+
size: "md",
|
|
97
|
+
},
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
const funnelConversionVariants = cva("flex items-center text-muted-foreground", {
|
|
101
|
+
variants: {
|
|
102
|
+
size: {
|
|
103
|
+
sm: "text-[10px]",
|
|
104
|
+
md: "text-xs",
|
|
105
|
+
lg: "text-xs",
|
|
106
|
+
},
|
|
107
|
+
direction: {
|
|
108
|
+
vertical: "justify-end",
|
|
109
|
+
horizontal: "shrink-0 justify-center self-center",
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
compoundVariants: [
|
|
113
|
+
{ direction: "vertical", size: "sm", className: "py-0" },
|
|
114
|
+
{ direction: "vertical", size: "md", className: "py-0.5" },
|
|
115
|
+
{ direction: "vertical", size: "lg", className: "py-0.5" },
|
|
116
|
+
],
|
|
117
|
+
defaultVariants: {
|
|
118
|
+
size: "md",
|
|
119
|
+
direction: "vertical",
|
|
120
|
+
},
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
export interface FunnelStepsProps
|
|
124
|
+
extends ComponentProps<"div">,
|
|
125
|
+
VariantProps<typeof funnelStepsVariants> {
|
|
126
|
+
/** ファネルステップの配列(先頭が最大幅) */
|
|
127
|
+
data: FunnelStep[]
|
|
128
|
+
/** ステップ間のコンバージョン率を表示する */
|
|
129
|
+
showConversion?: boolean
|
|
130
|
+
/** 値の表示フォーマット関数 */
|
|
131
|
+
formatValue?: (value: number) => string
|
|
132
|
+
/** マウント時のエントリアニメーションを有効にする */
|
|
133
|
+
animate?: boolean
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function defaultFormatValue(value: number): string {
|
|
137
|
+
return value.toLocaleString()
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function computeConversionRate(current: number, previous: number): string {
|
|
141
|
+
if (previous === 0) return "--"
|
|
142
|
+
return `${((current / previous) * 100).toFixed(1)}%`
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export const FunnelSteps = forwardRef<HTMLDivElement, FunnelStepsProps>(function FunnelSteps(
|
|
146
|
+
{
|
|
147
|
+
data,
|
|
148
|
+
showConversion = false,
|
|
149
|
+
formatValue = defaultFormatValue,
|
|
150
|
+
animate = false,
|
|
151
|
+
size = "md",
|
|
152
|
+
direction = "vertical",
|
|
153
|
+
className,
|
|
154
|
+
...props
|
|
155
|
+
},
|
|
156
|
+
ref,
|
|
157
|
+
) {
|
|
158
|
+
const [mounted, setMounted] = useState(false)
|
|
159
|
+
const isHorizontal = direction === "horizontal"
|
|
160
|
+
|
|
161
|
+
useEffect(() => {
|
|
162
|
+
if (animate) setMounted(true)
|
|
163
|
+
}, [animate])
|
|
164
|
+
|
|
165
|
+
const maxValue = data.length > 0 ? data[0]!.value : 0
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<div
|
|
169
|
+
ref={ref}
|
|
170
|
+
data-slot="funnel-steps"
|
|
171
|
+
role="list"
|
|
172
|
+
aria-label="ファネル"
|
|
173
|
+
className={cn(funnelStepsVariants({ size, direction }), className)}
|
|
174
|
+
{...props}
|
|
175
|
+
>
|
|
176
|
+
{data.map((step, index) => {
|
|
177
|
+
const percentage = maxValue > 0 ? (step.value / maxValue) * 100 : 0
|
|
178
|
+
// Keep a sliver visible for non-zero steps that would round to nothing.
|
|
179
|
+
const barSize = step.value > 0 && percentage < 2 ? 2 : percentage
|
|
180
|
+
const color = step.color ?? DEFAULT_COLORS[index % DEFAULT_COLORS.length]
|
|
181
|
+
const isClickable = !!step.onClick
|
|
182
|
+
|
|
183
|
+
const barInteractionProps = isClickable
|
|
184
|
+
? {
|
|
185
|
+
role: "button" as const,
|
|
186
|
+
tabIndex: 0,
|
|
187
|
+
onClick: step.onClick,
|
|
188
|
+
onKeyDown: (e: KeyboardEvent) => {
|
|
189
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
190
|
+
e.preventDefault()
|
|
191
|
+
step.onClick?.()
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
}
|
|
195
|
+
: {}
|
|
196
|
+
|
|
197
|
+
const barInteractionClasses = isClickable
|
|
198
|
+
? "cursor-pointer hover:brightness-110 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:outline-none"
|
|
199
|
+
: undefined
|
|
200
|
+
|
|
201
|
+
return (
|
|
202
|
+
<Fragment key={index}>
|
|
203
|
+
{isHorizontal ? (
|
|
204
|
+
<div
|
|
205
|
+
role="listitem"
|
|
206
|
+
className="flex min-w-0 flex-1 flex-col items-center gap-1"
|
|
207
|
+
aria-label={`${step.label}: ${formatValue(step.value)}`}
|
|
208
|
+
>
|
|
209
|
+
<span
|
|
210
|
+
data-slot="funnel-value"
|
|
211
|
+
className={cn(
|
|
212
|
+
"text-muted-foreground tabular-nums",
|
|
213
|
+
size === "sm" ? "text-xs" : "text-sm",
|
|
214
|
+
)}
|
|
215
|
+
>
|
|
216
|
+
{formatValue(step.value)}
|
|
217
|
+
</span>
|
|
218
|
+
<div className={funnelBarContainerHorizontalVariants({ size })}>
|
|
219
|
+
<div
|
|
220
|
+
{...barInteractionProps}
|
|
221
|
+
className={cn(
|
|
222
|
+
"w-full rounded-sm transition-[height] ease-out",
|
|
223
|
+
color,
|
|
224
|
+
barInteractionClasses,
|
|
225
|
+
)}
|
|
226
|
+
style={{
|
|
227
|
+
height: animate ? (mounted ? `${barSize}%` : "0%") : `${barSize}%`,
|
|
228
|
+
transitionDuration: "0.5s",
|
|
229
|
+
transitionDelay: animate ? `${index * 0.08}s` : undefined,
|
|
230
|
+
}}
|
|
231
|
+
/>
|
|
232
|
+
</div>
|
|
233
|
+
<span
|
|
234
|
+
className={cn(
|
|
235
|
+
"w-full truncate text-center text-muted-foreground",
|
|
236
|
+
size === "sm" ? "text-[10px]" : "text-xs",
|
|
237
|
+
)}
|
|
238
|
+
>
|
|
239
|
+
{step.label}
|
|
240
|
+
</span>
|
|
241
|
+
</div>
|
|
242
|
+
) : (
|
|
243
|
+
<div role="listitem" aria-label={`${step.label}: ${formatValue(step.value)}`}>
|
|
244
|
+
<div className={funnelLabelVariants({ size })}>
|
|
245
|
+
<span className="truncate">{step.label}</span>
|
|
246
|
+
<span data-slot="funnel-value" className="tabular-nums">
|
|
247
|
+
{formatValue(step.value)}
|
|
248
|
+
</span>
|
|
249
|
+
</div>
|
|
250
|
+
<div className="relative w-full overflow-hidden rounded-sm bg-muted">
|
|
251
|
+
<div
|
|
252
|
+
{...barInteractionProps}
|
|
253
|
+
className={cn(funnelBarVariants({ size }), color, barInteractionClasses)}
|
|
254
|
+
style={{
|
|
255
|
+
width: animate ? (mounted ? `${barSize}%` : "0%") : `${barSize}%`,
|
|
256
|
+
transitionDuration: "0.5s",
|
|
257
|
+
transitionDelay: animate ? `${index * 0.08}s` : undefined,
|
|
258
|
+
}}
|
|
259
|
+
/>
|
|
260
|
+
</div>
|
|
261
|
+
</div>
|
|
262
|
+
)}
|
|
263
|
+
{showConversion && index < data.length - 1 && (
|
|
264
|
+
<div className={funnelConversionVariants({ size, direction })} aria-hidden="true">
|
|
265
|
+
<span>→ {computeConversionRate(data[index + 1]!.value, step.value)}</span>
|
|
266
|
+
</div>
|
|
267
|
+
)}
|
|
268
|
+
</Fragment>
|
|
269
|
+
)
|
|
270
|
+
})}
|
|
271
|
+
</div>
|
|
272
|
+
)
|
|
273
|
+
})
|
|
274
|
+
FunnelSteps.displayName = "FunnelSteps"
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Copied by `vantage add ui hover-card`. This file is yours to edit.
|
|
2
|
+
// Generated from @squadbase/vantage src/ui/hover-card.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
|
|
3
|
+
"use client"
|
|
4
|
+
|
|
5
|
+
import { PreviewCard as PreviewCardPrimitive } from "@base-ui/react/preview-card"
|
|
6
|
+
import { cn } from "@squadbase/vantage/ui"
|
|
7
|
+
|
|
8
|
+
function HoverCard({ ...props }: PreviewCardPrimitive.Root.Props) {
|
|
9
|
+
return <PreviewCardPrimitive.Root data-slot="hover-card" {...props} />
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function HoverCardTrigger({ ...props }: PreviewCardPrimitive.Trigger.Props) {
|
|
13
|
+
return (
|
|
14
|
+
<PreviewCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function HoverCardContent({
|
|
19
|
+
className,
|
|
20
|
+
side = "bottom",
|
|
21
|
+
sideOffset = 4,
|
|
22
|
+
align = "center",
|
|
23
|
+
alignOffset = 4,
|
|
24
|
+
...props
|
|
25
|
+
}: PreviewCardPrimitive.Popup.Props &
|
|
26
|
+
Pick<
|
|
27
|
+
PreviewCardPrimitive.Positioner.Props,
|
|
28
|
+
"align" | "alignOffset" | "side" | "sideOffset"
|
|
29
|
+
>) {
|
|
30
|
+
return (
|
|
31
|
+
<PreviewCardPrimitive.Portal data-slot="hover-card-portal">
|
|
32
|
+
<PreviewCardPrimitive.Positioner
|
|
33
|
+
align={align}
|
|
34
|
+
alignOffset={alignOffset}
|
|
35
|
+
side={side}
|
|
36
|
+
sideOffset={sideOffset}
|
|
37
|
+
className="isolate z-50"
|
|
38
|
+
>
|
|
39
|
+
<PreviewCardPrimitive.Popup
|
|
40
|
+
data-slot="hover-card-content"
|
|
41
|
+
className={cn(
|
|
42
|
+
"z-50 w-64 origin-(--transform-origin) 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",
|
|
43
|
+
className
|
|
44
|
+
)}
|
|
45
|
+
{...props}
|
|
46
|
+
/>
|
|
47
|
+
</PreviewCardPrimitive.Positioner>
|
|
48
|
+
</PreviewCardPrimitive.Portal>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export { HoverCard, HoverCardTrigger, HoverCardContent }
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// Copied by `vantage add ui input-group`. This file is yours to edit.
|
|
2
|
+
// Generated from @squadbase/vantage src/ui/input-group.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
5
|
+
|
|
6
|
+
import { cn } from "@squadbase/vantage/ui"
|
|
7
|
+
import { Button } from "@squadbase/vantage/ui"
|
|
8
|
+
import { Input } from "@squadbase/vantage/ui"
|
|
9
|
+
import { Textarea } from "@squadbase/vantage/ui"
|
|
10
|
+
|
|
11
|
+
function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
12
|
+
return (
|
|
13
|
+
<div
|
|
14
|
+
data-slot="input-group"
|
|
15
|
+
role="group"
|
|
16
|
+
className={cn(
|
|
17
|
+
"group/input-group relative flex h-8 w-full min-w-0 items-center rounded-lg border border-input transition-colors outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:bg-input/50 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:bg-input/30 dark:has-disabled:bg-input/80 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5",
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const inputGroupAddonVariants = cva(
|
|
26
|
+
"flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium text-muted-foreground select-none group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4",
|
|
27
|
+
{
|
|
28
|
+
variants: {
|
|
29
|
+
align: {
|
|
30
|
+
"inline-start":
|
|
31
|
+
"order-first pl-2 has-[>button]:ml-[-0.3rem] has-[>kbd]:ml-[-0.15rem]",
|
|
32
|
+
"inline-end":
|
|
33
|
+
"order-last pr-2 has-[>button]:mr-[-0.3rem] has-[>kbd]:mr-[-0.15rem]",
|
|
34
|
+
"block-start":
|
|
35
|
+
"order-first w-full justify-start px-2.5 pt-2 group-has-[>input]/input-group:pt-2 [.border-b]:pb-2",
|
|
36
|
+
"block-end":
|
|
37
|
+
"order-last w-full justify-start px-2.5 pb-2 group-has-[>input]/input-group:pb-2 [.border-t]:pt-2",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
defaultVariants: {
|
|
41
|
+
align: "inline-start",
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
function InputGroupAddon({
|
|
47
|
+
className,
|
|
48
|
+
align = "inline-start",
|
|
49
|
+
...props
|
|
50
|
+
}: React.ComponentProps<"div"> & VariantProps<typeof inputGroupAddonVariants>) {
|
|
51
|
+
return (
|
|
52
|
+
<div
|
|
53
|
+
role="group"
|
|
54
|
+
data-slot="input-group-addon"
|
|
55
|
+
data-align={align}
|
|
56
|
+
className={cn(inputGroupAddonVariants({ align }), className)}
|
|
57
|
+
onClick={(e) => {
|
|
58
|
+
if ((e.target as HTMLElement).closest("button")) {
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
e.currentTarget.parentElement?.querySelector("input")?.focus()
|
|
62
|
+
}}
|
|
63
|
+
{...props}
|
|
64
|
+
/>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const inputGroupButtonVariants = cva(
|
|
69
|
+
"flex items-center gap-2 text-sm shadow-none",
|
|
70
|
+
{
|
|
71
|
+
variants: {
|
|
72
|
+
size: {
|
|
73
|
+
xs: "h-6 gap-1 rounded-[calc(var(--radius)-3px)] px-1.5 [&>svg:not([class*='size-'])]:size-3.5",
|
|
74
|
+
sm: "",
|
|
75
|
+
"icon-xs":
|
|
76
|
+
"size-6 rounded-[calc(var(--radius)-3px)] p-0 has-[>svg]:p-0",
|
|
77
|
+
"icon-sm": "size-8 p-0 has-[>svg]:p-0",
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
defaultVariants: {
|
|
81
|
+
size: "xs",
|
|
82
|
+
},
|
|
83
|
+
}
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
function InputGroupButton({
|
|
87
|
+
className,
|
|
88
|
+
type = "button",
|
|
89
|
+
variant = "ghost",
|
|
90
|
+
size = "xs",
|
|
91
|
+
...props
|
|
92
|
+
}: Omit<React.ComponentProps<typeof Button>, "size" | "type"> &
|
|
93
|
+
VariantProps<typeof inputGroupButtonVariants> & {
|
|
94
|
+
type?: "button" | "submit" | "reset"
|
|
95
|
+
}) {
|
|
96
|
+
return (
|
|
97
|
+
<Button
|
|
98
|
+
type={type}
|
|
99
|
+
data-size={size}
|
|
100
|
+
variant={variant}
|
|
101
|
+
className={cn(inputGroupButtonVariants({ size }), className)}
|
|
102
|
+
{...props}
|
|
103
|
+
/>
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function InputGroupText({ className, ...props }: React.ComponentProps<"span">) {
|
|
108
|
+
return (
|
|
109
|
+
<span
|
|
110
|
+
className={cn(
|
|
111
|
+
"flex items-center gap-2 text-sm text-muted-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
|
|
112
|
+
className
|
|
113
|
+
)}
|
|
114
|
+
{...props}
|
|
115
|
+
/>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function InputGroupInput({
|
|
120
|
+
className,
|
|
121
|
+
...props
|
|
122
|
+
}: React.ComponentProps<"input">) {
|
|
123
|
+
return (
|
|
124
|
+
<Input
|
|
125
|
+
data-slot="input-group-control"
|
|
126
|
+
className={cn(
|
|
127
|
+
"flex-1 rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent",
|
|
128
|
+
className
|
|
129
|
+
)}
|
|
130
|
+
{...props}
|
|
131
|
+
/>
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function InputGroupTextarea({
|
|
136
|
+
className,
|
|
137
|
+
...props
|
|
138
|
+
}: React.ComponentProps<"textarea">) {
|
|
139
|
+
return (
|
|
140
|
+
<Textarea
|
|
141
|
+
data-slot="input-group-control"
|
|
142
|
+
className={cn(
|
|
143
|
+
"flex-1 resize-none rounded-none border-0 bg-transparent py-2 shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent",
|
|
144
|
+
className
|
|
145
|
+
)}
|
|
146
|
+
{...props}
|
|
147
|
+
/>
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export {
|
|
152
|
+
InputGroup,
|
|
153
|
+
InputGroupAddon,
|
|
154
|
+
InputGroupButton,
|
|
155
|
+
InputGroupText,
|
|
156
|
+
InputGroupInput,
|
|
157
|
+
InputGroupTextarea,
|
|
158
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Copied by `vantage add ui input`. This file is yours to edit.
|
|
2
|
+
// Generated from @squadbase/vantage src/ui/input.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Input as InputPrimitive } from "@base-ui/react/input"
|
|
5
|
+
|
|
6
|
+
import { cn } from "@squadbase/vantage/ui"
|
|
7
|
+
|
|
8
|
+
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
|
9
|
+
return (
|
|
10
|
+
<InputPrimitive
|
|
11
|
+
type={type}
|
|
12
|
+
data-slot="input"
|
|
13
|
+
className={cn(
|
|
14
|
+
"h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
|
|
15
|
+
className
|
|
16
|
+
)}
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { Input }
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Copied by `vantage add ui kbd`. This file is yours to edit.
|
|
2
|
+
// Generated from @squadbase/vantage src/ui/kbd.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
|
|
3
|
+
import { cn } from "@squadbase/vantage/ui"
|
|
4
|
+
|
|
5
|
+
function Kbd({ className, ...props }: React.ComponentProps<"kbd">) {
|
|
6
|
+
return (
|
|
7
|
+
<kbd
|
|
8
|
+
data-slot="kbd"
|
|
9
|
+
className={cn(
|
|
10
|
+
"pointer-events-none inline-flex h-5 w-fit min-w-5 items-center justify-center gap-1 rounded-sm bg-muted px-1 font-sans text-xs font-medium text-muted-foreground select-none in-data-[slot=tooltip-content]:bg-background/20 in-data-[slot=tooltip-content]:text-background dark:in-data-[slot=tooltip-content]:bg-background/10 [&_svg:not([class*='size-'])]:size-3",
|
|
11
|
+
className
|
|
12
|
+
)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function KbdGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
19
|
+
return (
|
|
20
|
+
<kbd
|
|
21
|
+
data-slot="kbd-group"
|
|
22
|
+
className={cn("inline-flex items-center gap-1", className)}
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { Kbd, KbdGroup }
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Copied by `vantage add ui label`. This file is yours to edit.
|
|
2
|
+
// Generated from @squadbase/vantage src/ui/label.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@squadbase/vantage/ui"
|
|
6
|
+
|
|
7
|
+
function Label({ className, ...props }: React.ComponentProps<"label">) {
|
|
8
|
+
return (
|
|
9
|
+
<label
|
|
10
|
+
data-slot="label"
|
|
11
|
+
className={cn(
|
|
12
|
+
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
|
13
|
+
className
|
|
14
|
+
)}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { Label }
|