tembro 4.1.0 → 4.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/dist/components/inputs/combobox.cjs +1 -0
- package/dist/components/inputs/combobox.d.ts +46 -0
- package/dist/components/inputs/combobox.js +1 -0
- package/dist/components/inputs/index.d.ts +1 -0
- package/dist/components/inputs/public.d.ts +1 -0
- package/dist/src/components/inputs/combobox.cjs +1 -0
- package/dist/src/components/inputs/combobox.js +162 -0
- package/dist/src/components/inputs/index.cjs +1 -1
- package/dist/src/components/inputs/index.js +10 -9
- package/dist/src/components/inputs/public.cjs +1 -1
- package/dist/src/components/inputs/public.js +10 -9
- package/dist/src/components/ui/popover/index.cjs +1 -1
- package/dist/src/components/ui/popover/index.js +1 -1
- package/dist/src/index.cjs +1 -1
- package/dist/src/index.js +55 -54
- package/dist/src/public-component-surface.cjs +1 -1
- package/dist/src/public-component-surface.js +102 -41
- package/package.json +1 -1
- package/packages/cli/dist/index.cjs +37 -37
- package/packages/cli/vendor/src/components/inputs/combobox.tsx +303 -0
- package/packages/cli/vendor/src/components/inputs/index.ts +1 -0
- package/packages/cli/vendor/src/components/inputs/public.ts +1 -0
- package/packages/cli/vendor/src/components/ui/popover/index.tsx +1 -1
- package/packages/cli/vendor/src/public-component-surface.ts +35 -22
- package/packages/cli/vendor/templates/showcase/src/showcase/layout/HeroSection.tsx +2 -2
- package/packages/cli/vendor/templates/showcase/src/showcase/layout/WorkbenchSidebar.tsx +1 -1
- package/registry.json +26 -11
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { CheckIcon, ChevronsUpDownIcon, SearchIcon, XIcon } from "lucide-react"
|
|
5
|
+
|
|
6
|
+
import { Button } from "@/components/ui/button"
|
|
7
|
+
import { Input } from "@/components/ui/input"
|
|
8
|
+
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
|
|
9
|
+
import { cn, stopInteractivePropagation } from "@/lib/utils"
|
|
10
|
+
|
|
11
|
+
export type ComboboxOption<TValue extends string = string, TData = unknown> = {
|
|
12
|
+
value: TValue
|
|
13
|
+
label: React.ReactNode
|
|
14
|
+
description?: React.ReactNode
|
|
15
|
+
disabled?: boolean
|
|
16
|
+
disabledReason?: React.ReactNode
|
|
17
|
+
keywords?: string[]
|
|
18
|
+
data?: TData
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type ComboboxGroup<TValue extends string = string, TData = unknown> = {
|
|
22
|
+
label?: React.ReactNode
|
|
23
|
+
options: ComboboxOption<TValue, TData>[]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type ComboboxLabels = {
|
|
27
|
+
placeholder?: string
|
|
28
|
+
searchPlaceholder?: string
|
|
29
|
+
empty?: string
|
|
30
|
+
clear?: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type ComboboxProps<TValue extends string = string, TData = unknown> = Omit<React.ComponentProps<"div">, "onChange"> & {
|
|
34
|
+
value?: TValue
|
|
35
|
+
defaultValue?: TValue
|
|
36
|
+
onValueChange?: (value: TValue | undefined, option?: ComboboxOption<TValue, TData>) => void
|
|
37
|
+
options?: ComboboxOption<TValue, TData>[]
|
|
38
|
+
groups?: ComboboxGroup<TValue, TData>[]
|
|
39
|
+
disabled?: boolean
|
|
40
|
+
clearable?: boolean
|
|
41
|
+
searchable?: boolean
|
|
42
|
+
open?: boolean
|
|
43
|
+
defaultOpen?: boolean
|
|
44
|
+
onOpenChange?: (open: boolean) => void
|
|
45
|
+
labels?: ComboboxLabels
|
|
46
|
+
invalid?: boolean
|
|
47
|
+
renderOption?: (option: ComboboxOption<TValue, TData>, state: { selected: boolean }) => React.ReactNode
|
|
48
|
+
renderValue?: (option: ComboboxOption<TValue, TData>) => React.ReactNode
|
|
49
|
+
filterOption?: (option: ComboboxOption<TValue, TData>, search: string) => boolean
|
|
50
|
+
triggerClassName?: string
|
|
51
|
+
contentClassName?: string
|
|
52
|
+
searchClassName?: string
|
|
53
|
+
optionClassName?: string
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function useControllableState<TValue>({
|
|
57
|
+
value,
|
|
58
|
+
defaultValue,
|
|
59
|
+
onChange,
|
|
60
|
+
}: {
|
|
61
|
+
value?: TValue
|
|
62
|
+
defaultValue: TValue
|
|
63
|
+
onChange?: (value: TValue) => void
|
|
64
|
+
}) {
|
|
65
|
+
const [internalValue, setInternalValue] = React.useState(defaultValue)
|
|
66
|
+
const isControlled = value !== undefined
|
|
67
|
+
const currentValue = isControlled ? value : internalValue
|
|
68
|
+
|
|
69
|
+
const setValue = React.useCallback(
|
|
70
|
+
(nextValue: TValue) => {
|
|
71
|
+
if (!isControlled) {
|
|
72
|
+
setInternalValue(nextValue)
|
|
73
|
+
}
|
|
74
|
+
onChange?.(nextValue)
|
|
75
|
+
},
|
|
76
|
+
[isControlled, onChange]
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
return [currentValue, setValue] as const
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function normalizeComboboxGroups<TValue extends string, TData>({
|
|
83
|
+
options,
|
|
84
|
+
groups,
|
|
85
|
+
}: Pick<ComboboxProps<TValue, TData>, "options" | "groups">) {
|
|
86
|
+
if (groups?.length) return groups
|
|
87
|
+
if (options?.length) return [{ options }]
|
|
88
|
+
return []
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function getComboboxOptionText(option: ComboboxOption) {
|
|
92
|
+
return [
|
|
93
|
+
option.value,
|
|
94
|
+
typeof option.label === "string" || typeof option.label === "number" ? String(option.label) : "",
|
|
95
|
+
typeof option.description === "string" || typeof option.description === "number" ? String(option.description) : "",
|
|
96
|
+
...(option.keywords ?? []),
|
|
97
|
+
]
|
|
98
|
+
.join(" ")
|
|
99
|
+
.toLowerCase()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function defaultFilterOption(option: ComboboxOption, search: string) {
|
|
103
|
+
if (!search.trim()) return true
|
|
104
|
+
return getComboboxOptionText(option).includes(search.trim().toLowerCase())
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function findOption<TValue extends string, TData>(
|
|
108
|
+
groups: ComboboxGroup<TValue, TData>[],
|
|
109
|
+
value?: TValue
|
|
110
|
+
) {
|
|
111
|
+
if (!value) return undefined
|
|
112
|
+
|
|
113
|
+
for (const group of groups) {
|
|
114
|
+
const option = group.options.find((item) => item.value === value)
|
|
115
|
+
if (option) return option
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return undefined
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function Combobox<TValue extends string = string, TData = unknown>({
|
|
122
|
+
className,
|
|
123
|
+
value,
|
|
124
|
+
defaultValue,
|
|
125
|
+
onValueChange,
|
|
126
|
+
options,
|
|
127
|
+
groups,
|
|
128
|
+
disabled = false,
|
|
129
|
+
clearable = true,
|
|
130
|
+
searchable = true,
|
|
131
|
+
open,
|
|
132
|
+
defaultOpen = false,
|
|
133
|
+
onOpenChange,
|
|
134
|
+
labels,
|
|
135
|
+
invalid,
|
|
136
|
+
renderOption,
|
|
137
|
+
renderValue,
|
|
138
|
+
filterOption = defaultFilterOption,
|
|
139
|
+
triggerClassName,
|
|
140
|
+
contentClassName,
|
|
141
|
+
searchClassName,
|
|
142
|
+
optionClassName,
|
|
143
|
+
...props
|
|
144
|
+
}: ComboboxProps<TValue, TData>) {
|
|
145
|
+
const normalizedGroups = React.useMemo(() => normalizeComboboxGroups({ options, groups }), [groups, options])
|
|
146
|
+
const [currentValue, setCurrentValue] = useControllableState<TValue | undefined>({
|
|
147
|
+
value,
|
|
148
|
+
defaultValue,
|
|
149
|
+
onChange: (nextValue) => onValueChange?.(nextValue, findOption(normalizedGroups, nextValue)),
|
|
150
|
+
})
|
|
151
|
+
const [isOpen, setIsOpen] = useControllableState({
|
|
152
|
+
value: open,
|
|
153
|
+
defaultValue: defaultOpen,
|
|
154
|
+
onChange: onOpenChange,
|
|
155
|
+
})
|
|
156
|
+
const [search, setSearch] = React.useState("")
|
|
157
|
+
const selectedOption = findOption(normalizedGroups, currentValue)
|
|
158
|
+
const filteredGroups = React.useMemo(
|
|
159
|
+
() =>
|
|
160
|
+
normalizedGroups
|
|
161
|
+
.map((group) => ({
|
|
162
|
+
...group,
|
|
163
|
+
options: group.options.filter((option) => filterOption(option, search)),
|
|
164
|
+
}))
|
|
165
|
+
.filter((group) => group.options.length > 0),
|
|
166
|
+
[filterOption, normalizedGroups, search]
|
|
167
|
+
)
|
|
168
|
+
const hasMatches = filteredGroups.some((group) => group.options.length > 0)
|
|
169
|
+
|
|
170
|
+
const selectOption = (option: ComboboxOption<TValue, TData>) => {
|
|
171
|
+
if (option.disabled) return
|
|
172
|
+
|
|
173
|
+
setCurrentValue(option.value)
|
|
174
|
+
setIsOpen(false)
|
|
175
|
+
setSearch("")
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const clearSelection = () => {
|
|
179
|
+
setCurrentValue(undefined)
|
|
180
|
+
setSearch("")
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const handleClear = (event: React.MouseEvent<HTMLElement>) => {
|
|
184
|
+
stopInteractivePropagation(event)
|
|
185
|
+
clearSelection()
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
<div data-slot="combobox" className={cn("w-full", className)} {...props}>
|
|
190
|
+
<Popover open={isOpen} onOpenChange={setIsOpen}>
|
|
191
|
+
<PopoverTrigger
|
|
192
|
+
render={
|
|
193
|
+
<Button
|
|
194
|
+
type="button"
|
|
195
|
+
variant="outline"
|
|
196
|
+
disabled={disabled}
|
|
197
|
+
aria-expanded={isOpen}
|
|
198
|
+
aria-invalid={invalid || undefined}
|
|
199
|
+
data-slot="combobox-trigger"
|
|
200
|
+
className={cn("w-full justify-between text-left", triggerClassName)}
|
|
201
|
+
/>
|
|
202
|
+
}
|
|
203
|
+
>
|
|
204
|
+
<span className="min-w-0 flex-1 truncate">
|
|
205
|
+
{selectedOption ? renderValue?.(selectedOption) ?? selectedOption.label : (
|
|
206
|
+
<span className="text-muted-foreground">{labels?.placeholder ?? "Select option"}</span>
|
|
207
|
+
)}
|
|
208
|
+
</span>
|
|
209
|
+
<span className="ml-2 inline-flex shrink-0 items-center gap-1">
|
|
210
|
+
{clearable && currentValue && !disabled ? (
|
|
211
|
+
<span
|
|
212
|
+
role="button"
|
|
213
|
+
tabIndex={0}
|
|
214
|
+
data-slot="combobox-clear"
|
|
215
|
+
aria-label={labels?.clear ?? "Clear selection"}
|
|
216
|
+
onClick={handleClear}
|
|
217
|
+
onKeyDown={(event) => {
|
|
218
|
+
if (event.key !== "Enter" && event.key !== " ") return
|
|
219
|
+
event.preventDefault()
|
|
220
|
+
stopInteractivePropagation(event)
|
|
221
|
+
clearSelection()
|
|
222
|
+
}}
|
|
223
|
+
>
|
|
224
|
+
<XIcon data-icon="clear" />
|
|
225
|
+
</span>
|
|
226
|
+
) : null}
|
|
227
|
+
<ChevronsUpDownIcon className="size-4 opacity-60" />
|
|
228
|
+
</span>
|
|
229
|
+
</PopoverTrigger>
|
|
230
|
+
<PopoverContent align="start" data-slot="combobox-content" className={cn("w-(--anchor-width) min-w-72 gap-2 p-2", contentClassName)}>
|
|
231
|
+
{searchable ? (
|
|
232
|
+
<div data-slot="combobox-search-wrap" className="relative">
|
|
233
|
+
<SearchIcon className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
|
|
234
|
+
<Input
|
|
235
|
+
data-slot="combobox-search"
|
|
236
|
+
value={search}
|
|
237
|
+
onChange={(event) => setSearch(event.currentTarget.value)}
|
|
238
|
+
placeholder={labels?.searchPlaceholder ?? "Search..."}
|
|
239
|
+
className={cn("pl-9", searchClassName)}
|
|
240
|
+
autoFocus
|
|
241
|
+
/>
|
|
242
|
+
</div>
|
|
243
|
+
) : null}
|
|
244
|
+
|
|
245
|
+
<div data-slot="combobox-list" className="flex max-h-72 flex-col gap-1 overflow-y-auto pr-1">
|
|
246
|
+
{!hasMatches ? (
|
|
247
|
+
<div data-slot="combobox-empty" className="rounded-[var(--radius-md)] px-3 py-6 text-center text-sm text-muted-foreground">
|
|
248
|
+
{labels?.empty ?? "No options found"}
|
|
249
|
+
</div>
|
|
250
|
+
) : null}
|
|
251
|
+
|
|
252
|
+
{filteredGroups.map((group, groupIndex) => (
|
|
253
|
+
<div key={groupIndex} data-slot="combobox-group">
|
|
254
|
+
{group.label ? (
|
|
255
|
+
<div data-slot="combobox-group-label" className="sticky top-0 z-10 bg-popover px-2 py-1.5 text-xs font-medium text-muted-foreground">
|
|
256
|
+
{group.label}
|
|
257
|
+
</div>
|
|
258
|
+
) : null}
|
|
259
|
+
{group.options.map((option) => {
|
|
260
|
+
const selected = option.value === currentValue
|
|
261
|
+
|
|
262
|
+
return (
|
|
263
|
+
<button
|
|
264
|
+
key={option.value}
|
|
265
|
+
type="button"
|
|
266
|
+
disabled={option.disabled}
|
|
267
|
+
data-slot="combobox-option"
|
|
268
|
+
data-selected={selected || undefined}
|
|
269
|
+
data-disabled={option.disabled || undefined}
|
|
270
|
+
className={cn(
|
|
271
|
+
"flex w-full items-start gap-2.5 rounded-[var(--radius-md)] px-2.5 py-2 text-left text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[selected]:bg-accent/70",
|
|
272
|
+
optionClassName
|
|
273
|
+
)}
|
|
274
|
+
onClick={() => selectOption(option)}
|
|
275
|
+
>
|
|
276
|
+
<span className="mt-0.5 flex size-4 shrink-0 items-center justify-center">
|
|
277
|
+
{selected ? <CheckIcon className="size-4" /> : null}
|
|
278
|
+
</span>
|
|
279
|
+
<span className="min-w-0 flex-1">
|
|
280
|
+
{renderOption?.(option, { selected }) ?? (
|
|
281
|
+
<span className="flex min-w-0 flex-col">
|
|
282
|
+
<span className="truncate font-medium">{option.label}</span>
|
|
283
|
+
{(option.description || option.disabledReason) ? (
|
|
284
|
+
<span className="truncate text-xs text-muted-foreground">
|
|
285
|
+
{option.disabled ? option.disabledReason ?? option.description : option.description}
|
|
286
|
+
</span>
|
|
287
|
+
) : null}
|
|
288
|
+
</span>
|
|
289
|
+
)}
|
|
290
|
+
</span>
|
|
291
|
+
</button>
|
|
292
|
+
)
|
|
293
|
+
})}
|
|
294
|
+
</div>
|
|
295
|
+
))}
|
|
296
|
+
</div>
|
|
297
|
+
</PopoverContent>
|
|
298
|
+
</Popover>
|
|
299
|
+
</div>
|
|
300
|
+
)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export { Combobox }
|
|
@@ -46,7 +46,7 @@ function PopoverContent({
|
|
|
46
46
|
<PopoverPrimitive.Popup
|
|
47
47
|
data-slot="popover-content"
|
|
48
48
|
className={cn(
|
|
49
|
-
"z-50 flex max-h-[min(var(--available-height),calc(100dvh-1rem))] w-[min(var(--anchor-width),20rem)] min-w-60 max-w-[calc(100vw-1rem)] origin-(--transform-origin) flex-col gap-3 overflow-y-auto outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
|
49
|
+
"z-50 flex max-h-[min(var(--available-height),calc(100dvh-1rem))] w-[min(var(--anchor-width),20rem)] min-w-60 max-w-[calc(100vw-1rem)] origin-(--transform-origin) flex-col gap-3 overflow-y-auto rounded-[var(--radius-lg)] border border-border bg-popover text-popover-foreground shadow-lg outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
|
50
50
|
className
|
|
51
51
|
)}
|
|
52
52
|
{...props}
|
|
@@ -5,20 +5,32 @@ export type PublicComponentSurfaceEntry = {
|
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
export const documentedPublicComponentSurfaces: readonly PublicComponentSurfaceEntry[] = [
|
|
8
|
-
{ slug: "button", registryName: "button", surface: "documented" },
|
|
9
|
-
{ slug: "input", registryName: "input", surface: "documented" },
|
|
10
|
-
{ slug: "
|
|
11
|
-
{ slug: "
|
|
12
|
-
{ slug: "
|
|
13
|
-
{ slug: "
|
|
14
|
-
{ slug: "
|
|
15
|
-
{ slug: "
|
|
16
|
-
{ slug: "
|
|
17
|
-
{ slug: "
|
|
18
|
-
{ slug: "
|
|
19
|
-
{ slug: "
|
|
20
|
-
{ slug: "
|
|
8
|
+
{ slug: "button", registryName: "button", surface: "documented" },
|
|
9
|
+
{ slug: "input", registryName: "input", surface: "documented" },
|
|
10
|
+
{ slug: "textarea", registryName: "textarea", surface: "documented" },
|
|
11
|
+
{ slug: "select", registryName: "select", surface: "documented" },
|
|
12
|
+
{ slug: "combobox", registryName: "combobox", surface: "documented" },
|
|
13
|
+
{ slug: "async-select", registryName: "async-select", surface: "documented" },
|
|
14
|
+
{ slug: "card", registryName: "card", surface: "documented" },
|
|
15
|
+
{ slug: "badge", registryName: "badge", surface: "documented" },
|
|
16
|
+
{ slug: "checkbox", registryName: "checkbox", surface: "documented" },
|
|
17
|
+
{ slug: "switch", registryName: "switch", surface: "documented" },
|
|
18
|
+
{ slug: "radio-group", registryName: "radio-group", surface: "documented" },
|
|
19
|
+
{ slug: "overlay", registryName: "dialog", surface: "documented" },
|
|
20
|
+
{ slug: "dropdown-menu", registryName: "dropdown-menu", surface: "documented" },
|
|
21
|
+
{ slug: "popover", registryName: "popover", surface: "documented" },
|
|
22
|
+
{ slug: "tooltip", registryName: "tooltip", surface: "documented" },
|
|
23
|
+
{ slug: "data-table", registryName: "data-table", surface: "documented" },
|
|
24
|
+
{ slug: "pagination", registryName: "pagination", surface: "documented" },
|
|
25
|
+
{ slug: "calendar", registryName: "calendar", surface: "documented" },
|
|
26
|
+
{ slug: "date-picker", registryName: "date-picker", surface: "documented" },
|
|
27
|
+
{ slug: "date-range-picker", registryName: "date-range-picker", surface: "documented" },
|
|
28
|
+
{ slug: "alert", registryName: "alert", surface: "documented" },
|
|
29
|
+
{ slug: "toast", registryName: "toast", surface: "documented" },
|
|
30
|
+
{ slug: "notification-center", registryName: "notification-center", surface: "documented" },
|
|
31
|
+
{ slug: "bar-chart", registryName: "charts", surface: "documented" },
|
|
21
32
|
{ slug: "sidebar", registryName: "sidebar", surface: "documented" },
|
|
33
|
+
{ slug: "breadcrumbs", registryName: "breadcrumbs", surface: "documented" },
|
|
22
34
|
{ slug: "workspace-layout", registryName: "workspace-layout", surface: "documented" },
|
|
23
35
|
{ slug: "state-view", registryName: "state-view", surface: "documented" },
|
|
24
36
|
{ slug: "file-upload", registryName: "file-upload", surface: "documented" },
|
|
@@ -41,15 +53,7 @@ export const documentedPublicComponentSurfaces: readonly PublicComponentSurfaceE
|
|
|
41
53
|
{ slug: "tree-view", registryName: "tree-view", surface: "documented" },
|
|
42
54
|
{ slug: "copy-button", registryName: "copy-button", surface: "documented" },
|
|
43
55
|
{ slug: "section", registryName: "section", surface: "documented" },
|
|
44
|
-
{ slug: "form-builder", registryName: "form-builder", surface: "documented" },
|
|
45
56
|
{ slug: "empty-state", registryName: "empty-state", surface: "documented" },
|
|
46
|
-
{ slug: "page-toolbar", registryName: "page-toolbar", surface: "documented" },
|
|
47
|
-
{ slug: "bulk-action-bar", registryName: "bulk-action-bar", surface: "documented" },
|
|
48
|
-
{ slug: "detail-layout", registryName: "detail-layout", surface: "documented" },
|
|
49
|
-
{ slug: "settings-page", registryName: "settings-page", surface: "documented" },
|
|
50
|
-
{ slug: "data-view", registryName: "data-view", surface: "documented" },
|
|
51
|
-
{ slug: "resource-page", registryName: "resource-page", surface: "documented" },
|
|
52
|
-
{ slug: "resource-detail-page", registryName: "resource-detail-page", surface: "documented" },
|
|
53
57
|
{ slug: "calendar-scheduler", registryName: "calendar-scheduler", surface: "documented" },
|
|
54
58
|
{ slug: "dual-list-picker", registryName: "dual-list-picker", surface: "documented" },
|
|
55
59
|
{ slug: "resizable-panel", registryName: "resizable-panel", surface: "documented" },
|
|
@@ -67,7 +71,16 @@ export const documentedPublicComponentSurfaces: readonly PublicComponentSurfaceE
|
|
|
67
71
|
{ slug: "image-cropper", registryName: "image-cropper", surface: "documented" },
|
|
68
72
|
] as const
|
|
69
73
|
|
|
70
|
-
export const standalonePublicComponentSurfaces: readonly PublicComponentSurfaceEntry[] = [
|
|
74
|
+
export const standalonePublicComponentSurfaces: readonly PublicComponentSurfaceEntry[] = [
|
|
75
|
+
{ slug: "form-builder", registryName: "form-builder", surface: "standalone" },
|
|
76
|
+
{ slug: "page-toolbar", registryName: "page-toolbar", surface: "standalone" },
|
|
77
|
+
{ slug: "bulk-action-bar", registryName: "bulk-action-bar", surface: "standalone" },
|
|
78
|
+
{ slug: "detail-layout", registryName: "detail-layout", surface: "standalone" },
|
|
79
|
+
{ slug: "settings-page", registryName: "settings-page", surface: "standalone" },
|
|
80
|
+
{ slug: "data-view", registryName: "data-view", surface: "standalone" },
|
|
81
|
+
{ slug: "resource-page", registryName: "resource-page", surface: "standalone" },
|
|
82
|
+
{ slug: "resource-detail-page", registryName: "resource-detail-page", surface: "standalone" },
|
|
83
|
+
] as const
|
|
71
84
|
|
|
72
85
|
export const publicComponentSurfaceEntries = [
|
|
73
86
|
...documentedPublicComponentSurfaces,
|
|
@@ -25,7 +25,7 @@ export function HeroSection() {
|
|
|
25
25
|
<div className="flex flex-wrap gap-2">
|
|
26
26
|
<Button leftIcon={<PlusIcon className="size-4" />}>Create</Button>
|
|
27
27
|
<Button variant="outline" leftIcon={<SettingsIcon className="size-4" />}>Settings</Button>
|
|
28
|
-
<CopyButton value="npx tembro@4.1
|
|
28
|
+
<CopyButton value="npx tembro@4.2.1 list --json">Copy list command</CopyButton>
|
|
29
29
|
</div>
|
|
30
30
|
</div>
|
|
31
31
|
|
|
@@ -33,7 +33,7 @@ export function HeroSection() {
|
|
|
33
33
|
<StatisticCard label="Registry components" value={moduleCount} change="visible below" trend="up" description="tembro add <name>" />
|
|
34
34
|
<StatisticCard label="Local source files" value="152" change="with hooks/lib" trend="up" description="installed by CLI" />
|
|
35
35
|
<StatisticCard label="Categories" value={registryGroups.length} change="all shown" trend="up" description="actions to wizard" />
|
|
36
|
-
<StatisticCard label="Build" value="Pass" change="doctor pass" trend="up" description="tembro@4.1
|
|
36
|
+
<StatisticCard label="Build" value="Pass" change="doctor pass" trend="up" description="tembro@4.2.1" />
|
|
37
37
|
</StatisticGrid>
|
|
38
38
|
</div>
|
|
39
39
|
</section>
|
package/registry.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://tembro.dev/registry.schema.json",
|
|
3
3
|
"name": "tembro",
|
|
4
|
-
"version": "4.1
|
|
4
|
+
"version": "4.2.1",
|
|
5
5
|
"description": "Reusable React + TypeScript UI kit registry",
|
|
6
6
|
"groups": {
|
|
7
7
|
"ui": [
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"pagination"
|
|
40
40
|
],
|
|
41
41
|
"inputs": [
|
|
42
|
+
"combobox",
|
|
42
43
|
"async-select",
|
|
43
44
|
"otp-input",
|
|
44
45
|
"rating",
|
|
@@ -180,6 +181,7 @@
|
|
|
180
181
|
"dialog",
|
|
181
182
|
"popover",
|
|
182
183
|
"select",
|
|
184
|
+
"combobox",
|
|
183
185
|
"table",
|
|
184
186
|
"badge",
|
|
185
187
|
"card",
|
|
@@ -216,6 +218,7 @@
|
|
|
216
218
|
"dialog",
|
|
217
219
|
"popover",
|
|
218
220
|
"select",
|
|
221
|
+
"combobox",
|
|
219
222
|
"table",
|
|
220
223
|
"badge",
|
|
221
224
|
"card",
|
|
@@ -259,18 +262,30 @@
|
|
|
259
262
|
"documented": [
|
|
260
263
|
"button",
|
|
261
264
|
"input",
|
|
265
|
+
"textarea",
|
|
262
266
|
"select",
|
|
267
|
+
"combobox",
|
|
268
|
+
"async-select",
|
|
263
269
|
"card",
|
|
264
270
|
"badge",
|
|
265
271
|
"checkbox",
|
|
272
|
+
"switch",
|
|
273
|
+
"radio-group",
|
|
266
274
|
"dialog",
|
|
275
|
+
"dropdown-menu",
|
|
276
|
+
"popover",
|
|
277
|
+
"tooltip",
|
|
267
278
|
"data-table",
|
|
279
|
+
"pagination",
|
|
268
280
|
"calendar",
|
|
269
281
|
"date-picker",
|
|
270
282
|
"date-range-picker",
|
|
271
283
|
"alert",
|
|
284
|
+
"toast",
|
|
285
|
+
"notification-center",
|
|
272
286
|
"charts",
|
|
273
287
|
"sidebar",
|
|
288
|
+
"breadcrumbs",
|
|
274
289
|
"workspace-layout",
|
|
275
290
|
"state-view",
|
|
276
291
|
"file-upload",
|
|
@@ -293,15 +308,7 @@
|
|
|
293
308
|
"tree-view",
|
|
294
309
|
"copy-button",
|
|
295
310
|
"section",
|
|
296
|
-
"form-builder",
|
|
297
311
|
"empty-state",
|
|
298
|
-
"page-toolbar",
|
|
299
|
-
"bulk-action-bar",
|
|
300
|
-
"detail-layout",
|
|
301
|
-
"settings-page",
|
|
302
|
-
"data-view",
|
|
303
|
-
"resource-page",
|
|
304
|
-
"resource-detail-page",
|
|
305
312
|
"calendar-scheduler",
|
|
306
313
|
"dual-list-picker",
|
|
307
314
|
"resizable-panel",
|
|
@@ -318,7 +325,16 @@
|
|
|
318
325
|
"rich-text-editor",
|
|
319
326
|
"image-cropper"
|
|
320
327
|
],
|
|
321
|
-
"standalone": [
|
|
328
|
+
"standalone": [
|
|
329
|
+
"form-builder",
|
|
330
|
+
"page-toolbar",
|
|
331
|
+
"bulk-action-bar",
|
|
332
|
+
"detail-layout",
|
|
333
|
+
"settings-page",
|
|
334
|
+
"data-view",
|
|
335
|
+
"resource-page",
|
|
336
|
+
"resource-detail-page"
|
|
337
|
+
]
|
|
322
338
|
},
|
|
323
339
|
"migrationAliases": {
|
|
324
340
|
"clearable-input": "input",
|
|
@@ -332,7 +348,6 @@
|
|
|
332
348
|
"date-input": "input",
|
|
333
349
|
"date-range-input": "input",
|
|
334
350
|
"simple-select": "select",
|
|
335
|
-
"combobox": "select",
|
|
336
351
|
"hover-card": "popover",
|
|
337
352
|
"copy-field": "copy-button",
|
|
338
353
|
"comparison-card": "card",
|