@squadbase/vantage 0.6.2-dev1.9d91dda → 0.6.2-dev1.ac99e02

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) 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-MFWRPYKW.js} +7 -7
  4. package/dist/{build-TVKFIVFQ.js.map → build-MFWRPYKW.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-P7W4LR3Z.js → chunk-7DZGJQ3P.js} +3 -3
  8. package/dist/{chunk-P7W4LR3Z.js.map → chunk-7DZGJQ3P.js.map} +1 -1
  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-NKFBFDRZ.js → chunk-AG6RZ2RR.js} +981 -14
  12. package/dist/chunk-AG6RZ2RR.js.map +1 -0
  13. package/dist/{chunk-QI3XQKHS.js → chunk-GNXCX4WE.js} +10 -3
  14. package/dist/chunk-GNXCX4WE.js.map +1 -0
  15. package/dist/chunk-IBGOCWJB.js +1716 -0
  16. package/dist/chunk-IBGOCWJB.js.map +1 -0
  17. package/dist/{chunk-RPZAALCX.js → chunk-OB6UBJXF.js} +153 -153
  18. package/dist/{chunk-RPZAALCX.js.map → chunk-OB6UBJXF.js.map} +1 -1
  19. package/dist/{chunk-LMUACBMA.js → chunk-QEWCYW3I.js} +43 -8
  20. package/dist/chunk-QEWCYW3I.js.map +1 -0
  21. package/dist/{chunk-ZVANIULZ.js → chunk-RNTDBX6S.js} +4 -4
  22. package/dist/{chunk-ZVANIULZ.js.map → chunk-RNTDBX6S.js.map} +1 -1
  23. package/dist/{chunk-PVTHHV7X.js → chunk-V3LF7BE7.js} +27 -5
  24. package/dist/chunk-V3LF7BE7.js.map +1 -0
  25. package/dist/{chunk-DTDVSFRY.js → chunk-XQUSGIDG.js} +17 -3
  26. package/dist/chunk-XQUSGIDG.js.map +1 -0
  27. package/dist/cli.js +12 -11
  28. package/dist/cli.js.map +1 -1
  29. package/dist/client/index.d.ts +32 -3
  30. package/dist/client/index.js +205 -12
  31. package/dist/client/index.js.map +1 -1
  32. package/dist/components/index.d.ts +216 -1
  33. package/dist/components/index.js +2 -2
  34. package/dist/{dev-GDKATCC7.js → dev-DCTOHQ6W.js} +8 -8
  35. package/dist/dev-DCTOHQ6W.js.map +1 -0
  36. package/dist/{docs-EJJ3BECT.js → docs-JVP4RPKT.js} +3 -3
  37. package/dist/{docs-EJJ3BECT.js.map → docs-JVP4RPKT.js.map} +1 -1
  38. package/dist/{doctor-WQXPXP6K.js → doctor-5E2VEZPK.js} +5 -5
  39. package/dist/{doctor-WQXPXP6K.js.map → doctor-5E2VEZPK.js.map} +1 -1
  40. package/dist/{preview-OGMCNQUO.js → preview-T6CLSQVX.js} +3 -3
  41. package/dist/{preview-OGMCNQUO.js.map → preview-T6CLSQVX.js.map} +1 -1
  42. package/dist/query/index.d.ts +0 -11
  43. package/dist/query/index.js +2 -2
  44. package/dist/{routes-MWBWWOJ2.js → routes-3SGAFDMC.js} +4 -4
  45. package/dist/{routes-MWBWWOJ2.js.map → routes-3SGAFDMC.js.map} +1 -1
  46. package/dist/{search-6Y422SV6.js → search-5LC4XPB5.js} +3 -3
  47. package/dist/{search-6Y422SV6.js.map → search-5LC4XPB5.js.map} +1 -1
  48. package/dist/table/index.js +7 -6
  49. package/dist/table/index.js.map +1 -1
  50. package/dist/ui/index.d.ts +140 -10
  51. package/dist/ui/index.js +1405 -63
  52. package/dist/ui/index.js.map +1 -1
  53. package/dist/{upgrade-HCANX4GQ.js → upgrade-DBBE6GIR.js} +6 -6
  54. package/dist/{upgrade-HCANX4GQ.js.map → upgrade-DBBE6GIR.js.map} +1 -1
  55. package/dist/vite/index.d.ts +10 -0
  56. package/dist/vite/index.js +3 -3
  57. package/docs/index.json +291 -61
  58. package/docs/ja/changelog.md +26 -0
  59. package/docs/ja/components.md +33 -89
  60. package/docs/ja/page-types/csv.md +83 -0
  61. package/docs/ja/page-types/doc.md +83 -0
  62. package/docs/ja/page-types/excel.md +82 -0
  63. package/docs/ja/page-types/slide.md +132 -0
  64. package/docs/ja/page-types.md +47 -0
  65. package/docs/ja/parts/area-chart.md +61 -0
  66. package/docs/ja/parts/bar-chart.md +69 -0
  67. package/docs/ja/parts/bar-list.md +35 -0
  68. package/docs/ja/parts/category-bar.md +36 -0
  69. package/docs/ja/parts/donut-chart.md +47 -0
  70. package/docs/ja/parts/echart.md +3 -0
  71. package/docs/ja/parts/line-chart.md +58 -0
  72. package/docs/ja/parts/progress-circle.md +34 -0
  73. package/docs/ja/parts/sparkline.md +1 -1
  74. package/docs/ja/parts/tracker.md +32 -0
  75. package/docs/ja/routing.md +8 -86
  76. package/docs/ja/ui/alert-dialog.md +41 -0
  77. package/docs/ja/ui/avatar.md +31 -0
  78. package/docs/ja/ui/combobox.md +74 -0
  79. package/docs/ja/ui/drawer.md +41 -0
  80. package/docs/ja/ui/field.md +42 -0
  81. package/docs/ja/ui/hover-card.md +21 -0
  82. package/docs/ja/ui/kbd.md +16 -0
  83. package/docs/ja/ui/pagination.md +36 -0
  84. package/docs/ja/ui/radio-group.md +21 -0
  85. package/docs/ja/ui/toast.md +46 -0
  86. package/docs/ja/ui-and-theming.md +14 -7
  87. package/package.json +21 -13
  88. package/registry/ui/accordion.tsx +74 -0
  89. package/registry/ui/alert-dialog.tsx +189 -0
  90. package/registry/ui/alert.tsx +78 -0
  91. package/registry/ui/app-shell.tsx +281 -0
  92. package/registry/ui/area-chart.tsx +1015 -0
  93. package/registry/ui/avatar.tsx +110 -0
  94. package/registry/ui/badge.tsx +54 -0
  95. package/registry/ui/bar-chart.tsx +1013 -0
  96. package/registry/ui/bar-list.tsx +302 -0
  97. package/registry/ui/breadcrumb.tsx +126 -0
  98. package/registry/ui/button.tsx +60 -0
  99. package/registry/ui/calendar.tsx +221 -0
  100. package/registry/ui/card.tsx +105 -0
  101. package/registry/ui/category-bar.tsx +281 -0
  102. package/registry/ui/checkbox.tsx +28 -0
  103. package/registry/ui/collapsible.tsx +21 -0
  104. package/registry/ui/combobox.tsx +310 -0
  105. package/registry/ui/command.tsx +196 -0
  106. package/registry/ui/dashboard-card.tsx +174 -0
  107. package/registry/ui/data-table.tsx +16 -91
  108. package/registry/ui/date-range-picker.tsx +209 -0
  109. package/registry/ui/dialog.tsx +159 -0
  110. package/registry/ui/donut-chart.tsx +1004 -0
  111. package/registry/ui/drawer.tsx +229 -0
  112. package/registry/ui/dropdown-menu.tsx +272 -0
  113. package/registry/ui/echart.tsx +486 -0
  114. package/registry/ui/field.tsx +240 -0
  115. package/registry/ui/filter-bar.tsx +365 -0
  116. package/registry/ui/funnel-steps.tsx +274 -0
  117. package/registry/ui/hover-card.tsx +52 -0
  118. package/registry/ui/input-group.tsx +158 -0
  119. package/registry/ui/input.tsx +22 -0
  120. package/registry/ui/kbd.tsx +28 -0
  121. package/registry/ui/label.tsx +20 -0
  122. package/registry/ui/line-chart.tsx +1007 -0
  123. package/registry/ui/metric-value.tsx +54 -0
  124. package/registry/ui/multi-select.tsx +226 -0
  125. package/registry/ui/page-shell.tsx +227 -0
  126. package/registry/ui/pagination.tsx +138 -0
  127. package/registry/ui/placeholder.tsx +30 -0
  128. package/registry/ui/popover.tsx +90 -0
  129. package/registry/ui/progress-circle.tsx +272 -0
  130. package/registry/ui/progress.tsx +83 -0
  131. package/registry/ui/radio-group.tsx +39 -0
  132. package/registry/ui/scroll-area.tsx +55 -0
  133. package/registry/ui/searchable-select.tsx +209 -0
  134. package/registry/ui/section-header.tsx +41 -0
  135. package/registry/ui/segmented-control.tsx +71 -0
  136. package/registry/ui/select.tsx +253 -0
  137. package/registry/ui/separator.tsx +25 -0
  138. package/registry/ui/sheet.tsx +137 -0
  139. package/registry/ui/sidebar.tsx +723 -0
  140. package/registry/ui/skeleton.tsx +15 -0
  141. package/registry/ui/slider.tsx +54 -0
  142. package/registry/ui/sparkline.tsx +155 -0
  143. package/registry/ui/spinner.tsx +12 -0
  144. package/registry/ui/states.tsx +61 -0
  145. package/registry/ui/status-badge.tsx +79 -0
  146. package/registry/ui/switch.tsx +32 -0
  147. package/registry/ui/table.tsx +116 -0
  148. package/registry/ui/tabs.tsx +82 -0
  149. package/registry/ui/textarea.tsx +20 -0
  150. package/registry/ui/toast.tsx +250 -0
  151. package/registry/ui/toggle-group.tsx +89 -0
  152. package/registry/ui/toggle.tsx +45 -0
  153. package/registry/ui/tooltip.tsx +66 -0
  154. package/registry/ui/tracker.tsx +236 -0
  155. package/registry/ui/trend-indicator.tsx +62 -0
  156. package/skills/vantage-add-feature/SKILL.md +9 -7
  157. package/skills/vantage-pitfalls/SKILL.md +5 -1
  158. package/templates/AGENTS.md +10 -1
  159. package/templates/agents/csv.md +2 -1
  160. package/templates/agents/doc.md +4 -3
  161. package/templates/agents/page.md +22 -8
  162. package/templates/agents/xlsx.md +2 -1
  163. package/dist/add-DY4AXANK.js +0 -6
  164. package/dist/add-DY4AXANK.js.map +0 -1
  165. package/dist/chunk-DTDVSFRY.js.map +0 -1
  166. package/dist/chunk-LMUACBMA.js.map +0 -1
  167. package/dist/chunk-NKFBFDRZ.js.map +0 -1
  168. package/dist/chunk-PVTHHV7X.js.map +0 -1
  169. package/dist/chunk-QI3XQKHS.js.map +0 -1
  170. package/dist/chunk-VA776YE5.js +0 -316
  171. package/dist/chunk-VA776YE5.js.map +0 -1
  172. package/dist/dev-GDKATCC7.js.map +0 -1
@@ -0,0 +1,54 @@
1
+ // Copied by `vantage add ui metric-value`. This file is yours to edit.
2
+ // Generated from @squadbase/vantage src/components/metric-value.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
3
+ /**
4
+ * MetricValue / MetricUnit — the large number in a dashboard tile and its
5
+ * trailing unit, sized as a pair so the unit stays subordinate at every size.
6
+ */
7
+ import { forwardRef, type HTMLAttributes } from "react"
8
+ import { cva, type VariantProps } from "class-variance-authority"
9
+
10
+ import { cn } from "@squadbase/vantage/ui"
11
+
12
+ export const metricValueVariants = cva("font-medium tracking-tight", {
13
+ variants: {
14
+ size: {
15
+ sm: "text-base",
16
+ md: "text-2xl",
17
+ lg: "text-3xl",
18
+ xl: "text-4xl",
19
+ },
20
+ },
21
+ defaultVariants: {
22
+ size: "md",
23
+ },
24
+ })
25
+
26
+ export interface MetricValueProps
27
+ extends HTMLAttributes<HTMLDivElement>,
28
+ VariantProps<typeof metricValueVariants> {}
29
+
30
+ export const MetricValue = forwardRef<HTMLDivElement, MetricValueProps>(
31
+ ({ className, size, ...props }, ref) => (
32
+ <div
33
+ ref={ref}
34
+ data-slot="metric-value"
35
+ className={cn("my-4", metricValueVariants({ size }), className)}
36
+ {...props}
37
+ />
38
+ ),
39
+ )
40
+ MetricValue.displayName = "MetricValue"
41
+
42
+ export interface MetricUnitProps extends HTMLAttributes<HTMLSpanElement> {}
43
+
44
+ export const MetricUnit = forwardRef<HTMLSpanElement, MetricUnitProps>(
45
+ ({ className, ...props }, ref) => (
46
+ <span
47
+ ref={ref}
48
+ data-slot="metric-unit"
49
+ className={cn("text-sm font-normal text-muted-foreground", className)}
50
+ {...props}
51
+ />
52
+ ),
53
+ )
54
+ MetricUnit.displayName = "MetricUnit"
@@ -0,0 +1,226 @@
1
+ // Copied by `vantage add ui multi-select`. This file is yours to edit.
2
+ // Generated from @squadbase/vantage src/components/multi-select.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
3
+ /**
4
+ * MultiSelect — a multi-value combobox that shows the selection as removable
5
+ * badges. Past `maxCount` badges the rest collapse into a "+N件" counter.
6
+ * Pass `onSearch` to take filtering over yourself (server-side search).
7
+ */
8
+ import { forwardRef, useId, useMemo, useState } from "react"
9
+ import { ChevronDownIcon } from "lucide-react"
10
+
11
+ import { cn } from "@squadbase/vantage/ui"
12
+ import { Badge } 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 MultiSelectProps {
124
+ options: SelectOptions
125
+ value: string[]
126
+ onChange?: (value: string[]) => void
127
+ onSearch?: (query: string) => void
128
+ maxCount?: number
129
+ placeholder?: string
130
+ searchPlaceholder?: string
131
+ emptyText?: string
132
+ isLoading?: boolean
133
+ disabled?: boolean
134
+ className?: string
135
+ }
136
+
137
+ export const MultiSelect = forwardRef<HTMLDivElement, MultiSelectProps>(function MultiSelect(
138
+ {
139
+ options,
140
+ value,
141
+ onChange,
142
+ onSearch,
143
+ maxCount = 3,
144
+ placeholder = "選択してください",
145
+ searchPlaceholder = "検索...",
146
+ emptyText = "見つかりませんでした",
147
+ isLoading = false,
148
+ disabled = false,
149
+ className,
150
+ },
151
+ ref,
152
+ ) {
153
+ const [open, setOpen] = useState(false)
154
+ const listId = useId()
155
+
156
+ const allOptions = useMemo(() => flattenOptions(options), [options])
157
+ const selectedOptions = useMemo(
158
+ () => allOptions.filter((opt) => value.includes(opt.value)),
159
+ [allOptions, value],
160
+ )
161
+
162
+ function handleSelect(selectedValue: string) {
163
+ if (value.includes(selectedValue)) {
164
+ onChange?.(value.filter((v) => v !== selectedValue))
165
+ } else {
166
+ onChange?.([...value, selectedValue])
167
+ }
168
+ }
169
+
170
+ const visibleBadges = selectedOptions.slice(0, maxCount)
171
+ const overflowCount = selectedOptions.length - maxCount
172
+
173
+ return (
174
+ <div ref={ref} data-slot="multi-select">
175
+ <Popover open={open} onOpenChange={setOpen}>
176
+ <PopoverTrigger
177
+ role="combobox"
178
+ aria-expanded={open}
179
+ aria-controls={listId}
180
+ disabled={disabled}
181
+ className={cn(
182
+ "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-all 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",
183
+ className,
184
+ )}
185
+ >
186
+ <span className="flex min-w-0 flex-1 flex-wrap items-center gap-1">
187
+ {selectedOptions.length === 0 ? (
188
+ <span className="text-muted-foreground">{placeholder}</span>
189
+ ) : (
190
+ <>
191
+ {visibleBadges.map((option) => (
192
+ <Badge key={option.value} variant="secondary" className="shrink-0 gap-1 pr-1">
193
+ <span className="max-w-[120px] truncate">{option.label}</span>
194
+ <RemoveButton
195
+ label={`${option.label}を削除`}
196
+ onRemove={() => onChange?.(value.filter((v) => v !== option.value))}
197
+ iconClassName="size-3"
198
+ />
199
+ </Badge>
200
+ ))}
201
+ {overflowCount > 0 && (
202
+ <Badge variant="secondary" className="shrink-0">
203
+ +{overflowCount}件
204
+ </Badge>
205
+ )}
206
+ </>
207
+ )}
208
+ </span>
209
+ <ChevronDownIcon className="size-4 shrink-0 opacity-50" />
210
+ </PopoverTrigger>
211
+ <PopoverContent id={listId} className="w-(--anchor-width) p-0" align="start">
212
+ <OptionCommandList
213
+ options={options}
214
+ isSelected={(v) => value.includes(v)}
215
+ onSelect={handleSelect}
216
+ onSearch={onSearch}
217
+ isLoading={isLoading}
218
+ searchPlaceholder={searchPlaceholder}
219
+ emptyText={emptyText}
220
+ />
221
+ </PopoverContent>
222
+ </Popover>
223
+ </div>
224
+ )
225
+ })
226
+ MultiSelect.displayName = "MultiSelect"
@@ -0,0 +1,227 @@
1
+ // Copied by `vantage add ui page-shell`. This file is yours to edit.
2
+ // Generated from @squadbase/vantage src/components/page-shell.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
3
+ /**
4
+ * PageShell — the standard page frame: a bordered header band (title,
5
+ * description, meta, actions), an optional summary card row, and the content
6
+ * area. Composed from parts so a page can use only what it needs.
7
+ */
8
+ import { forwardRef, type HTMLAttributes } from "react"
9
+ import { cva, type VariantProps } from "class-variance-authority"
10
+
11
+ import { cn } from "@squadbase/vantage/ui"
12
+
13
+ export interface PageShellProps extends HTMLAttributes<HTMLDivElement> {}
14
+
15
+ export const PageShell = forwardRef<HTMLDivElement, PageShellProps>(
16
+ ({ className, ...props }, ref) => (
17
+ <div ref={ref} data-slot="page-shell" className={cn("flex flex-col", className)} {...props} />
18
+ ),
19
+ )
20
+ PageShell.displayName = "PageShell"
21
+
22
+ export interface PageShellHeaderProps extends HTMLAttributes<HTMLDivElement> {}
23
+
24
+ /**
25
+ * A wrapping flex row, not a two-column grid: when the heading cannot keep its
26
+ * 20rem basis next to `PageShellHeaderEnd`, the end block drops below it. A
27
+ * `1fr auto` grid would instead hand the heading whatever the end block leaves,
28
+ * and a CJK title — breakable between any two characters — collapses into a
29
+ * one-character column.
30
+ */
31
+ export const PageShellHeader = forwardRef<HTMLDivElement, PageShellHeaderProps>(
32
+ ({ className, ...props }, ref) => (
33
+ <div
34
+ ref={ref}
35
+ data-slot="page-shell-header"
36
+ className={cn("flex flex-wrap items-start gap-x-4 gap-y-6 border-b px-6 py-12", className)}
37
+ {...props}
38
+ />
39
+ ),
40
+ )
41
+ PageShellHeader.displayName = "PageShellHeader"
42
+
43
+ export interface PageShellHeadingProps extends HTMLAttributes<HTMLDivElement> {}
44
+
45
+ export const PageShellHeading = forwardRef<HTMLDivElement, PageShellHeadingProps>(
46
+ ({ className, ...props }, ref) => (
47
+ <div
48
+ ref={ref}
49
+ data-slot="page-shell-heading"
50
+ className={cn("min-w-0 grow basis-80", className)}
51
+ {...props}
52
+ />
53
+ ),
54
+ )
55
+ PageShellHeading.displayName = "PageShellHeading"
56
+
57
+ export interface PageShellTitleProps extends HTMLAttributes<HTMLHeadingElement> {}
58
+
59
+ export const PageShellTitle = forwardRef<HTMLHeadingElement, PageShellTitleProps>(
60
+ ({ className, ...props }, ref) => (
61
+ <h1
62
+ ref={ref}
63
+ data-slot="page-shell-title"
64
+ className={cn("text-2xl font-bold tracking-tight text-foreground", className)}
65
+ {...props}
66
+ />
67
+ ),
68
+ )
69
+ PageShellTitle.displayName = "PageShellTitle"
70
+
71
+ export interface PageShellDescriptionProps extends HTMLAttributes<HTMLParagraphElement> {}
72
+
73
+ export const PageShellDescription = forwardRef<HTMLParagraphElement, PageShellDescriptionProps>(
74
+ ({ className, ...props }, ref) => (
75
+ <p
76
+ ref={ref}
77
+ data-slot="page-shell-description"
78
+ className={cn("mt-1 text-sm text-muted-foreground", className)}
79
+ {...props}
80
+ />
81
+ ),
82
+ )
83
+ PageShellDescription.displayName = "PageShellDescription"
84
+
85
+ export interface PageShellHeaderEndProps extends HTMLAttributes<HTMLDivElement> {}
86
+
87
+ export const PageShellHeaderEnd = forwardRef<HTMLDivElement, PageShellHeaderEndProps>(
88
+ ({ className, ...props }, ref) => (
89
+ <div
90
+ ref={ref}
91
+ data-slot="page-shell-header-end"
92
+ className={cn("flex max-w-full shrink-0 flex-col flex-wrap items-end gap-2", className)}
93
+ {...props}
94
+ />
95
+ ),
96
+ )
97
+ PageShellHeaderEnd.displayName = "PageShellHeaderEnd"
98
+
99
+ export interface PageShellMetaProps extends HTMLAttributes<HTMLDivElement> {}
100
+
101
+ export const PageShellMeta = forwardRef<HTMLDivElement, PageShellMetaProps>(
102
+ ({ className, ...props }, ref) => (
103
+ <div
104
+ ref={ref}
105
+ data-slot="page-shell-meta"
106
+ className={cn("text-sm text-muted-foreground", className)}
107
+ {...props}
108
+ />
109
+ ),
110
+ )
111
+ PageShellMeta.displayName = "PageShellMeta"
112
+
113
+ export interface PageShellActionsProps extends HTMLAttributes<HTMLDivElement> {}
114
+
115
+ export const PageShellActions = forwardRef<HTMLDivElement, PageShellActionsProps>(
116
+ ({ className, ...props }, ref) => (
117
+ <div
118
+ ref={ref}
119
+ data-slot="page-shell-actions"
120
+ className={cn("flex items-center gap-2", className)}
121
+ {...props}
122
+ />
123
+ ),
124
+ )
125
+ PageShellActions.displayName = "PageShellActions"
126
+
127
+ export interface PageShellSummaryProps extends HTMLAttributes<HTMLDivElement> {}
128
+
129
+ export const PageShellSummary = forwardRef<HTMLDivElement, PageShellSummaryProps>(
130
+ ({ className, ...props }, ref) => (
131
+ <div
132
+ ref={ref}
133
+ data-slot="page-shell-summary"
134
+ className={cn(
135
+ "grid basis-full grid-cols-1 gap-3 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4",
136
+ className,
137
+ )}
138
+ {...props}
139
+ />
140
+ ),
141
+ )
142
+ PageShellSummary.displayName = "PageShellSummary"
143
+
144
+ /**
145
+ * Accent colours are spelled out one utility set per colour rather than built
146
+ * from a template string — Tailwind only emits classes it can see literally in
147
+ * the source.
148
+ */
149
+ export const pageShellSummaryCardVariants = cva(
150
+ "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",
151
+ {
152
+ variants: {
153
+ accent: {
154
+ default: "text-card-foreground",
155
+ accent:
156
+ "border-[var(--chart-1)]/30 bg-linear-to-r from-[var(--chart-1)]/5 to-[var(--chart-1)]/5 [&>svg]:text-[var(--chart-1)]",
157
+ amber:
158
+ "border-amber-300 bg-amber-50 [&>svg]:text-amber-600 dark:border-amber-800 dark:bg-amber-950/50 dark:[&>svg]:text-amber-400",
159
+ blue: "border-blue-300 bg-blue-50 [&>svg]:text-blue-600 dark:border-blue-800 dark:bg-blue-950/50 dark:[&>svg]:text-blue-400",
160
+ emerald:
161
+ "border-emerald-300 bg-emerald-50 [&>svg]:text-emerald-600 dark:border-emerald-800 dark:bg-emerald-950/50 dark:[&>svg]:text-emerald-400",
162
+ red: "border-red-300 bg-red-50 [&>svg]:text-red-600 dark:border-red-800 dark:bg-red-950/50 dark:[&>svg]:text-red-400",
163
+ violet:
164
+ "border-violet-300 bg-violet-50 [&>svg]:text-violet-600 dark:border-violet-800 dark:bg-violet-950/50 dark:[&>svg]:text-violet-400",
165
+ orange:
166
+ "border-orange-300 bg-orange-50 [&>svg]:text-orange-600 dark:border-orange-800 dark:bg-orange-950/50 dark:[&>svg]:text-orange-400",
167
+ cyan: "border-cyan-300 bg-cyan-50 [&>svg]:text-cyan-600 dark:border-cyan-800 dark:bg-cyan-950/50 dark:[&>svg]:text-cyan-400",
168
+ slate:
169
+ "border-slate-300 bg-slate-50 [&>svg]:text-slate-600 dark:border-slate-800 dark:bg-slate-950/50 dark:[&>svg]:text-slate-400",
170
+ green:
171
+ "border-green-300 bg-green-50 [&>svg]:text-green-600 dark:border-green-800 dark:bg-green-950/50 dark:[&>svg]:text-green-400",
172
+ teal: "border-teal-300 bg-teal-50 [&>svg]:text-teal-600 dark:border-teal-800 dark:bg-teal-950/50 dark:[&>svg]:text-teal-400",
173
+ sky: "border-sky-300 bg-sky-50 [&>svg]:text-sky-600 dark:border-sky-800 dark:bg-sky-950/50 dark:[&>svg]:text-sky-400",
174
+ indigo:
175
+ "border-indigo-300 bg-indigo-50 [&>svg]:text-indigo-600 dark:border-indigo-800 dark:bg-indigo-950/50 dark:[&>svg]:text-indigo-400",
176
+ purple:
177
+ "border-purple-300 bg-purple-50 [&>svg]:text-purple-600 dark:border-purple-800 dark:bg-purple-950/50 dark:[&>svg]:text-purple-400",
178
+ pink: "border-pink-300 bg-pink-50 [&>svg]:text-pink-600 dark:border-pink-800 dark:bg-pink-950/50 dark:[&>svg]:text-pink-400",
179
+ rose: "border-rose-300 bg-rose-50 [&>svg]:text-rose-600 dark:border-rose-800 dark:bg-rose-950/50 dark:[&>svg]:text-rose-400",
180
+ yellow:
181
+ "border-yellow-300 bg-yellow-50 [&>svg]:text-yellow-600 dark:border-yellow-800 dark:bg-yellow-950/50 dark:[&>svg]:text-yellow-400",
182
+ lime: "border-lime-300 bg-lime-50 [&>svg]:text-lime-600 dark:border-lime-800 dark:bg-lime-950/50 dark:[&>svg]:text-lime-400",
183
+ gray: "border-gray-300 bg-gray-50 [&>svg]:text-gray-600 dark:border-gray-800 dark:bg-gray-950/50 dark:[&>svg]:text-gray-400",
184
+ zinc: "border-zinc-300 bg-zinc-50 [&>svg]:text-zinc-600 dark:border-zinc-800 dark:bg-zinc-950/50 dark:[&>svg]:text-zinc-400",
185
+ neutral:
186
+ "border-neutral-300 bg-neutral-50 [&>svg]:text-neutral-600 dark:border-neutral-800 dark:bg-neutral-950/50 dark:[&>svg]:text-neutral-400",
187
+ stone:
188
+ "border-stone-300 bg-stone-50 [&>svg]:text-stone-600 dark:border-stone-800 dark:bg-stone-950/50 dark:[&>svg]:text-stone-400",
189
+ fuchsia:
190
+ "border-fuchsia-300 bg-fuchsia-50 [&>svg]:text-fuchsia-600 dark:border-fuchsia-800 dark:bg-fuchsia-950/50 dark:[&>svg]:text-fuchsia-400",
191
+ },
192
+ },
193
+ defaultVariants: {
194
+ accent: "default",
195
+ },
196
+ },
197
+ )
198
+
199
+ export interface PageShellSummaryCardProps
200
+ extends HTMLAttributes<HTMLDivElement>,
201
+ VariantProps<typeof pageShellSummaryCardVariants> {}
202
+
203
+ export const PageShellSummaryCard = forwardRef<HTMLDivElement, PageShellSummaryCardProps>(
204
+ ({ className, accent, ...props }, ref) => (
205
+ <div
206
+ ref={ref}
207
+ data-slot="page-shell-summary-card"
208
+ className={cn(pageShellSummaryCardVariants({ accent }), className)}
209
+ {...props}
210
+ />
211
+ ),
212
+ )
213
+ PageShellSummaryCard.displayName = "PageShellSummaryCard"
214
+
215
+ export interface PageShellContentProps extends HTMLAttributes<HTMLDivElement> {}
216
+
217
+ export const PageShellContent = forwardRef<HTMLDivElement, PageShellContentProps>(
218
+ ({ className, ...props }, ref) => (
219
+ <div
220
+ ref={ref}
221
+ data-slot="page-shell-content"
222
+ className={cn("flex-1 p-6", className)}
223
+ {...props}
224
+ />
225
+ ),
226
+ )
227
+ PageShellContent.displayName = "PageShellContent"
@@ -0,0 +1,138 @@
1
+ // Copied by `vantage add ui pagination`. This file is yours to edit.
2
+ // Generated from @squadbase/vantage src/ui/pagination.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
3
+ import * as React from "react"
4
+ import { cn } from "@squadbase/vantage/ui"
5
+
6
+ import { Button } from "@squadbase/vantage/ui"
7
+ import { ChevronLeftIcon, ChevronRightIcon, MoreHorizontalIcon } from "lucide-react"
8
+
9
+ function Pagination({ className, ...props }: React.ComponentProps<"nav">) {
10
+ return (
11
+ <nav
12
+ role="navigation"
13
+ aria-label="pagination"
14
+ data-slot="pagination"
15
+ className={cn("mx-auto flex w-full justify-center", className)}
16
+ {...props}
17
+ />
18
+ )
19
+ }
20
+
21
+ function PaginationContent({
22
+ className,
23
+ ...props
24
+ }: React.ComponentProps<"ul">) {
25
+ return (
26
+ <ul
27
+ data-slot="pagination-content"
28
+ className={cn("flex items-center gap-0.5", className)}
29
+ {...props}
30
+ />
31
+ )
32
+ }
33
+
34
+ function PaginationItem({ ...props }: React.ComponentProps<"li">) {
35
+ return <li data-slot="pagination-item" {...props} />
36
+ }
37
+
38
+ type PaginationLinkProps = {
39
+ isActive?: boolean
40
+ } & Pick<React.ComponentProps<typeof Button>, "size"> &
41
+ React.ComponentProps<"a">
42
+
43
+ function PaginationLink({
44
+ className,
45
+ isActive,
46
+ size = "icon",
47
+ ...props
48
+ }: PaginationLinkProps) {
49
+ return (
50
+ <Button
51
+ variant={isActive ? "outline" : "ghost"}
52
+ size={size}
53
+ className={cn(className)}
54
+ nativeButton={false}
55
+ render={
56
+ <a
57
+ aria-current={isActive ? "page" : undefined}
58
+ data-slot="pagination-link"
59
+ data-active={isActive}
60
+ {...props}
61
+ />
62
+ }
63
+ />
64
+ )
65
+ }
66
+
67
+ function PaginationPrevious({
68
+ className,
69
+ text = "Previous",
70
+ ...props
71
+ }: React.ComponentProps<typeof PaginationLink> & { text?: string }) {
72
+ return (
73
+ <PaginationLink
74
+ aria-label="Go to previous page"
75
+ size="default"
76
+ className={cn("pl-1.5!", className)}
77
+ {...props}
78
+ >
79
+ <ChevronLeftIcon
80
+ data-icon="inline-start"
81
+ className="cn-rtl-flip"
82
+ />
83
+ <span className="hidden sm:block">{text}</span>
84
+ </PaginationLink>
85
+ )
86
+ }
87
+
88
+ function PaginationNext({
89
+ className,
90
+ text = "Next",
91
+ ...props
92
+ }: React.ComponentProps<typeof PaginationLink> & { text?: string }) {
93
+ return (
94
+ <PaginationLink
95
+ aria-label="Go to next page"
96
+ size="default"
97
+ className={cn("pr-1.5!", className)}
98
+ {...props}
99
+ >
100
+ <span className="hidden sm:block">{text}</span>
101
+ <ChevronRightIcon
102
+ data-icon="inline-end"
103
+ className="cn-rtl-flip"
104
+ />
105
+ </PaginationLink>
106
+ )
107
+ }
108
+
109
+ function PaginationEllipsis({
110
+ className,
111
+ ...props
112
+ }: React.ComponentProps<"span">) {
113
+ return (
114
+ <span
115
+ aria-hidden
116
+ data-slot="pagination-ellipsis"
117
+ className={cn(
118
+ "flex size-8 items-center justify-center [&_svg:not([class*='size-'])]:size-4",
119
+ className
120
+ )}
121
+ {...props}
122
+ >
123
+ <MoreHorizontalIcon
124
+ />
125
+ <span className="sr-only">More pages</span>
126
+ </span>
127
+ )
128
+ }
129
+
130
+ export {
131
+ Pagination,
132
+ PaginationContent,
133
+ PaginationEllipsis,
134
+ PaginationItem,
135
+ PaginationLink,
136
+ PaginationNext,
137
+ PaginationPrevious,
138
+ }
@@ -0,0 +1,30 @@
1
+ // Copied by `vantage add ui placeholder`. This file is yours to edit.
2
+ // Generated from @squadbase/vantage src/components/placeholder.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
3
+ /**
4
+ * Placeholder — marks sample (not-yet-real) data on a template screen.
5
+ *
6
+ * Renders an inline `<span>`, so it is safe inside headings and descriptions
7
+ * (unlike `Skeleton`, which is a block `<div>`). The `animate-pulse` + muted
8
+ * styling signals "this is placeholder data — swap in your real values".
9
+ * Override size/weight through `className` (e.g. `text-2xl font-bold`).
10
+ */
11
+ import { forwardRef, type HTMLAttributes } from "react"
12
+
13
+ import { cn } from "@squadbase/vantage/ui"
14
+
15
+ export interface PlaceholderProps extends HTMLAttributes<HTMLSpanElement> {}
16
+
17
+ export const Placeholder = forwardRef<HTMLSpanElement, PlaceholderProps>(function Placeholder(
18
+ { className, ...props },
19
+ ref,
20
+ ) {
21
+ return (
22
+ <span
23
+ ref={ref}
24
+ data-slot="placeholder"
25
+ className={cn("inline-block animate-pulse text-muted-foreground", className)}
26
+ {...props}
27
+ />
28
+ )
29
+ })
30
+ Placeholder.displayName = "Placeholder"