@squadbase/vantage 0.6.2-dev1.a7c4a3e → 0.6.2-dev1.ac99e02

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/dist/add-4GHF2TX2.js +6 -0
  2. package/dist/add-4GHF2TX2.js.map +1 -0
  3. package/dist/{build-JB4VOMUI.js → build-MFWRPYKW.js} +9 -9
  4. package/dist/{build-JB4VOMUI.js.map → build-MFWRPYKW.js.map} +1 -1
  5. package/dist/{check-ZJYK6RLG.js → check-ZBIH422T.js} +7 -7
  6. package/dist/{check-ZJYK6RLG.js.map → check-ZBIH422T.js.map} +1 -1
  7. package/dist/{chunk-BCE22JSM.js → chunk-7DZGJQ3P.js} +33 -14
  8. package/dist/chunk-7DZGJQ3P.js.map +1 -0
  9. package/dist/{chunk-U2M6HGKQ.js → chunk-AABMKHEU.js} +4 -4
  10. package/dist/{chunk-U2M6HGKQ.js.map → chunk-AABMKHEU.js.map} +1 -1
  11. package/dist/{chunk-MP3QSOXL.js → chunk-AG6RZ2RR.js} +992 -16
  12. package/dist/chunk-AG6RZ2RR.js.map +1 -0
  13. package/dist/{chunk-KLEIVDUV.js → chunk-CEXAQQ5W.js} +150 -11
  14. package/dist/chunk-CEXAQQ5W.js.map +1 -0
  15. package/dist/{chunk-QI3XQKHS.js → chunk-GNXCX4WE.js} +10 -3
  16. package/dist/chunk-GNXCX4WE.js.map +1 -0
  17. package/dist/chunk-IBGOCWJB.js +1716 -0
  18. package/dist/chunk-IBGOCWJB.js.map +1 -0
  19. package/dist/{chunk-JGRMRXXZ.js → chunk-JKLLFVEU.js} +3 -3
  20. package/dist/{chunk-JGRMRXXZ.js.map → chunk-JKLLFVEU.js.map} +1 -1
  21. package/dist/{chunk-RPZAALCX.js → chunk-OB6UBJXF.js} +153 -153
  22. package/dist/{chunk-RPZAALCX.js.map → chunk-OB6UBJXF.js.map} +1 -1
  23. package/dist/{chunk-EP6JHXSR.js → chunk-QEWCYW3I.js} +44 -9
  24. package/dist/chunk-QEWCYW3I.js.map +1 -0
  25. package/dist/{chunk-ZVANIULZ.js → chunk-RNTDBX6S.js} +4 -4
  26. package/dist/{chunk-ZVANIULZ.js.map → chunk-RNTDBX6S.js.map} +1 -1
  27. package/dist/{chunk-PVTHHV7X.js → chunk-V3LF7BE7.js} +27 -5
  28. package/dist/chunk-V3LF7BE7.js.map +1 -0
  29. package/dist/{chunk-DTDVSFRY.js → chunk-XQUSGIDG.js} +17 -3
  30. package/dist/chunk-XQUSGIDG.js.map +1 -0
  31. package/dist/cli.js +12 -11
  32. package/dist/cli.js.map +1 -1
  33. package/dist/client/index.d.ts +32 -3
  34. package/dist/client/index.js +205 -12
  35. package/dist/client/index.js.map +1 -1
  36. package/dist/components/index.d.ts +216 -1
  37. package/dist/components/index.js +2 -2
  38. package/dist/{dev-FCEC3UKQ.js → dev-DCTOHQ6W.js} +10 -10
  39. package/dist/dev-DCTOHQ6W.js.map +1 -0
  40. package/dist/{docs-EJJ3BECT.js → docs-JVP4RPKT.js} +3 -3
  41. package/dist/{docs-EJJ3BECT.js.map → docs-JVP4RPKT.js.map} +1 -1
  42. package/dist/{doctor-WHZXPERA.js → doctor-5E2VEZPK.js} +7 -7
  43. package/dist/{doctor-WHZXPERA.js.map → doctor-5E2VEZPK.js.map} +1 -1
  44. package/dist/{preview-OGMCNQUO.js → preview-T6CLSQVX.js} +3 -3
  45. package/dist/{preview-OGMCNQUO.js.map → preview-T6CLSQVX.js.map} +1 -1
  46. package/dist/query/index.d.ts +0 -11
  47. package/dist/query/index.js +2 -2
  48. package/dist/{routes-GTFO5PUF.js → routes-3SGAFDMC.js} +6 -6
  49. package/dist/{routes-GTFO5PUF.js.map → routes-3SGAFDMC.js.map} +1 -1
  50. package/dist/{search-6Y422SV6.js → search-5LC4XPB5.js} +3 -3
  51. package/dist/{search-6Y422SV6.js.map → search-5LC4XPB5.js.map} +1 -1
  52. package/dist/slide/index.js +47 -10
  53. package/dist/slide/index.js.map +1 -1
  54. package/dist/table/index.js +8 -7
  55. package/dist/table/index.js.map +1 -1
  56. package/dist/ui/index.d.ts +140 -10
  57. package/dist/ui/index.js +1405 -63
  58. package/dist/ui/index.js.map +1 -1
  59. package/dist/{upgrade-AL4KZZR4.js → upgrade-DBBE6GIR.js} +7 -7
  60. package/dist/{upgrade-AL4KZZR4.js.map → upgrade-DBBE6GIR.js.map} +1 -1
  61. package/dist/vite/index.d.ts +10 -0
  62. package/dist/vite/index.js +4 -4
  63. package/docs/index.json +291 -61
  64. package/docs/ja/changelog.md +26 -0
  65. package/docs/ja/components.md +33 -89
  66. package/docs/ja/page-types/csv.md +83 -0
  67. package/docs/ja/page-types/doc.md +83 -0
  68. package/docs/ja/page-types/excel.md +82 -0
  69. package/docs/ja/page-types/slide.md +132 -0
  70. package/docs/ja/page-types.md +47 -0
  71. package/docs/ja/parts/area-chart.md +61 -0
  72. package/docs/ja/parts/bar-chart.md +69 -0
  73. package/docs/ja/parts/bar-list.md +35 -0
  74. package/docs/ja/parts/category-bar.md +36 -0
  75. package/docs/ja/parts/donut-chart.md +47 -0
  76. package/docs/ja/parts/echart.md +3 -0
  77. package/docs/ja/parts/line-chart.md +58 -0
  78. package/docs/ja/parts/progress-circle.md +34 -0
  79. package/docs/ja/parts/sparkline.md +1 -1
  80. package/docs/ja/parts/tracker.md +32 -0
  81. package/docs/ja/routing.md +8 -86
  82. package/docs/ja/ui/alert-dialog.md +41 -0
  83. package/docs/ja/ui/avatar.md +31 -0
  84. package/docs/ja/ui/combobox.md +74 -0
  85. package/docs/ja/ui/drawer.md +41 -0
  86. package/docs/ja/ui/field.md +42 -0
  87. package/docs/ja/ui/hover-card.md +21 -0
  88. package/docs/ja/ui/kbd.md +16 -0
  89. package/docs/ja/ui/pagination.md +36 -0
  90. package/docs/ja/ui/radio-group.md +21 -0
  91. package/docs/ja/ui/toast.md +46 -0
  92. package/docs/ja/ui-and-theming.md +14 -7
  93. package/package.json +25 -13
  94. package/registry/ui/accordion.tsx +74 -0
  95. package/registry/ui/alert-dialog.tsx +189 -0
  96. package/registry/ui/alert.tsx +78 -0
  97. package/registry/ui/app-shell.tsx +281 -0
  98. package/registry/ui/area-chart.tsx +1015 -0
  99. package/registry/ui/avatar.tsx +110 -0
  100. package/registry/ui/badge.tsx +54 -0
  101. package/registry/ui/bar-chart.tsx +1013 -0
  102. package/registry/ui/bar-list.tsx +302 -0
  103. package/registry/ui/breadcrumb.tsx +126 -0
  104. package/registry/ui/button.tsx +60 -0
  105. package/registry/ui/calendar.tsx +221 -0
  106. package/registry/ui/card.tsx +105 -0
  107. package/registry/ui/category-bar.tsx +281 -0
  108. package/registry/ui/checkbox.tsx +28 -0
  109. package/registry/ui/collapsible.tsx +21 -0
  110. package/registry/ui/combobox.tsx +310 -0
  111. package/registry/ui/command.tsx +196 -0
  112. package/registry/ui/dashboard-card.tsx +174 -0
  113. package/registry/ui/data-table.tsx +16 -91
  114. package/registry/ui/date-range-picker.tsx +209 -0
  115. package/registry/ui/dialog.tsx +159 -0
  116. package/registry/ui/donut-chart.tsx +1004 -0
  117. package/registry/ui/drawer.tsx +229 -0
  118. package/registry/ui/dropdown-menu.tsx +272 -0
  119. package/registry/ui/echart.tsx +486 -0
  120. package/registry/ui/field.tsx +240 -0
  121. package/registry/ui/filter-bar.tsx +365 -0
  122. package/registry/ui/funnel-steps.tsx +274 -0
  123. package/registry/ui/hover-card.tsx +52 -0
  124. package/registry/ui/input-group.tsx +158 -0
  125. package/registry/ui/input.tsx +22 -0
  126. package/registry/ui/kbd.tsx +28 -0
  127. package/registry/ui/label.tsx +20 -0
  128. package/registry/ui/line-chart.tsx +1007 -0
  129. package/registry/ui/metric-value.tsx +54 -0
  130. package/registry/ui/multi-select.tsx +226 -0
  131. package/registry/ui/page-shell.tsx +227 -0
  132. package/registry/ui/pagination.tsx +138 -0
  133. package/registry/ui/placeholder.tsx +30 -0
  134. package/registry/ui/popover.tsx +90 -0
  135. package/registry/ui/progress-circle.tsx +272 -0
  136. package/registry/ui/progress.tsx +83 -0
  137. package/registry/ui/radio-group.tsx +39 -0
  138. package/registry/ui/scroll-area.tsx +55 -0
  139. package/registry/ui/searchable-select.tsx +209 -0
  140. package/registry/ui/section-header.tsx +41 -0
  141. package/registry/ui/segmented-control.tsx +71 -0
  142. package/registry/ui/select.tsx +253 -0
  143. package/registry/ui/separator.tsx +25 -0
  144. package/registry/ui/sheet.tsx +137 -0
  145. package/registry/ui/sidebar.tsx +723 -0
  146. package/registry/ui/skeleton.tsx +15 -0
  147. package/registry/ui/slider.tsx +54 -0
  148. package/registry/ui/sparkline.tsx +155 -0
  149. package/registry/ui/spinner.tsx +12 -0
  150. package/registry/ui/states.tsx +61 -0
  151. package/registry/ui/status-badge.tsx +79 -0
  152. package/registry/ui/switch.tsx +32 -0
  153. package/registry/ui/table.tsx +116 -0
  154. package/registry/ui/tabs.tsx +82 -0
  155. package/registry/ui/textarea.tsx +20 -0
  156. package/registry/ui/toast.tsx +250 -0
  157. package/registry/ui/toggle-group.tsx +89 -0
  158. package/registry/ui/toggle.tsx +45 -0
  159. package/registry/ui/tooltip.tsx +66 -0
  160. package/registry/ui/tracker.tsx +236 -0
  161. package/registry/ui/trend-indicator.tsx +62 -0
  162. package/skills/vantage-add-feature/SKILL.md +9 -7
  163. package/skills/vantage-design/SKILL.md +3 -1
  164. package/skills/vantage-design/references/format.md +1 -1
  165. package/skills/vantage-pitfalls/SKILL.md +5 -1
  166. package/skills/vantage-slide/SKILL.md +17 -0
  167. package/templates/AGENTS.md +10 -1
  168. package/templates/agents/api.md +29 -0
  169. package/templates/agents/csv.md +2 -1
  170. package/templates/agents/doc.md +4 -3
  171. package/templates/agents/page.md +22 -8
  172. package/templates/agents/slide.md +13 -1
  173. package/templates/agents/xlsx.md +2 -1
  174. package/theme.css +7 -1
  175. package/dist/add-DY4AXANK.js +0 -6
  176. package/dist/add-DY4AXANK.js.map +0 -1
  177. package/dist/chunk-BCE22JSM.js.map +0 -1
  178. package/dist/chunk-DTDVSFRY.js.map +0 -1
  179. package/dist/chunk-EP6JHXSR.js.map +0 -1
  180. package/dist/chunk-KLEIVDUV.js.map +0 -1
  181. package/dist/chunk-MP3QSOXL.js.map +0 -1
  182. package/dist/chunk-PVTHHV7X.js.map +0 -1
  183. package/dist/chunk-QI3XQKHS.js.map +0 -1
  184. package/dist/chunk-T3FLAGI6.js +0 -316
  185. package/dist/chunk-T3FLAGI6.js.map +0 -1
  186. package/dist/dev-FCEC3UKQ.js.map +0 -1
@@ -0,0 +1,209 @@
1
+ // Copied by `vantage add ui searchable-select`. This file is yours to edit.
2
+ // Generated from @squadbase/vantage src/components/searchable-select.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
3
+ /**
4
+ * SearchableSelect — a single-select combobox with a filter box. Filtering runs
5
+ * client-side over the given options; pass `onSearch` to take it over yourself
6
+ * (server-side search), which turns the built-in filter off.
7
+ */
8
+ import { forwardRef, useMemo, useState } from "react"
9
+ import { ChevronDownIcon } from "lucide-react"
10
+
11
+ import { cn } from "@squadbase/vantage/ui"
12
+ import { Button } from "@squadbase/vantage/ui"
13
+ import { Popover, PopoverContent, PopoverTrigger } from "@squadbase/vantage/ui"
14
+ import { flattenOptions, type SelectOptions } from "@squadbase/vantage/components"
15
+ import type { KeyboardEvent, MouseEvent } from "react"
16
+ import { CheckIcon, LoaderIcon, XIcon } from "lucide-react"
17
+ import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@squadbase/vantage/ui"
18
+ import { isGroupedOptions, type SelectOption } from "@squadbase/vantage/components"
19
+
20
+ /* ---------- inlined from src/components/option-command.tsx (internal to @squadbase/vantage) ---------- */
21
+
22
+ interface OptionCommandListProps {
23
+ options: SelectOptions
24
+ /** Whether the given option value is currently selected (drives the check). */
25
+ isSelected: (value: string) => boolean
26
+ onSelect: (value: string) => void
27
+ /** Take filtering over yourself (server-side search); turns off built-in filter. */
28
+ onSearch?: (query: string) => void
29
+ isLoading: boolean
30
+ searchPlaceholder: string
31
+ emptyText: string
32
+ }
33
+
34
+ /** The searchable list body — flat or grouped, with loading and empty states. */
35
+ function OptionCommandList({
36
+ options,
37
+ isSelected,
38
+ onSelect,
39
+ onSearch,
40
+ isLoading,
41
+ searchPlaceholder,
42
+ emptyText,
43
+ }: OptionCommandListProps) {
44
+ const renderOptions = (items: SelectOption[]) =>
45
+ items.map((option) => (
46
+ <CommandItem
47
+ key={option.value}
48
+ value={option.value}
49
+ keywords={[option.label]}
50
+ disabled={option.disabled}
51
+ onSelect={onSelect}
52
+ >
53
+ {option.icon && <option.icon className="size-4 shrink-0" />}
54
+ <span className="flex-1 truncate">
55
+ {option.label}
56
+ {option.description && (
57
+ <span className="ml-2 text-xs text-muted-foreground">{option.description}</span>
58
+ )}
59
+ </span>
60
+ <CheckIcon
61
+ className={cn("size-4 shrink-0", isSelected(option.value) ? "opacity-100" : "opacity-0")}
62
+ />
63
+ </CommandItem>
64
+ ))
65
+
66
+ return (
67
+ <Command shouldFilter={!onSearch}>
68
+ <CommandInput placeholder={searchPlaceholder} onValueChange={onSearch} />
69
+ <CommandList>
70
+ {isLoading ? (
71
+ <div className="flex items-center justify-center py-6">
72
+ <LoaderIcon className="size-4 animate-spin text-muted-foreground" />
73
+ </div>
74
+ ) : (
75
+ <>
76
+ <CommandEmpty>{emptyText}</CommandEmpty>
77
+ {isGroupedOptions(options)
78
+ ? options.map((group) => (
79
+ <CommandGroup key={group.label} heading={group.label}>
80
+ {renderOptions(group.options)}
81
+ </CommandGroup>
82
+ ))
83
+ : renderOptions(options)}
84
+ </>
85
+ )}
86
+ </CommandList>
87
+ </Command>
88
+ )
89
+ }
90
+
91
+ interface RemoveButtonProps {
92
+ label: string
93
+ /** Runs on click and on Enter/Space; propagation is stopped for you. */
94
+ onRemove: () => void
95
+ /** Override the X icon size (default `size-3.5`). */
96
+ iconClassName?: string
97
+ }
98
+
99
+ /** The keyboard-accessible ×-affordance inside a badge or trigger. */
100
+ function RemoveButton({ label, onRemove, iconClassName }: RemoveButtonProps) {
101
+ const handle = (e: MouseEvent | KeyboardEvent) => {
102
+ e.stopPropagation()
103
+ onRemove()
104
+ }
105
+ return (
106
+ <span
107
+ role="button"
108
+ tabIndex={0}
109
+ aria-label={label}
110
+ className="rounded-sm opacity-50 hover:opacity-100 focus-visible:ring-1 focus-visible:ring-ring focus-visible:outline-none"
111
+ onClick={handle}
112
+ onKeyDown={(e) => {
113
+ if (e.key === "Enter" || e.key === " ") handle(e)
114
+ }}
115
+ >
116
+ <XIcon className={cn("size-3.5", iconClassName)} />
117
+ </span>
118
+ )
119
+ }
120
+
121
+ /* ---------- end of inlined code ---------- */
122
+
123
+ export interface SearchableSelectProps {
124
+ options: SelectOptions
125
+ value?: string
126
+ onChange?: (value: string | undefined) => void
127
+ onSearch?: (query: string) => void
128
+ placeholder?: string
129
+ searchPlaceholder?: string
130
+ emptyText?: string
131
+ isLoading?: boolean
132
+ disabled?: boolean
133
+ clearable?: boolean
134
+ className?: string
135
+ }
136
+
137
+ export const SearchableSelect = forwardRef<HTMLDivElement, SearchableSelectProps>(
138
+ function SearchableSelect(
139
+ {
140
+ options,
141
+ value,
142
+ onChange,
143
+ onSearch,
144
+ placeholder = "選択してください",
145
+ searchPlaceholder = "検索...",
146
+ emptyText = "見つかりませんでした",
147
+ isLoading = false,
148
+ disabled = false,
149
+ clearable = true,
150
+ className,
151
+ },
152
+ ref,
153
+ ) {
154
+ const [open, setOpen] = useState(false)
155
+
156
+ const allOptions = useMemo(() => flattenOptions(options), [options])
157
+ const selectedOption = useMemo(
158
+ () => allOptions.find((opt) => opt.value === value),
159
+ [allOptions, value],
160
+ )
161
+
162
+ function handleSelect(selectedValue: string) {
163
+ onChange?.(selectedValue === value ? undefined : selectedValue)
164
+ setOpen(false)
165
+ }
166
+
167
+ return (
168
+ <div ref={ref} data-slot="searchable-select">
169
+ <Popover open={open} onOpenChange={setOpen}>
170
+ <PopoverTrigger
171
+ render={
172
+ <Button
173
+ variant="outline"
174
+ disabled={disabled}
175
+ className={cn(
176
+ "w-full justify-between font-normal",
177
+ !selectedOption && "text-muted-foreground",
178
+ className,
179
+ )}
180
+ />
181
+ }
182
+ role="combobox"
183
+ aria-expanded={open}
184
+ >
185
+ <span className="truncate">{selectedOption ? selectedOption.label : placeholder}</span>
186
+ <span className="flex shrink-0 items-center gap-1">
187
+ {clearable && value && (
188
+ <RemoveButton label="クリア" onRemove={() => onChange?.(undefined)} />
189
+ )}
190
+ <ChevronDownIcon className="size-4 opacity-50" />
191
+ </span>
192
+ </PopoverTrigger>
193
+ <PopoverContent className="w-(--anchor-width) p-0" align="start">
194
+ <OptionCommandList
195
+ options={options}
196
+ isSelected={(v) => value === v}
197
+ onSelect={handleSelect}
198
+ onSearch={onSearch}
199
+ isLoading={isLoading}
200
+ searchPlaceholder={searchPlaceholder}
201
+ emptyText={emptyText}
202
+ />
203
+ </PopoverContent>
204
+ </Popover>
205
+ </div>
206
+ )
207
+ },
208
+ )
209
+ SearchableSelect.displayName = "SearchableSelect"
@@ -0,0 +1,41 @@
1
+ // Copied by `vantage add ui section-header`. This file is yours to edit.
2
+ // Generated from @squadbase/vantage src/components/section-header.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
3
+ /**
4
+ * SectionHeader — a titled divider inside a page: heading, optional
5
+ * description, and right-aligned actions that wrap below on narrow screens.
6
+ */
7
+ import { forwardRef, type ReactNode } from "react"
8
+
9
+ import { cn } from "@squadbase/vantage/ui"
10
+
11
+ const headingStyles = {
12
+ h1: "text-3xl font-bold tracking-tight",
13
+ h2: "text-2xl font-semibold tracking-tight",
14
+ h3: "text-xl font-semibold",
15
+ h4: "text-lg font-semibold",
16
+ } as const
17
+
18
+ export interface SectionHeaderProps {
19
+ title: string
20
+ description?: string
21
+ actions?: ReactNode
22
+ className?: string
23
+ as?: keyof typeof headingStyles
24
+ }
25
+
26
+ export const SectionHeader = forwardRef<HTMLDivElement, SectionHeaderProps>(
27
+ ({ title, description, actions, className, as: Comp = "h2" }, ref) => (
28
+ <div
29
+ ref={ref}
30
+ data-slot="section-header"
31
+ className={cn("flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between", className)}
32
+ >
33
+ <div>
34
+ <Comp className={headingStyles[Comp]}>{title}</Comp>
35
+ {description && <p className="mt-1 text-sm text-muted-foreground">{description}</p>}
36
+ </div>
37
+ {actions && <div className="shrink-0">{actions}</div>}
38
+ </div>
39
+ ),
40
+ )
41
+ SectionHeader.displayName = "SectionHeader"
@@ -0,0 +1,71 @@
1
+ // Copied by `vantage add ui segmented-control`. This file is yours to edit.
2
+ // Generated from @squadbase/vantage src/components/segmented-control.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
3
+ /**
4
+ * SegmentedControl — a single-select row of buttons over `ToggleGroup`. Base
5
+ * UI's toggle group is multi-value by default, so this wrapper narrows it to
6
+ * one value and refuses to deselect the active segment.
7
+ */
8
+ import { forwardRef } from "react"
9
+
10
+ import { cn } from "@squadbase/vantage/ui"
11
+ import { ToggleGroup, ToggleGroupItem } from "@squadbase/vantage/ui"
12
+ import type { SegmentOption } from "@squadbase/vantage/components"
13
+
14
+ const iconSizes = {
15
+ sm: "size-3.5",
16
+ default: "size-4",
17
+ lg: "size-5",
18
+ } as const
19
+
20
+ export interface SegmentedControlProps {
21
+ options: SegmentOption[]
22
+ value: string
23
+ onChange: (value: string) => void
24
+ size?: keyof typeof iconSizes
25
+ fullWidth?: boolean
26
+ ariaLabel?: string
27
+ className?: string
28
+ }
29
+
30
+ export const SegmentedControl = forwardRef<HTMLDivElement, SegmentedControlProps>(
31
+ function SegmentedControl(
32
+ { options, value, onChange, size = "default", fullWidth = false, ariaLabel, className },
33
+ ref,
34
+ ) {
35
+ return (
36
+ <ToggleGroup
37
+ ref={ref}
38
+ data-slot="segmented-control"
39
+ value={[value]}
40
+ onValueChange={(next) => {
41
+ // Base UI reports the full pressed set; keep the newly added segment
42
+ // and ignore the empty set so the control never loses its selection.
43
+ const picked = next.find((item) => item !== value) ?? next[0]
44
+ if (typeof picked === "string") onChange(picked)
45
+ }}
46
+ variant="outline"
47
+ size={size}
48
+ spacing={0}
49
+ aria-label={ariaLabel}
50
+ className={cn(fullWidth && "w-full", className)}
51
+ >
52
+ {options.map((option) => {
53
+ const Icon = option.icon
54
+ return (
55
+ <ToggleGroupItem
56
+ key={option.value}
57
+ value={option.value}
58
+ disabled={option.disabled}
59
+ aria-label={option.label}
60
+ className={cn(fullWidth && "flex-1")}
61
+ >
62
+ {Icon && <Icon className={iconSizes[size]} aria-hidden="true" />}
63
+ {option.label}
64
+ </ToggleGroupItem>
65
+ )
66
+ })}
67
+ </ToggleGroup>
68
+ )
69
+ },
70
+ )
71
+ SegmentedControl.displayName = "SegmentedControl"
@@ -0,0 +1,253 @@
1
+ // Copied by `vantage add ui select`. This file is yours to edit.
2
+ // Generated from @squadbase/vantage src/ui/select.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
3
+ import * as React from "react"
4
+ import { Select as SelectPrimitive } from "@base-ui/react/select"
5
+
6
+ import { cn } from "@squadbase/vantage/ui"
7
+ import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
8
+
9
+ type DerivedSelectItem = { label: React.ReactNode; value: unknown }
10
+
11
+ /**
12
+ * Collect every reachable `SelectItem`'s `value` → children pair.
13
+ *
14
+ * Base UI's `Select.Value` renders the selected *value*, not the matching
15
+ * item's children — without `items` the trigger shows a raw `"kanto"` where the
16
+ * list says "関東". Deriving `items` from the children here means the ordinary
17
+ * case is right by default. The walk only sees `SelectItem` elements present in
18
+ * this tree, so options produced by an intermediate component still need
19
+ * `items` passed explicitly (an explicit `items` always wins).
20
+ */
21
+ function collectSelectItems(
22
+ node: React.ReactNode,
23
+ into: DerivedSelectItem[] = [],
24
+ ): DerivedSelectItem[] {
25
+ React.Children.forEach(node, (child) => {
26
+ if (!React.isValidElement(child)) return
27
+ const props = child.props as { value?: unknown; children?: React.ReactNode }
28
+ if (child.type === SelectItem) {
29
+ if (props.value !== undefined) into.push({ label: props.children, value: props.value })
30
+ return
31
+ }
32
+ if (props.children !== undefined) collectSelectItems(props.children, into)
33
+ })
34
+ return into
35
+ }
36
+
37
+ function Select<Value, Multiple extends boolean | undefined = false>({
38
+ items,
39
+ children,
40
+ ...props
41
+ }: SelectPrimitive.Root.Props<Value, Multiple>) {
42
+ const derivedItems = React.useMemo(() => {
43
+ if (items) return items
44
+ const collected = collectSelectItems(children)
45
+ return collected.length > 0 ? collected : undefined
46
+ }, [items, children])
47
+
48
+ return (
49
+ <SelectPrimitive.Root<Value, Multiple> items={derivedItems} {...props}>
50
+ {children}
51
+ </SelectPrimitive.Root>
52
+ )
53
+ }
54
+
55
+ function SelectGroup({ className, ...props }: SelectPrimitive.Group.Props) {
56
+ return (
57
+ <SelectPrimitive.Group
58
+ data-slot="select-group"
59
+ className={cn("scroll-my-1 p-1", className)}
60
+ {...props}
61
+ />
62
+ )
63
+ }
64
+
65
+ function SelectValue({ className, ...props }: SelectPrimitive.Value.Props) {
66
+ return (
67
+ <SelectPrimitive.Value
68
+ data-slot="select-value"
69
+ className={cn("flex flex-1 text-left", className)}
70
+ {...props}
71
+ />
72
+ )
73
+ }
74
+
75
+ function SelectTrigger({
76
+ className,
77
+ size = "default",
78
+ children,
79
+ ...props
80
+ }: SelectPrimitive.Trigger.Props & {
81
+ size?: "sm" | "default"
82
+ }) {
83
+ return (
84
+ <SelectPrimitive.Trigger
85
+ data-slot="select-trigger"
86
+ data-size={size}
87
+ className={cn(
88
+ "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",
89
+ className
90
+ )}
91
+ {...props}
92
+ >
93
+ {children}
94
+ <SelectPrimitive.Icon
95
+ render={
96
+ <ChevronDownIcon className="pointer-events-none size-4 text-muted-foreground" />
97
+ }
98
+ />
99
+ </SelectPrimitive.Trigger>
100
+ )
101
+ }
102
+
103
+ function SelectContent({
104
+ className,
105
+ children,
106
+ side = "bottom",
107
+ sideOffset = 4,
108
+ align = "center",
109
+ alignOffset = 0,
110
+ alignItemWithTrigger = true,
111
+ ...props
112
+ }: SelectPrimitive.Popup.Props &
113
+ Pick<
114
+ SelectPrimitive.Positioner.Props,
115
+ "align" | "alignOffset" | "side" | "sideOffset" | "alignItemWithTrigger"
116
+ >) {
117
+ return (
118
+ <SelectPrimitive.Portal>
119
+ <SelectPrimitive.Positioner
120
+ side={side}
121
+ sideOffset={sideOffset}
122
+ align={align}
123
+ alignOffset={alignOffset}
124
+ alignItemWithTrigger={alignItemWithTrigger}
125
+ className="isolate z-50"
126
+ >
127
+ <SelectPrimitive.Popup
128
+ data-slot="select-content"
129
+ data-align-trigger={alignItemWithTrigger}
130
+ className={cn(
131
+ "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 data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
132
+ className
133
+ )}
134
+ {...props}
135
+ >
136
+ <SelectScrollUpButton />
137
+ <SelectPrimitive.List>{children}</SelectPrimitive.List>
138
+ <SelectScrollDownButton />
139
+ </SelectPrimitive.Popup>
140
+ </SelectPrimitive.Positioner>
141
+ </SelectPrimitive.Portal>
142
+ )
143
+ }
144
+
145
+ function SelectLabel({
146
+ className,
147
+ ...props
148
+ }: SelectPrimitive.GroupLabel.Props) {
149
+ return (
150
+ <SelectPrimitive.GroupLabel
151
+ data-slot="select-label"
152
+ className={cn("px-1.5 py-1 text-xs text-muted-foreground", className)}
153
+ {...props}
154
+ />
155
+ )
156
+ }
157
+
158
+ function SelectItem({
159
+ className,
160
+ children,
161
+ ...props
162
+ }: SelectPrimitive.Item.Props) {
163
+ return (
164
+ <SelectPrimitive.Item
165
+ data-slot="select-item"
166
+ className={cn(
167
+ "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",
168
+ className
169
+ )}
170
+ {...props}
171
+ >
172
+ {/*
173
+ `line-clamp-1` rather than `truncate`: the item's width is capped by the
174
+ popup (`--anchor-width`), and a label longer than that must stop before
175
+ the `pr-8` reserved for the check indicator. `text-overflow: ellipsis`
176
+ does not apply to a flex container's anonymous text, so it would clip
177
+ without an ellipsis; `-webkit-line-clamp` ellipsizes the text node.
178
+ */}
179
+ <SelectPrimitive.ItemText className="min-w-0 flex-1 line-clamp-1">
180
+ {children}
181
+ </SelectPrimitive.ItemText>
182
+ <SelectPrimitive.ItemIndicator
183
+ render={
184
+ <span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center" />
185
+ }
186
+ >
187
+ <CheckIcon className="pointer-events-none" />
188
+ </SelectPrimitive.ItemIndicator>
189
+ </SelectPrimitive.Item>
190
+ )
191
+ }
192
+
193
+ function SelectSeparator({
194
+ className,
195
+ ...props
196
+ }: SelectPrimitive.Separator.Props) {
197
+ return (
198
+ <SelectPrimitive.Separator
199
+ data-slot="select-separator"
200
+ className={cn("pointer-events-none -mx-1 my-1 h-px bg-border", className)}
201
+ {...props}
202
+ />
203
+ )
204
+ }
205
+
206
+ function SelectScrollUpButton({
207
+ className,
208
+ ...props
209
+ }: React.ComponentProps<typeof SelectPrimitive.ScrollUpArrow>) {
210
+ return (
211
+ <SelectPrimitive.ScrollUpArrow
212
+ data-slot="select-scroll-up-button"
213
+ className={cn(
214
+ "top-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
215
+ className
216
+ )}
217
+ {...props}
218
+ >
219
+ <ChevronUpIcon />
220
+ </SelectPrimitive.ScrollUpArrow>
221
+ )
222
+ }
223
+
224
+ function SelectScrollDownButton({
225
+ className,
226
+ ...props
227
+ }: React.ComponentProps<typeof SelectPrimitive.ScrollDownArrow>) {
228
+ return (
229
+ <SelectPrimitive.ScrollDownArrow
230
+ data-slot="select-scroll-down-button"
231
+ className={cn(
232
+ "bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
233
+ className
234
+ )}
235
+ {...props}
236
+ >
237
+ <ChevronDownIcon />
238
+ </SelectPrimitive.ScrollDownArrow>
239
+ )
240
+ }
241
+
242
+ export {
243
+ Select,
244
+ SelectContent,
245
+ SelectGroup,
246
+ SelectItem,
247
+ SelectLabel,
248
+ SelectScrollDownButton,
249
+ SelectScrollUpButton,
250
+ SelectSeparator,
251
+ SelectTrigger,
252
+ SelectValue,
253
+ }
@@ -0,0 +1,25 @@
1
+ // Copied by `vantage add ui separator`. This file is yours to edit.
2
+ // Generated from @squadbase/vantage src/ui/separator.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
3
+ import { Separator as SeparatorPrimitive } from "@base-ui/react/separator"
4
+
5
+ import { cn } from "@squadbase/vantage/ui"
6
+
7
+ function Separator({
8
+ className,
9
+ orientation = "horizontal",
10
+ ...props
11
+ }: SeparatorPrimitive.Props) {
12
+ return (
13
+ <SeparatorPrimitive
14
+ data-slot="separator"
15
+ orientation={orientation}
16
+ className={cn(
17
+ "shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
18
+ className
19
+ )}
20
+ {...props}
21
+ />
22
+ )
23
+ }
24
+
25
+ export { Separator }