@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,56 @@
1
+ // Copied by `vantage add ui scroll-area`. This file is yours to edit.
2
+ // Generated from @squadbase/squadkit registry/ui/scroll-area.tsx by `npm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
+ "use client"
4
+
5
+ import { ScrollArea as ScrollAreaPrimitive } from "@base-ui/react/scroll-area"
6
+
7
+ import { cn } from "@squadbase/vantage/ui"
8
+
9
+ function ScrollArea({
10
+ className,
11
+ children,
12
+ ...props
13
+ }: ScrollAreaPrimitive.Root.Props) {
14
+ return (
15
+ <ScrollAreaPrimitive.Root
16
+ data-slot="scroll-area"
17
+ className={cn("relative", className)}
18
+ {...props}
19
+ >
20
+ <ScrollAreaPrimitive.Viewport
21
+ data-slot="scroll-area-viewport"
22
+ className="size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1"
23
+ >
24
+ {children}
25
+ </ScrollAreaPrimitive.Viewport>
26
+ <ScrollBar />
27
+ <ScrollAreaPrimitive.Corner />
28
+ </ScrollAreaPrimitive.Root>
29
+ )
30
+ }
31
+
32
+ function ScrollBar({
33
+ className,
34
+ orientation = "vertical",
35
+ ...props
36
+ }: ScrollAreaPrimitive.Scrollbar.Props) {
37
+ return (
38
+ <ScrollAreaPrimitive.Scrollbar
39
+ data-slot="scroll-area-scrollbar"
40
+ data-orientation={orientation}
41
+ orientation={orientation}
42
+ className={cn(
43
+ "flex touch-none p-px transition-colors select-none data-horizontal:h-2.5 data-horizontal:flex-col data-horizontal:border-t data-horizontal:border-t-transparent data-vertical:h-full data-vertical:w-2.5 data-vertical:border-l data-vertical:border-l-transparent",
44
+ className
45
+ )}
46
+ {...props}
47
+ >
48
+ <ScrollAreaPrimitive.Thumb
49
+ data-slot="scroll-area-thumb"
50
+ className="relative flex-1 rounded-full bg-border"
51
+ />
52
+ </ScrollAreaPrimitive.Scrollbar>
53
+ )
54
+ }
55
+
56
+ export { ScrollArea, ScrollBar }
@@ -0,0 +1,214 @@
1
+ // Copied by `vantage add ui searchable-select`. This file is yours to edit.
2
+ // Generated from @squadbase/squadkit registry/components/searchable-select.tsx by `npm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
+ "use client"
4
+
5
+ /**
6
+ * SearchableSelect — a single-select combobox with a filter box. Filtering runs
7
+ * client-side over the given options; pass `onSearch` to take it over yourself
8
+ * (server-side search), which turns the built-in filter off.
9
+ */
10
+ import { forwardRef, useMemo, useState } from "react"
11
+ import { ChevronDownIcon } from "lucide-react"
12
+
13
+ import { cn } from "@squadbase/vantage/ui"
14
+ import { Button } from "@squadbase/vantage/ui"
15
+ import { Popover, PopoverContent, PopoverTrigger } from "@squadbase/vantage/ui"
16
+ import { flattenOptions, type SelectOptions } from "@squadbase/vantage/components"
17
+ import type { KeyboardEvent, MouseEvent } from "react"
18
+ import { CheckIcon, LoaderIcon, XIcon } from "lucide-react"
19
+ import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@squadbase/vantage/ui"
20
+ import { isGroupedOptions, type SelectOption } from "@squadbase/vantage/components"
21
+
22
+ /* ---------- inlined from @squadbase/squadkit registry/components/option-command.tsx (internal to @squadbase/vantage) ---------- */
23
+
24
+ interface OptionCommandListProps {
25
+ options: SelectOptions
26
+ /** Whether the given option value is currently selected (drives the check). */
27
+ isSelected: (value: string) => boolean
28
+ onSelect: (value: string) => void
29
+ /** Take filtering over yourself (server-side search); turns off built-in filter. */
30
+ onSearch?: (query: string) => void
31
+ isLoading: boolean
32
+ searchPlaceholder: string
33
+ emptyText: string
34
+ }
35
+
36
+ /** The searchable list body — flat or grouped, with loading and empty states. */
37
+ function OptionCommandList({
38
+ options,
39
+ isSelected,
40
+ onSelect,
41
+ onSearch,
42
+ isLoading,
43
+ searchPlaceholder,
44
+ emptyText,
45
+ }: OptionCommandListProps) {
46
+ const renderOptions = (items: SelectOption[]) =>
47
+ items.map((option) => (
48
+ <CommandItem
49
+ key={option.value}
50
+ value={option.value}
51
+ keywords={[option.label]}
52
+ disabled={option.disabled}
53
+ onSelect={onSelect}
54
+ >
55
+ {option.icon && <option.icon className="size-4 shrink-0" />}
56
+ <span className="flex-1 truncate">
57
+ {option.label}
58
+ {option.description && (
59
+ <span className="ml-2 text-xs text-muted-foreground">{option.description}</span>
60
+ )}
61
+ </span>
62
+ <CheckIcon
63
+ className={cn("size-4 shrink-0", isSelected(option.value) ? "opacity-100" : "opacity-0")}
64
+ />
65
+ </CommandItem>
66
+ ))
67
+
68
+ return (
69
+ <Command shouldFilter={!onSearch}>
70
+ <CommandInput placeholder={searchPlaceholder} onValueChange={onSearch} />
71
+ <CommandList>
72
+ {isLoading ? (
73
+ <div className="flex items-center justify-center py-6">
74
+ <LoaderIcon className="size-4 animate-spin text-muted-foreground" />
75
+ </div>
76
+ ) : (
77
+ <>
78
+ <CommandEmpty>{emptyText}</CommandEmpty>
79
+ {isGroupedOptions(options)
80
+ ? options.map((group) => (
81
+ <CommandGroup key={group.label} heading={group.label}>
82
+ {renderOptions(group.options)}
83
+ </CommandGroup>
84
+ ))
85
+ : renderOptions(options)}
86
+ </>
87
+ )}
88
+ </CommandList>
89
+ </Command>
90
+ )
91
+ }
92
+
93
+ interface RemoveButtonProps {
94
+ label: string
95
+ /** Runs on click and on Enter/Space; propagation is stopped for you. */
96
+ onRemove: () => void
97
+ /** Override the X icon size (default `size-3.5`). */
98
+ iconClassName?: string
99
+ }
100
+
101
+ /** The keyboard-accessible ×-affordance inside a badge or trigger. */
102
+ function RemoveButton({ label, onRemove, iconClassName }: RemoveButtonProps) {
103
+ const handle = (e: MouseEvent | KeyboardEvent) => {
104
+ e.stopPropagation()
105
+ onRemove()
106
+ }
107
+ return (
108
+ <span
109
+ role="button"
110
+ tabIndex={0}
111
+ aria-label={label}
112
+ className="rounded-sm opacity-50 hover:opacity-100 focus-visible:ring-1 focus-visible:ring-ring focus-visible:outline-none"
113
+ onClick={handle}
114
+ onKeyDown={(e) => {
115
+ if (e.key === "Enter" || e.key === " ") handle(e)
116
+ }}
117
+ >
118
+ <XIcon className={cn("size-3.5", iconClassName)} />
119
+ </span>
120
+ )
121
+ }
122
+
123
+ /* ---------- end of inlined code ---------- */
124
+
125
+ export interface SearchableSelectProps {
126
+ options: SelectOptions
127
+ value?: string
128
+ onChange?: (value: string | undefined) => void
129
+ onSearch?: (query: string) => void
130
+ placeholder?: string
131
+ searchPlaceholder?: string
132
+ emptyText?: string
133
+ isLoading?: boolean
134
+ disabled?: boolean
135
+ clearable?: boolean
136
+ className?: string
137
+ }
138
+
139
+ /** Not given yet (a draft fills props in steps): render empty, keep the same value between renders. */
140
+ const NO_OPTIONS: never[] = []
141
+
142
+ export const SearchableSelect = forwardRef<HTMLDivElement, SearchableSelectProps>(
143
+ function SearchableSelect(
144
+ {
145
+ options = NO_OPTIONS,
146
+ value,
147
+ onChange,
148
+ onSearch,
149
+ placeholder = "選択してください",
150
+ searchPlaceholder = "検索...",
151
+ emptyText = "見つかりませんでした",
152
+ isLoading = false,
153
+ disabled = false,
154
+ clearable = true,
155
+ className,
156
+ },
157
+ ref,
158
+ ) {
159
+ const [open, setOpen] = useState(false)
160
+
161
+ const allOptions = useMemo(() => flattenOptions(options), [options])
162
+ const selectedOption = useMemo(
163
+ () => allOptions.find((opt) => opt.value === value),
164
+ [allOptions, value],
165
+ )
166
+
167
+ function handleSelect(selectedValue: string) {
168
+ onChange?.(selectedValue === value ? undefined : selectedValue)
169
+ setOpen(false)
170
+ }
171
+
172
+ return (
173
+ <div ref={ref} data-slot="searchable-select">
174
+ <Popover open={open} onOpenChange={setOpen}>
175
+ <PopoverTrigger
176
+ render={
177
+ <Button
178
+ variant="outline"
179
+ disabled={disabled}
180
+ className={cn(
181
+ "w-full justify-between font-normal",
182
+ !selectedOption && "text-muted-foreground",
183
+ className,
184
+ )}
185
+ />
186
+ }
187
+ role="combobox"
188
+ aria-expanded={open}
189
+ >
190
+ <span className="truncate">{selectedOption ? selectedOption.label : placeholder}</span>
191
+ <span className="flex shrink-0 items-center gap-1">
192
+ {clearable && value && (
193
+ <RemoveButton label="クリア" onRemove={() => onChange?.(undefined)} />
194
+ )}
195
+ <ChevronDownIcon className="size-4 opacity-50" />
196
+ </span>
197
+ </PopoverTrigger>
198
+ <PopoverContent className="w-(--anchor-width) p-0" align="start">
199
+ <OptionCommandList
200
+ options={options}
201
+ isSelected={(v) => value === v}
202
+ onSelect={handleSelect}
203
+ onSearch={onSearch}
204
+ isLoading={isLoading}
205
+ searchPlaceholder={searchPlaceholder}
206
+ emptyText={emptyText}
207
+ />
208
+ </PopoverContent>
209
+ </Popover>
210
+ </div>
211
+ )
212
+ },
213
+ )
214
+ SearchableSelect.displayName = "SearchableSelect"
@@ -0,0 +1,43 @@
1
+ // Copied by `vantage add ui section-header`. This file is yours to edit.
2
+ // Generated from @squadbase/squadkit registry/components/section-header.tsx by `npm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
+ "use client"
4
+
5
+ /**
6
+ * SectionHeader — a titled divider inside a page: heading, optional
7
+ * description, and right-aligned actions that wrap below on narrow screens.
8
+ */
9
+ import { forwardRef, type ReactNode } from "react"
10
+
11
+ import { cn } from "@squadbase/vantage/ui"
12
+
13
+ const headingStyles = {
14
+ h1: "text-3xl font-bold tracking-tight",
15
+ h2: "text-xl font-semibold tracking-tight",
16
+ h3: "text-xl font-semibold",
17
+ h4: "text-lg font-semibold",
18
+ } as const
19
+
20
+ export interface SectionHeaderProps {
21
+ title: string
22
+ description?: string
23
+ actions?: ReactNode
24
+ className?: string
25
+ as?: keyof typeof headingStyles
26
+ }
27
+
28
+ export const SectionHeader = forwardRef<HTMLDivElement, SectionHeaderProps>(
29
+ ({ title, description, actions, className, as: Comp = "h2" }, ref) => (
30
+ <div
31
+ ref={ref}
32
+ data-slot="section-header"
33
+ className={cn("flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between", className)}
34
+ >
35
+ <div>
36
+ <Comp className={headingStyles[Comp]}>{title}</Comp>
37
+ {description && <p className="mt-1 text-sm text-muted-foreground">{description}</p>}
38
+ </div>
39
+ {actions && <div className="shrink-0">{actions}</div>}
40
+ </div>
41
+ ),
42
+ )
43
+ SectionHeader.displayName = "SectionHeader"
@@ -0,0 +1,76 @@
1
+ // Copied by `vantage add ui segmented-control`. This file is yours to edit.
2
+ // Generated from @squadbase/squadkit registry/components/segmented-control.tsx by `npm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
+ "use client"
4
+
5
+ /**
6
+ * SegmentedControl — a single-select row of buttons over `ToggleGroup`. Base
7
+ * UI's toggle group is multi-value by default, so this wrapper narrows it to
8
+ * one value and refuses to deselect the active segment.
9
+ */
10
+ import { forwardRef } from "react"
11
+
12
+ import { cn } from "@squadbase/vantage/ui"
13
+ import { ToggleGroup, ToggleGroupItem } from "@squadbase/vantage/ui"
14
+ import type { SegmentOption } from "@squadbase/vantage/components"
15
+
16
+ const iconSizes = {
17
+ sm: "size-3.5",
18
+ default: "size-4",
19
+ lg: "size-5",
20
+ } as const
21
+
22
+ export interface SegmentedControlProps {
23
+ options: SegmentOption[]
24
+ value: string
25
+ onChange: (value: string) => void
26
+ size?: keyof typeof iconSizes
27
+ fullWidth?: boolean
28
+ ariaLabel?: string
29
+ className?: string
30
+ }
31
+
32
+ /** Not given yet (a draft fills props in steps): render empty, keep the same value between renders. */
33
+ const NO_OPTIONS: never[] = []
34
+
35
+ export const SegmentedControl = forwardRef<HTMLDivElement, SegmentedControlProps>(
36
+ function SegmentedControl(
37
+ { options = NO_OPTIONS, value, onChange, size = "default", fullWidth = false, ariaLabel, className },
38
+ ref,
39
+ ) {
40
+ return (
41
+ <ToggleGroup
42
+ ref={ref}
43
+ data-slot="segmented-control"
44
+ value={[value]}
45
+ onValueChange={(next) => {
46
+ // Base UI reports the full pressed set; keep the newly added segment
47
+ // and ignore the empty set so the control never loses its selection.
48
+ const picked = next.find((item) => item !== value) ?? next[0]
49
+ if (typeof picked === "string") onChange(picked)
50
+ }}
51
+ variant="outline"
52
+ size={size}
53
+ spacing={0}
54
+ aria-label={ariaLabel}
55
+ className={cn(fullWidth && "w-full", className)}
56
+ >
57
+ {options.map((option) => {
58
+ const Icon = option.icon
59
+ return (
60
+ <ToggleGroupItem
61
+ key={option.value}
62
+ value={option.value}
63
+ disabled={option.disabled}
64
+ aria-label={option.label}
65
+ className={cn(fullWidth && "flex-1")}
66
+ >
67
+ {Icon && <Icon className={iconSizes[size]} aria-hidden="true" />}
68
+ {option.label}
69
+ </ToggleGroupItem>
70
+ )
71
+ })}
72
+ </ToggleGroup>
73
+ )
74
+ },
75
+ )
76
+ SegmentedControl.displayName = "SegmentedControl"
@@ -0,0 +1,255 @@
1
+ // Copied by `vantage add ui select`. This file is yours to edit.
2
+ // Generated from @squadbase/squadkit registry/ui/select.tsx by `npm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
+ "use client"
4
+
5
+ import * as React from "react"
6
+ import { Select as SelectPrimitive } from "@base-ui/react/select"
7
+
8
+ import { cn } from "@squadbase/vantage/ui"
9
+ import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
10
+
11
+ type DerivedSelectItem = { label: React.ReactNode; value: unknown }
12
+
13
+ /**
14
+ * Collect every reachable `SelectItem`'s `value` → children pair.
15
+ *
16
+ * Base UI's `Select.Value` renders the selected *value*, not the matching
17
+ * item's children — without `items` the trigger shows a raw `"kanto"` where the
18
+ * list says "関東". Deriving `items` from the children here means the ordinary
19
+ * case is right by default. The walk only sees `SelectItem` elements present in
20
+ * this tree, so options produced by an intermediate component still need
21
+ * `items` passed explicitly (an explicit `items` always wins).
22
+ */
23
+ function collectSelectItems(
24
+ node: React.ReactNode,
25
+ into: DerivedSelectItem[] = [],
26
+ ): DerivedSelectItem[] {
27
+ React.Children.forEach(node, (child) => {
28
+ if (!React.isValidElement(child)) return
29
+ const props = child.props as { value?: unknown; children?: React.ReactNode }
30
+ if (child.type === SelectItem) {
31
+ if (props.value !== undefined) into.push({ label: props.children, value: props.value })
32
+ return
33
+ }
34
+ if (props.children !== undefined) collectSelectItems(props.children, into)
35
+ })
36
+ return into
37
+ }
38
+
39
+ function Select<Value, Multiple extends boolean | undefined = false>({
40
+ items,
41
+ children,
42
+ ...props
43
+ }: SelectPrimitive.Root.Props<Value, Multiple>) {
44
+ const derivedItems = React.useMemo(() => {
45
+ if (items) return items
46
+ const collected = collectSelectItems(children)
47
+ return collected.length > 0 ? collected : undefined
48
+ }, [items, children])
49
+
50
+ return (
51
+ <SelectPrimitive.Root<Value, Multiple> items={derivedItems} {...props}>
52
+ {children}
53
+ </SelectPrimitive.Root>
54
+ )
55
+ }
56
+
57
+ function SelectGroup({ className, ...props }: SelectPrimitive.Group.Props) {
58
+ return (
59
+ <SelectPrimitive.Group
60
+ data-slot="select-group"
61
+ className={cn("scroll-my-1 p-1", className)}
62
+ {...props}
63
+ />
64
+ )
65
+ }
66
+
67
+ function SelectValue({ className, ...props }: SelectPrimitive.Value.Props) {
68
+ return (
69
+ <SelectPrimitive.Value
70
+ data-slot="select-value"
71
+ className={cn("flex flex-1 text-left", className)}
72
+ {...props}
73
+ />
74
+ )
75
+ }
76
+
77
+ function SelectTrigger({
78
+ className,
79
+ size = "default",
80
+ children,
81
+ ...props
82
+ }: SelectPrimitive.Trigger.Props & {
83
+ size?: "sm" | "default"
84
+ }) {
85
+ return (
86
+ <SelectPrimitive.Trigger
87
+ data-slot="select-trigger"
88
+ data-size={size}
89
+ className={cn(
90
+ "flex w-fit items-center justify-between gap-1.5 rounded-lg border border-input bg-transparent py-2 pr-2 pl-2.5 text-sm whitespace-nowrap transition-colors outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
91
+ className
92
+ )}
93
+ {...props}
94
+ >
95
+ {children}
96
+ <SelectPrimitive.Icon
97
+ render={
98
+ <ChevronDownIcon className="pointer-events-none size-4 text-muted-foreground" />
99
+ }
100
+ />
101
+ </SelectPrimitive.Trigger>
102
+ )
103
+ }
104
+
105
+ function SelectContent({
106
+ className,
107
+ children,
108
+ side = "bottom",
109
+ sideOffset = 4,
110
+ align = "center",
111
+ alignOffset = 0,
112
+ alignItemWithTrigger = true,
113
+ ...props
114
+ }: SelectPrimitive.Popup.Props &
115
+ Pick<
116
+ SelectPrimitive.Positioner.Props,
117
+ "align" | "alignOffset" | "side" | "sideOffset" | "alignItemWithTrigger"
118
+ >) {
119
+ return (
120
+ <SelectPrimitive.Portal>
121
+ <SelectPrimitive.Positioner
122
+ side={side}
123
+ sideOffset={sideOffset}
124
+ align={align}
125
+ alignOffset={alignOffset}
126
+ alignItemWithTrigger={alignItemWithTrigger}
127
+ className="isolate z-50"
128
+ >
129
+ <SelectPrimitive.Popup
130
+ data-slot="select-content"
131
+ data-align-trigger={alignItemWithTrigger}
132
+ className={cn(
133
+ "cn-menu-target cn-menu-translucent relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none 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 motion-reduce:animate-none! data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
134
+ className
135
+ )}
136
+ {...props}
137
+ >
138
+ <SelectScrollUpButton />
139
+ <SelectPrimitive.List>{children}</SelectPrimitive.List>
140
+ <SelectScrollDownButton />
141
+ </SelectPrimitive.Popup>
142
+ </SelectPrimitive.Positioner>
143
+ </SelectPrimitive.Portal>
144
+ )
145
+ }
146
+
147
+ function SelectLabel({
148
+ className,
149
+ ...props
150
+ }: SelectPrimitive.GroupLabel.Props) {
151
+ return (
152
+ <SelectPrimitive.GroupLabel
153
+ data-slot="select-label"
154
+ className={cn("px-1.5 py-1 text-xs text-muted-foreground", className)}
155
+ {...props}
156
+ />
157
+ )
158
+ }
159
+
160
+ function SelectItem({
161
+ className,
162
+ children,
163
+ ...props
164
+ }: SelectPrimitive.Item.Props) {
165
+ return (
166
+ <SelectPrimitive.Item
167
+ data-slot="select-item"
168
+ className={cn(
169
+ "relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
170
+ className
171
+ )}
172
+ {...props}
173
+ >
174
+ {/*
175
+ `line-clamp-1` rather than `truncate`: the item's width is capped by the
176
+ popup (`--anchor-width`), and a label longer than that must stop before
177
+ the `pr-8` reserved for the check indicator. `text-overflow: ellipsis`
178
+ does not apply to a flex container's anonymous text, so it would clip
179
+ without an ellipsis; `-webkit-line-clamp` ellipsizes the text node.
180
+ */}
181
+ <SelectPrimitive.ItemText className="min-w-0 flex-1 line-clamp-1">
182
+ {children}
183
+ </SelectPrimitive.ItemText>
184
+ <SelectPrimitive.ItemIndicator
185
+ render={
186
+ <span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center" />
187
+ }
188
+ >
189
+ <CheckIcon className="pointer-events-none" />
190
+ </SelectPrimitive.ItemIndicator>
191
+ </SelectPrimitive.Item>
192
+ )
193
+ }
194
+
195
+ function SelectSeparator({
196
+ className,
197
+ ...props
198
+ }: SelectPrimitive.Separator.Props) {
199
+ return (
200
+ <SelectPrimitive.Separator
201
+ data-slot="select-separator"
202
+ className={cn("pointer-events-none -mx-1 my-1 h-px bg-border", className)}
203
+ {...props}
204
+ />
205
+ )
206
+ }
207
+
208
+ function SelectScrollUpButton({
209
+ className,
210
+ ...props
211
+ }: React.ComponentProps<typeof SelectPrimitive.ScrollUpArrow>) {
212
+ return (
213
+ <SelectPrimitive.ScrollUpArrow
214
+ data-slot="select-scroll-up-button"
215
+ className={cn(
216
+ "top-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
217
+ className
218
+ )}
219
+ {...props}
220
+ >
221
+ <ChevronUpIcon />
222
+ </SelectPrimitive.ScrollUpArrow>
223
+ )
224
+ }
225
+
226
+ function SelectScrollDownButton({
227
+ className,
228
+ ...props
229
+ }: React.ComponentProps<typeof SelectPrimitive.ScrollDownArrow>) {
230
+ return (
231
+ <SelectPrimitive.ScrollDownArrow
232
+ data-slot="select-scroll-down-button"
233
+ className={cn(
234
+ "bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
235
+ className
236
+ )}
237
+ {...props}
238
+ >
239
+ <ChevronDownIcon />
240
+ </SelectPrimitive.ScrollDownArrow>
241
+ )
242
+ }
243
+
244
+ export {
245
+ Select,
246
+ SelectContent,
247
+ SelectGroup,
248
+ SelectItem,
249
+ SelectLabel,
250
+ SelectScrollDownButton,
251
+ SelectScrollUpButton,
252
+ SelectSeparator,
253
+ SelectTrigger,
254
+ SelectValue,
255
+ }
@@ -0,0 +1,27 @@
1
+ // Copied by `vantage add ui separator`. This file is yours to edit.
2
+ // Generated from @squadbase/squadkit registry/ui/separator.tsx by `npm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
+ "use client"
4
+
5
+ import { Separator as SeparatorPrimitive } from "@base-ui/react/separator"
6
+
7
+ import { cn } from "@squadbase/vantage/ui"
8
+
9
+ function Separator({
10
+ className,
11
+ orientation = "horizontal",
12
+ ...props
13
+ }: SeparatorPrimitive.Props) {
14
+ return (
15
+ <SeparatorPrimitive
16
+ data-slot="separator"
17
+ orientation={orientation}
18
+ className={cn(
19
+ "shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
20
+ className
21
+ )}
22
+ {...props}
23
+ />
24
+ )
25
+ }
26
+
27
+ export { Separator }