@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,62 @@
1
+ // Copied by `vantage add ui metric-value`. This file is yours to edit.
2
+ // Generated from @squadbase/squadkit registry/components/metric-value.tsx by `npm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
+ "use client"
4
+
5
+ /**
6
+ * MetricValue / MetricUnit — the large number in a dashboard tile and its
7
+ * trailing unit, sized as a pair so the unit stays subordinate at every size.
8
+ *
9
+ * @deprecated KPI の数字は KpiCard を使う。KpiCard は書式(`format`、単位は
10
+ * `{ type: "number", unit: "社" }`)・比較ラベル・増減を値から計算するので、
11
+ * 手書きの数字と TrendIndicator の組み合わせのように食い違わない。
12
+ */
13
+ import { forwardRef, type HTMLAttributes } from "react"
14
+ import { cva, type VariantProps } from "class-variance-authority"
15
+
16
+ import { cn } from "@squadbase/vantage/ui"
17
+
18
+ export const metricValueVariants = cva("font-medium tracking-tight", {
19
+ variants: {
20
+ size: {
21
+ sm: "text-base",
22
+ md: "text-2xl",
23
+ lg: "text-3xl",
24
+ xl: "text-4xl",
25
+ },
26
+ },
27
+ defaultVariants: {
28
+ size: "md",
29
+ },
30
+ })
31
+
32
+ export interface MetricValueProps
33
+ extends HTMLAttributes<HTMLDivElement>,
34
+ VariantProps<typeof metricValueVariants> {}
35
+
36
+ /** @deprecated KPI の数字は KpiCard を使う。 */
37
+ export const MetricValue = forwardRef<HTMLDivElement, MetricValueProps>(
38
+ ({ className, size, ...props }, ref) => (
39
+ <div
40
+ ref={ref}
41
+ data-slot="metric-value"
42
+ className={cn("my-4", metricValueVariants({ size }), className)}
43
+ {...props}
44
+ />
45
+ ),
46
+ )
47
+ MetricValue.displayName = "MetricValue"
48
+
49
+ export interface MetricUnitProps extends HTMLAttributes<HTMLSpanElement> {}
50
+
51
+ /** @deprecated KPI の単位は KpiCard の `format`(`{ type: "number", unit: "社" }`)で付ける。 */
52
+ export const MetricUnit = forwardRef<HTMLSpanElement, MetricUnitProps>(
53
+ ({ className, ...props }, ref) => (
54
+ <span
55
+ ref={ref}
56
+ data-slot="metric-unit"
57
+ className={cn("text-sm font-normal text-muted-foreground", className)}
58
+ {...props}
59
+ />
60
+ ),
61
+ )
62
+ MetricUnit.displayName = "MetricUnit"
@@ -0,0 +1,232 @@
1
+ // Copied by `vantage add ui multi-select`. This file is yours to edit.
2
+ // Generated from @squadbase/squadkit registry/components/multi-select.tsx by `npm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
+ "use client"
4
+
5
+ /**
6
+ * MultiSelect — a multi-value combobox that shows the selection as removable
7
+ * badges. Past `maxCount` badges the rest collapse into a "+N件" counter.
8
+ * Pass `onSearch` to take filtering over yourself (server-side search).
9
+ */
10
+ import { forwardRef, useId, useMemo, useState } from "react"
11
+ import { ChevronDownIcon } from "lucide-react"
12
+
13
+ import { cn } from "@squadbase/vantage/ui"
14
+ import { Badge } 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 MultiSelectProps {
126
+ options: SelectOptions
127
+ value: string[]
128
+ onChange?: (value: string[]) => void
129
+ onSearch?: (query: string) => void
130
+ maxCount?: number
131
+ placeholder?: string
132
+ searchPlaceholder?: string
133
+ emptyText?: string
134
+ isLoading?: boolean
135
+ disabled?: 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
+ const NO_VALUES: string[] = []
142
+
143
+ export const MultiSelect = forwardRef<HTMLDivElement, MultiSelectProps>(function MultiSelect(
144
+ {
145
+ options = NO_OPTIONS,
146
+ value = NO_VALUES,
147
+ onChange,
148
+ onSearch,
149
+ maxCount = 3,
150
+ placeholder = "選択してください",
151
+ searchPlaceholder = "検索...",
152
+ emptyText = "見つかりませんでした",
153
+ isLoading = false,
154
+ disabled = false,
155
+ className,
156
+ },
157
+ ref,
158
+ ) {
159
+ const [open, setOpen] = useState(false)
160
+ const listId = useId()
161
+
162
+ const allOptions = useMemo(() => flattenOptions(options), [options])
163
+ const selectedOptions = useMemo(
164
+ () => allOptions.filter((opt) => value.includes(opt.value)),
165
+ [allOptions, value],
166
+ )
167
+
168
+ function handleSelect(selectedValue: string) {
169
+ if (value.includes(selectedValue)) {
170
+ onChange?.(value.filter((v) => v !== selectedValue))
171
+ } else {
172
+ onChange?.([...value, selectedValue])
173
+ }
174
+ }
175
+
176
+ const visibleBadges = selectedOptions.slice(0, maxCount)
177
+ const overflowCount = selectedOptions.length - maxCount
178
+
179
+ return (
180
+ <div ref={ref} data-slot="multi-select">
181
+ <Popover open={open} onOpenChange={setOpen}>
182
+ <PopoverTrigger
183
+ role="combobox"
184
+ aria-expanded={open}
185
+ aria-controls={listId}
186
+ disabled={disabled}
187
+ className={cn(
188
+ "inline-flex min-h-9 w-full items-center justify-between gap-2 rounded-md border bg-background px-3 py-1.5 text-sm shadow-xs transition-[color,background-color,border-color,box-shadow] outline-none hover:bg-accent hover:text-accent-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
189
+ className,
190
+ )}
191
+ >
192
+ <span className="flex min-w-0 flex-1 flex-wrap items-center gap-1">
193
+ {selectedOptions.length === 0 ? (
194
+ <span className="text-muted-foreground">{placeholder}</span>
195
+ ) : (
196
+ <>
197
+ {visibleBadges.map((option) => (
198
+ <Badge key={option.value} variant="secondary" className="shrink-0 gap-1 pr-1">
199
+ <span className="max-w-[120px] truncate">{option.label}</span>
200
+ <RemoveButton
201
+ label={`${option.label}を削除`}
202
+ onRemove={() => onChange?.(value.filter((v) => v !== option.value))}
203
+ iconClassName="size-3"
204
+ />
205
+ </Badge>
206
+ ))}
207
+ {overflowCount > 0 && (
208
+ <Badge variant="secondary" className="shrink-0">
209
+ +{overflowCount}件
210
+ </Badge>
211
+ )}
212
+ </>
213
+ )}
214
+ </span>
215
+ <ChevronDownIcon className="size-4 shrink-0 opacity-50" />
216
+ </PopoverTrigger>
217
+ <PopoverContent id={listId} className="w-(--anchor-width) p-0" align="start">
218
+ <OptionCommandList
219
+ options={options}
220
+ isSelected={(v) => value.includes(v)}
221
+ onSelect={handleSelect}
222
+ onSearch={onSearch}
223
+ isLoading={isLoading}
224
+ searchPlaceholder={searchPlaceholder}
225
+ emptyText={emptyText}
226
+ />
227
+ </PopoverContent>
228
+ </Popover>
229
+ </div>
230
+ )
231
+ })
232
+ MultiSelect.displayName = "MultiSelect"
@@ -0,0 +1,282 @@
1
+ // Copied by `vantage add ui page-shell`. This file is yours to edit.
2
+ // Generated from @squadbase/squadkit registry/components/page-shell.tsx by `npm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
+ "use client"
4
+
5
+ /**
6
+ * PageShell — the standard page frame: a bordered header band (title,
7
+ * description, meta, actions), an optional summary card row, and the content
8
+ * area. Composed from parts so a page can use only what it needs.
9
+ */
10
+ import { forwardRef, type HTMLAttributes, type ReactNode } from "react"
11
+ import { cva, type VariantProps } from "class-variance-authority"
12
+
13
+ import { formatDateRange, EMPTY_VALUE } from "@squadbase/vantage/components"
14
+ import { cn } from "@squadbase/vantage/ui"
15
+ import { Badge } from "@squadbase/vantage/ui"
16
+ import { UpdatedAt, type UpdatedAtProps } from "@squadbase/vantage/components"
17
+
18
+ export interface PageShellProps extends HTMLAttributes<HTMLDivElement> {}
19
+
20
+ export const PageShell = forwardRef<HTMLDivElement, PageShellProps>(
21
+ ({ className, ...props }, ref) => (
22
+ <div ref={ref} data-slot="page-shell" className={cn("flex flex-col", className)} {...props} />
23
+ ),
24
+ )
25
+ PageShell.displayName = "PageShell"
26
+
27
+ export interface PageShellHeaderProps extends HTMLAttributes<HTMLDivElement> {
28
+ /** When the data was last updated. Drawn by `UpdatedAt` at the right end of the title row. */
29
+ updatedAt?: UpdatedAtProps["at"]
30
+ /** `"relative"` (default) or `"absolute-first"` ("16:41 更新(23 分前)") for pages where "now" matters. */
31
+ updatedAtVariant?: UpdatedAtProps["variant"]
32
+ /** IANA time zone for the absolute time of `updatedAt`. Defaults to the runtime's zone. */
33
+ timeZone?: string
34
+ /** Where the data comes from. Shown as a chip "出典:○○" on the second row. */
35
+ source?: string
36
+ /** The period the data covers. Shown as a chip "2026/9/23〜10/20" on the second row. */
37
+ period?: { start: Date | string | number; end: Date | string | number }
38
+ /** The page's overall state (an alert, a status badge). Shown on the third row; no row when absent. */
39
+ status?: ReactNode
40
+ }
41
+
42
+ /**
43
+ * A wrapping flex row, not a two-column grid: when the heading cannot keep its
44
+ * 20rem basis next to `PageShellHeaderEnd`, the end block drops below it. A
45
+ * `1fr auto` grid would instead hand the heading whatever the end block leaves,
46
+ * and a CJK title — breakable between any two characters — collapses into a
47
+ * one-character column.
48
+ *
49
+ * The meta props (`updatedAt`, `source`, `period`, `status`) fix the order every
50
+ * page head shares: title and update time (right end) → source / period chips →
51
+ * overall status → summary cards. Empty rows are not drawn. Rows are placed with
52
+ * flex `order`, so they land before `PageShellSummary` whatever the child order.
53
+ */
54
+ export const PageShellHeader = forwardRef<HTMLDivElement, PageShellHeaderProps>(
55
+ ({ className, updatedAt, updatedAtVariant, timeZone, source, period, status, children, ...props }, ref) => {
56
+ const periodText = period ? formatDateRange(period.start, period.end, { timeZone }) : undefined
57
+ const hasChips = Boolean(source) || (periodText !== undefined && periodText !== EMPTY_VALUE)
58
+ const hasStatus = status !== undefined && status !== null && status !== false && status !== ""
59
+ return (
60
+ <div
61
+ ref={ref}
62
+ data-slot="page-shell-header"
63
+ className={cn("flex flex-wrap items-start gap-x-4 gap-y-6 px-6 py-12", className)}
64
+ {...props}
65
+ >
66
+ {children}
67
+ {updatedAt !== undefined && updatedAt !== null ? (
68
+ <div data-slot="page-shell-updated-at" className="ml-auto max-w-full shrink-0 text-right">
69
+ <UpdatedAt at={updatedAt} variant={updatedAtVariant} timeZone={timeZone} />
70
+ </div>
71
+ ) : null}
72
+ {hasChips ? (
73
+ <div data-slot="page-shell-chips" className="order-1 flex basis-full flex-wrap gap-1.5">
74
+ {source ? (
75
+ <Badge variant="outline" className="h-auto max-w-full whitespace-normal">
76
+ 出典:{source}
77
+ </Badge>
78
+ ) : null}
79
+ {periodText && periodText !== EMPTY_VALUE ? (
80
+ <Badge variant="outline" className="tabular-nums">
81
+ {periodText}
82
+ </Badge>
83
+ ) : null}
84
+ </div>
85
+ ) : null}
86
+ {hasStatus ? (
87
+ <div data-slot="page-shell-status" className="order-2 min-w-0 basis-full">
88
+ {status}
89
+ </div>
90
+ ) : null}
91
+ </div>
92
+ )
93
+ },
94
+ )
95
+ PageShellHeader.displayName = "PageShellHeader"
96
+
97
+ export interface PageShellHeadingProps extends HTMLAttributes<HTMLDivElement> {}
98
+
99
+ export const PageShellHeading = forwardRef<HTMLDivElement, PageShellHeadingProps>(
100
+ ({ className, ...props }, ref) => (
101
+ <div
102
+ ref={ref}
103
+ data-slot="page-shell-heading"
104
+ className={cn("min-w-0 grow basis-80", className)}
105
+ {...props}
106
+ />
107
+ ),
108
+ )
109
+ PageShellHeading.displayName = "PageShellHeading"
110
+
111
+ export interface PageShellTitleProps extends HTMLAttributes<HTMLHeadingElement> {}
112
+
113
+ export const PageShellTitle = forwardRef<HTMLHeadingElement, PageShellTitleProps>(
114
+ ({ className, ...props }, ref) => (
115
+ <h1
116
+ ref={ref}
117
+ data-slot="page-shell-title"
118
+ className={cn("text-2xl font-bold tracking-tight text-foreground", className)}
119
+ {...props}
120
+ />
121
+ ),
122
+ )
123
+ PageShellTitle.displayName = "PageShellTitle"
124
+
125
+ export interface PageShellDescriptionProps extends HTMLAttributes<HTMLParagraphElement> {}
126
+
127
+ export const PageShellDescription = forwardRef<HTMLParagraphElement, PageShellDescriptionProps>(
128
+ ({ className, ...props }, ref) => (
129
+ <p
130
+ ref={ref}
131
+ data-slot="page-shell-description"
132
+ className={cn("mt-1 text-sm text-muted-foreground", className)}
133
+ {...props}
134
+ />
135
+ ),
136
+ )
137
+ PageShellDescription.displayName = "PageShellDescription"
138
+
139
+ export interface PageShellHeaderEndProps extends HTMLAttributes<HTMLDivElement> {}
140
+
141
+ export const PageShellHeaderEnd = forwardRef<HTMLDivElement, PageShellHeaderEndProps>(
142
+ ({ className, ...props }, ref) => (
143
+ <div
144
+ ref={ref}
145
+ data-slot="page-shell-header-end"
146
+ className={cn("flex max-w-full shrink-0 flex-col flex-wrap items-end gap-2", className)}
147
+ {...props}
148
+ />
149
+ ),
150
+ )
151
+ PageShellHeaderEnd.displayName = "PageShellHeaderEnd"
152
+
153
+ export interface PageShellMetaProps extends HTMLAttributes<HTMLDivElement> {}
154
+
155
+ export const PageShellMeta = forwardRef<HTMLDivElement, PageShellMetaProps>(
156
+ ({ className, ...props }, ref) => (
157
+ <div
158
+ ref={ref}
159
+ data-slot="page-shell-meta"
160
+ className={cn("text-sm text-muted-foreground", className)}
161
+ {...props}
162
+ />
163
+ ),
164
+ )
165
+ PageShellMeta.displayName = "PageShellMeta"
166
+
167
+ export interface PageShellActionsProps extends HTMLAttributes<HTMLDivElement> {}
168
+
169
+ export const PageShellActions = forwardRef<HTMLDivElement, PageShellActionsProps>(
170
+ ({ className, ...props }, ref) => (
171
+ <div
172
+ ref={ref}
173
+ data-slot="page-shell-actions"
174
+ className={cn("flex items-center gap-2", className)}
175
+ {...props}
176
+ />
177
+ ),
178
+ )
179
+ PageShellActions.displayName = "PageShellActions"
180
+
181
+ export interface PageShellSummaryProps extends HTMLAttributes<HTMLDivElement> {}
182
+
183
+ export const PageShellSummary = forwardRef<HTMLDivElement, PageShellSummaryProps>(
184
+ ({ className, ...props }, ref) => (
185
+ <div
186
+ ref={ref}
187
+ data-slot="page-shell-summary"
188
+ className={cn(
189
+ "order-3 grid basis-full grid-cols-1 gap-3 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4",
190
+ className,
191
+ )}
192
+ {...props}
193
+ />
194
+ ),
195
+ )
196
+ PageShellSummary.displayName = "PageShellSummary"
197
+
198
+ /**
199
+ * Accent tints come from theme tokens, never Tailwind palette colours: the
200
+ * semantic tones (`--positive` / `--negative` / `--warning` / `--info` /
201
+ * `--neutral` and their `*-subtle`) and, for hues with no status meaning,
202
+ * `--chart-4` / `--chart-5`. The colour-named accents are kept for compatibility
203
+ * and map to the nearest tone. Each set is a literal string — Tailwind only
204
+ * emits classes it can see literally in the source.
205
+ */
206
+ const summaryCardTones = {
207
+ positive: "border-positive/30 bg-positive-subtle [&>svg]:text-positive",
208
+ negative: "border-negative/30 bg-negative-subtle [&>svg]:text-negative",
209
+ warning: "border-warning/30 bg-warning-subtle [&>svg]:text-warning",
210
+ info: "border-info/30 bg-info-subtle [&>svg]:text-info",
211
+ neutral: "border-neutral/30 bg-neutral-subtle [&>svg]:text-neutral",
212
+ violet: "border-chart-4/30 bg-chart-4/5 [&>svg]:text-chart-4",
213
+ pink: "border-chart-5/30 bg-chart-5/5 [&>svg]:text-chart-5",
214
+ } as const
215
+
216
+ export const pageShellSummaryCardVariants = cva(
217
+ "relative flex items-start gap-3 rounded-lg border bg-background px-4 py-3 text-sm [&>svg]:mt-0.5 [&>svg]:size-4 [&>svg]:shrink-0",
218
+ {
219
+ variants: {
220
+ accent: {
221
+ default: "text-card-foreground",
222
+ accent:
223
+ "border-[var(--chart-1)]/30 bg-linear-to-r from-[var(--chart-1)]/5 to-[var(--chart-1)]/5 [&>svg]:text-[var(--chart-1)]",
224
+ amber: summaryCardTones.warning,
225
+ blue: summaryCardTones.info,
226
+ emerald: summaryCardTones.positive,
227
+ red: summaryCardTones.negative,
228
+ violet: summaryCardTones.violet,
229
+ orange: summaryCardTones.warning,
230
+ cyan: summaryCardTones.info,
231
+ slate: summaryCardTones.neutral,
232
+ green: summaryCardTones.positive,
233
+ teal: summaryCardTones.positive,
234
+ sky: summaryCardTones.info,
235
+ indigo: summaryCardTones.info,
236
+ purple: summaryCardTones.violet,
237
+ pink: summaryCardTones.pink,
238
+ rose: summaryCardTones.negative,
239
+ yellow: summaryCardTones.warning,
240
+ lime: summaryCardTones.positive,
241
+ gray: summaryCardTones.neutral,
242
+ zinc: summaryCardTones.neutral,
243
+ neutral: summaryCardTones.neutral,
244
+ stone: summaryCardTones.neutral,
245
+ fuchsia: summaryCardTones.violet,
246
+ },
247
+ },
248
+ defaultVariants: {
249
+ accent: "default",
250
+ },
251
+ },
252
+ )
253
+
254
+ export interface PageShellSummaryCardProps
255
+ extends HTMLAttributes<HTMLDivElement>,
256
+ VariantProps<typeof pageShellSummaryCardVariants> {}
257
+
258
+ export const PageShellSummaryCard = forwardRef<HTMLDivElement, PageShellSummaryCardProps>(
259
+ ({ className, accent, ...props }, ref) => (
260
+ <div
261
+ ref={ref}
262
+ data-slot="page-shell-summary-card"
263
+ className={cn(pageShellSummaryCardVariants({ accent }), className)}
264
+ {...props}
265
+ />
266
+ ),
267
+ )
268
+ PageShellSummaryCard.displayName = "PageShellSummaryCard"
269
+
270
+ export interface PageShellContentProps extends HTMLAttributes<HTMLDivElement> {}
271
+
272
+ export const PageShellContent = forwardRef<HTMLDivElement, PageShellContentProps>(
273
+ ({ className, ...props }, ref) => (
274
+ <div
275
+ ref={ref}
276
+ data-slot="page-shell-content"
277
+ className={cn("flex-1 p-6", className)}
278
+ {...props}
279
+ />
280
+ ),
281
+ )
282
+ PageShellContent.displayName = "PageShellContent"