tembro 2.0.5 → 2.0.6

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 (70) hide show
  1. package/dist/components/data-table/data-table-pagination.cjs +1 -1
  2. package/dist/components/data-table/data-table-pagination.js +4 -4
  3. package/dist/components/data-table/data-table.cjs +1 -1
  4. package/dist/components/data-table/data-table.d.ts +2 -2
  5. package/dist/components/data-table/data-table.js +2 -1
  6. package/dist/components/display/carousel.cjs +1 -1
  7. package/dist/components/display/carousel.d.ts +7 -1
  8. package/dist/components/display/carousel.js +44 -44
  9. package/dist/components/display/smart-card.d.ts +1 -1
  10. package/dist/components/display/status-legend.d.ts +1 -1
  11. package/dist/components/form/form-input.cjs +1 -1
  12. package/dist/components/form/form-input.d.ts +3 -4
  13. package/dist/components/form/form-input.js +257 -257
  14. package/dist/components/form/form-select.cjs +1 -1
  15. package/dist/components/form/form-select.d.ts +2 -2
  16. package/dist/components/form/form-select.js +6 -6
  17. package/dist/components/inputs/async-select.d.ts +3 -0
  18. package/dist/components/inputs/search-input.d.ts +3 -0
  19. package/dist/components/inputs/simple-select.cjs +1 -1
  20. package/dist/components/inputs/simple-select.d.ts +10 -32
  21. package/dist/components/inputs/simple-select.js +5 -101
  22. package/dist/components/ui/card/index.cjs +1 -1
  23. package/dist/components/ui/card/index.d.ts +13 -2
  24. package/dist/components/ui/card/index.js +74 -49
  25. package/dist/components/ui/input/index.cjs +1 -1
  26. package/dist/components/ui/input/index.d.ts +8 -0
  27. package/dist/components/ui/input/index.js +63 -30
  28. package/dist/components/ui/select/index.cjs +1 -1
  29. package/dist/components/ui/select/index.d.ts +33 -1
  30. package/dist/components/ui/select/index.js +154 -48
  31. package/dist/showcase/create-demo.cjs +5 -4
  32. package/dist/showcase/create-demo.js +2 -2
  33. package/dist/showcase/premium/data-table/showcase.cjs +1 -1
  34. package/dist/showcase/premium/data-table/showcase.js +1 -1
  35. package/dist/showcase/premium/data-table-parts/showcase.cjs +1 -1
  36. package/dist/showcase/premium/data-table-parts/showcase.js +3 -1
  37. package/dist/showcase/premium/select/mock.cjs +8 -9
  38. package/dist/showcase/premium/select/mock.js +1 -1
  39. package/dist/showcase/premium/select/showcase.cjs +1 -1
  40. package/dist/showcase/premium/select/showcase.js +76 -86
  41. package/dist/showcase/registry-specific.cjs +1 -1
  42. package/dist/showcase/registry-specific.js +78 -74
  43. package/dist/showcase/render-registry-preview.cjs +1 -1
  44. package/dist/showcase/render-registry-preview.js +91 -87
  45. package/dist/showcase/tembro-registry.json.cjs +1 -1
  46. package/dist/showcase/tembro-registry.json.js +1 -1
  47. package/package.json +1 -1
  48. package/packages/cli/dist/index.cjs +1 -1
  49. package/packages/cli/vendor/src/components/data-table/data-table-pagination.tsx +7 -7
  50. package/packages/cli/vendor/src/components/data-table/data-table.tsx +21 -20
  51. package/packages/cli/vendor/src/components/display/carousel.tsx +18 -6
  52. package/packages/cli/vendor/src/components/display/smart-card.tsx +1 -1
  53. package/packages/cli/vendor/src/components/display/status-legend.tsx +1 -1
  54. package/packages/cli/vendor/src/components/form/form-input.tsx +9 -12
  55. package/packages/cli/vendor/src/components/form/form-select.tsx +4 -4
  56. package/packages/cli/vendor/src/components/inputs/async-select.tsx +11 -8
  57. package/packages/cli/vendor/src/components/inputs/search-input.tsx +5 -2
  58. package/packages/cli/vendor/src/components/inputs/simple-select.tsx +20 -190
  59. package/packages/cli/vendor/src/components/ui/card/index.tsx +95 -25
  60. package/packages/cli/vendor/src/components/ui/input/index.tsx +98 -0
  61. package/packages/cli/vendor/src/components/ui/select/index.tsx +227 -34
  62. package/packages/cli/vendor/src/showcase/create-demo.tsx +16 -15
  63. package/packages/cli/vendor/src/showcase/premium/data-table/showcase.tsx +3 -3
  64. package/packages/cli/vendor/src/showcase/premium/data-table-parts/showcase.tsx +3 -3
  65. package/packages/cli/vendor/src/showcase/premium/select/mock.ts +1 -1
  66. package/packages/cli/vendor/src/showcase/premium/select/showcase.tsx +13 -21
  67. package/packages/cli/vendor/src/showcase/registry-specific.tsx +7 -8
  68. package/packages/cli/vendor/src/showcase/render-registry-preview.tsx +5 -6
  69. package/packages/cli/vendor/src/showcase/tembro-registry.json +1 -1
  70. package/registry.json +1 -1
@@ -39,6 +39,7 @@ export type InputTextProps = Omit<InputPrimitiveProps, "value"> & {
39
39
  kind?: "text"
40
40
  value?: string | number | readonly string[] | null
41
41
  onValueChange?: (value: string) => void
42
+ onDebouncedValueChange?: (value: string) => void
42
43
  leading?: React.ReactNode
43
44
  trailing?: React.ReactNode
44
45
  trailingAction?: React.ReactNode
@@ -57,6 +58,13 @@ export type InputTextProps = Omit<InputPrimitiveProps, "value"> & {
57
58
  helperClassName?: string
58
59
  leadingPointerEvents?: boolean
59
60
  trailingPointerEvents?: boolean
61
+ searchIcon?: React.ReactNode
62
+ loading?: boolean
63
+ loadingLabel?: string
64
+ resultCount?: number
65
+ shortcut?: React.ReactNode
66
+ debounceMs?: number
67
+ showMetaOnClear?: boolean
60
68
  }
61
69
 
62
70
  export type InputClearableProps = Omit<ClearableInputProps, "kind"> & {
@@ -121,6 +129,20 @@ function getInputTextLength(value: InputTextProps["value"] | InputTextProps["def
121
129
  return 0
122
130
  }
123
131
 
132
+ function shouldRenderSearchInput(props: InputTextProps) {
133
+ return (
134
+ props.type === "search" ||
135
+ props.searchIcon !== undefined ||
136
+ props.loading !== undefined ||
137
+ props.loadingLabel !== undefined ||
138
+ props.resultCount !== undefined ||
139
+ props.shortcut !== undefined ||
140
+ props.debounceMs !== undefined ||
141
+ props.showMetaOnClear !== undefined ||
142
+ props.onDebouncedValueChange !== undefined
143
+ )
144
+ }
145
+
124
146
  const Input = React.forwardRef<HTMLInputElement | HTMLDivElement, InputProps>((props, ref) => {
125
147
  const kind = props.kind ?? "text"
126
148
 
@@ -210,6 +232,82 @@ const Input = React.forwardRef<HTMLInputElement | HTMLDivElement, InputProps>((p
210
232
  }
211
233
 
212
234
  const inputProps = props as Omit<InputTextProps, "kind">
235
+
236
+ if (shouldRenderSearchInput(inputProps)) {
237
+ const {
238
+ leading,
239
+ searchIcon,
240
+ helperText,
241
+ errorText,
242
+ showCharacterCount,
243
+ countFormatter,
244
+ helperClassName,
245
+ inputClassName,
246
+ ...searchInputProps
247
+ } = inputProps
248
+ const searchValue: SearchInputProps["value"] = Array.isArray(inputProps.value)
249
+ ? inputProps.value.join(" ")
250
+ : inputProps.value == null || typeof inputProps.value === "string" || typeof inputProps.value === "number"
251
+ ? inputProps.value
252
+ : String(inputProps.value)
253
+ const searchDefaultValue: SearchInputProps["defaultValue"] = Array.isArray(inputProps.defaultValue)
254
+ ? inputProps.defaultValue.join(" ")
255
+ : inputProps.defaultValue == null ||
256
+ typeof inputProps.defaultValue === "string" ||
257
+ typeof inputProps.defaultValue === "number"
258
+ ? inputProps.defaultValue
259
+ : String(inputProps.defaultValue)
260
+
261
+ const searchInput = (
262
+ <SearchInput
263
+ ref={ref as React.ForwardedRef<HTMLInputElement>}
264
+ searchIcon={searchIcon ?? leading}
265
+ inputClassName={inputClassName}
266
+ {...searchInputProps}
267
+ value={searchValue}
268
+ defaultValue={searchDefaultValue}
269
+ />
270
+ )
271
+
272
+ if (!helperText && !errorText && !showCharacterCount) {
273
+ return searchInput
274
+ }
275
+
276
+ const currentLength = getInputTextLength(inputProps.value ?? inputProps.defaultValue)
277
+
278
+ return (
279
+ <div data-slot="input-field" className="grid gap-1.5">
280
+ {searchInput}
281
+ <div data-slot="input-meta" className="flex items-start justify-between gap-3 px-1">
282
+ <div
283
+ data-slot="input-helper"
284
+ className={cn(
285
+ "min-w-0 text-xs leading-5",
286
+ errorText ? "text-destructive" : "text-muted-foreground",
287
+ helperClassName
288
+ )}
289
+ >
290
+ {errorText ?? helperText}
291
+ </div>
292
+ {showCharacterCount ? (
293
+ <div
294
+ data-slot="input-count"
295
+ className={cn(
296
+ "shrink-0 text-[11px] font-medium tabular-nums",
297
+ errorText ? "text-destructive" : "text-muted-foreground"
298
+ )}
299
+ >
300
+ {countFormatter?.(currentLength, inputProps.maxLength) ??
301
+ (typeof inputProps.maxLength === "number"
302
+ ? `${currentLength}/${inputProps.maxLength}`
303
+ : currentLength)}
304
+ </div>
305
+ ) : null}
306
+ </div>
307
+ </div>
308
+ )
309
+ }
310
+
213
311
  const {
214
312
  value,
215
313
  defaultValue,
@@ -2,9 +2,9 @@
2
2
 
3
3
  import * as React from "react"
4
4
  import { Select as SelectPrimitive } from "@base-ui/react/select"
5
+ import { CheckIcon, ChevronDownIcon, ChevronUpIcon, LoaderCircleIcon, SearchIcon, XIcon } from "lucide-react"
5
6
 
6
- import { cn } from "@/lib/utils"
7
- import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from "lucide-react"
7
+ import { cn, stopInteractivePropagation } from "@/lib/utils"
8
8
 
9
9
  export type SelectRootProps = React.ComponentPropsWithoutRef<typeof SelectPrimitive.Root>
10
10
  export type SelectGroupProps = SelectPrimitive.Group.Props
@@ -21,7 +21,216 @@ export type SelectSeparatorProps = SelectPrimitive.Separator.Props
21
21
  export type SelectScrollUpButtonProps = React.ComponentProps<typeof SelectPrimitive.ScrollUpArrow>
22
22
  export type SelectScrollDownButtonProps = React.ComponentProps<typeof SelectPrimitive.ScrollDownArrow>
23
23
 
24
- const Select = SelectPrimitive.Root
24
+ export type SelectOption = {
25
+ label: React.ReactNode
26
+ value: string
27
+ disabled?: boolean
28
+ description?: React.ReactNode
29
+ keywords?: string[]
30
+ }
31
+
32
+ export type SelectProps = Omit<SelectRootProps, "value" | "defaultValue" | "onValueChange"> & {
33
+ value?: string | null
34
+ defaultValue?: string | null
35
+ onValueChange?: (value: string | undefined) => void
36
+ options?: SelectOption[]
37
+ placeholder?: string
38
+ searchPlaceholder?: string
39
+ emptyLabel?: React.ReactNode
40
+ clearLabel?: string
41
+ size?: "sm" | "default" | "lg"
42
+ clearable?: boolean
43
+ searchable?: boolean
44
+ loading?: boolean
45
+ loadingLabel?: React.ReactNode
46
+ disabled?: boolean
47
+ showSelectedDescription?: boolean
48
+ triggerClassName?: string
49
+ contentClassName?: string
50
+ itemClassName?: string
51
+ searchClassName?: string
52
+ renderOption?: (option: SelectOption, state: { selected: boolean }) => React.ReactNode
53
+ invalid?: boolean
54
+ }
55
+
56
+ const SelectRoot = SelectPrimitive.Root
57
+
58
+ function optionMatchesSearch(option: SelectOption, search: string) {
59
+ const normalized = search.trim().toLowerCase()
60
+ if (!normalized) return true
61
+
62
+ const labelText =
63
+ typeof option.label === "string" || typeof option.label === "number"
64
+ ? String(option.label)
65
+ : option.value
66
+ const haystack = [labelText, option.value, ...(option.keywords ?? [])].join(" ").toLowerCase()
67
+ return haystack.includes(normalized)
68
+ }
69
+
70
+ function Select({
71
+ value,
72
+ defaultValue,
73
+ onValueChange,
74
+ options,
75
+ placeholder = "Select",
76
+ searchPlaceholder = "Search options...",
77
+ emptyLabel = "No options found",
78
+ clearLabel = "Clear selection",
79
+ size = "default",
80
+ clearable = false,
81
+ searchable = false,
82
+ loading = false,
83
+ loadingLabel = "Loading options...",
84
+ disabled = false,
85
+ showSelectedDescription = true,
86
+ triggerClassName,
87
+ contentClassName,
88
+ itemClassName,
89
+ searchClassName,
90
+ renderOption,
91
+ invalid,
92
+ children,
93
+ onOpenChange,
94
+ ...props
95
+ }: SelectProps) {
96
+ if (!options) {
97
+ return (
98
+ <SelectRoot
99
+ value={value ?? undefined}
100
+ defaultValue={defaultValue ?? undefined}
101
+ onValueChange={(nextValue) => onValueChange?.((nextValue as string | null | undefined) ?? undefined)}
102
+ onOpenChange={onOpenChange}
103
+ disabled={disabled}
104
+ {...props}
105
+ >
106
+ {children}
107
+ </SelectRoot>
108
+ )
109
+ }
110
+
111
+ const [search, setSearch] = React.useState("")
112
+ const selectedOption = options.find((option) => option.value === value)
113
+ const filteredOptions = options.filter((option) => optionMatchesSearch(option, search))
114
+
115
+ return (
116
+ <SelectRoot
117
+ value={value ?? undefined}
118
+ defaultValue={defaultValue ?? undefined}
119
+ onValueChange={(nextValue) => onValueChange?.((nextValue as string | null | undefined) ?? undefined)}
120
+ disabled={disabled || loading}
121
+ onOpenChange={(open, eventDetails) => {
122
+ if (!open) setSearch("")
123
+ onOpenChange?.(open, eventDetails)
124
+ }}
125
+ {...props}
126
+ >
127
+ <SelectTrigger
128
+ size={size}
129
+ aria-invalid={invalid || undefined}
130
+ className={cn("w-full", triggerClassName)}
131
+ >
132
+ <SelectValue placeholder={placeholder}>
133
+ {selectedOption ? (
134
+ <span className="flex min-w-0 flex-col items-start">
135
+ <span className="truncate">{selectedOption.label}</span>
136
+ {showSelectedDescription && selectedOption.description ? (
137
+ <span className="truncate text-[11px] font-medium text-muted-foreground">
138
+ {selectedOption.description}
139
+ </span>
140
+ ) : null}
141
+ </span>
142
+ ) : null}
143
+ </SelectValue>
144
+ {loading ? <LoaderCircleIcon className="size-4 animate-spin text-muted-foreground" /> : null}
145
+ {clearable && value && !disabled && !loading ? (
146
+ <span
147
+ role="button"
148
+ tabIndex={0}
149
+ aria-label={clearLabel}
150
+ className="ml-1 rounded-[var(--radius-sm)] border border-border/65 p-1 text-muted-foreground transition-colors hover:border-border hover:bg-muted/55 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
151
+ onClick={(event) => {
152
+ event.preventDefault()
153
+ stopInteractivePropagation(event)
154
+ onValueChange?.(undefined)
155
+ }}
156
+ onMouseDown={stopInteractivePropagation}
157
+ onDoubleClick={stopInteractivePropagation}
158
+ onKeyDown={(event) => {
159
+ if (event.key === "Enter" || event.key === " ") {
160
+ event.preventDefault()
161
+ onValueChange?.(undefined)
162
+ }
163
+ }}
164
+ >
165
+ <XIcon className="size-3.5" />
166
+ </span>
167
+ ) : null}
168
+ </SelectTrigger>
169
+ <SelectContent className={cn("w-(--anchor-width)", contentClassName)}>
170
+ {searchable ? (
171
+ <div
172
+ data-slot="select-search"
173
+ className="sticky top-0 z-10 mb-1 flex items-center gap-2 rounded-[var(--radius-md)] border border-[color:var(--aui-card-border,var(--border))] bg-popover px-2.5 py-2 text-sm"
174
+ >
175
+ <SearchIcon className="size-4 text-muted-foreground" />
176
+ <input
177
+ value={search}
178
+ onChange={(event) => setSearch(event.target.value)}
179
+ placeholder={searchPlaceholder}
180
+ className={cn(
181
+ "min-w-0 flex-1 bg-transparent outline-none placeholder:text-muted-foreground",
182
+ searchClassName
183
+ )}
184
+ />
185
+ </div>
186
+ ) : null}
187
+
188
+ {loading ? (
189
+ <div
190
+ data-slot="select-state"
191
+ className="flex items-center gap-2 rounded-[var(--radius-md)] border border-[color:var(--aui-card-border,var(--border))] bg-[color:color-mix(in_oklch,var(--muted),transparent_55%)] px-3 py-2.5 text-sm text-muted-foreground"
192
+ >
193
+ <LoaderCircleIcon className="size-4 animate-spin" />
194
+ {loadingLabel}
195
+ </div>
196
+ ) : filteredOptions.length === 0 ? (
197
+ <div
198
+ data-slot="select-state"
199
+ className="rounded-[var(--radius-md)] border border-[color:var(--aui-card-border,var(--border))] bg-[color:color-mix(in_oklch,var(--muted),transparent_55%)] px-3 py-2.5 text-sm text-muted-foreground"
200
+ >
201
+ {emptyLabel}
202
+ </div>
203
+ ) : (
204
+ filteredOptions.map((option) => {
205
+ const selected = option.value === value
206
+ return (
207
+ <SelectItem
208
+ key={option.value}
209
+ value={option.value}
210
+ disabled={option.disabled}
211
+ className={cn("rounded-[var(--radius-md)]", itemClassName)}
212
+ >
213
+ {renderOption ? (
214
+ renderOption(option, { selected })
215
+ ) : (
216
+ <span className="flex min-w-0 flex-1 flex-col">
217
+ <span className="flex min-w-0 items-center gap-2">
218
+ <span className="truncate">{option.label}</span>
219
+ {selected ? <CheckIcon className="ml-auto size-3.5 text-primary" /> : null}
220
+ </span>
221
+ {option.description ? (
222
+ <span className="truncate text-xs text-muted-foreground">{option.description}</span>
223
+ ) : null}
224
+ </span>
225
+ )}
226
+ </SelectItem>
227
+ )
228
+ })
229
+ )}
230
+ </SelectContent>
231
+ </SelectRoot>
232
+ )
233
+ }
25
234
 
26
235
  function SelectGroup({ className, ...props }: SelectGroupProps) {
27
236
  return (
@@ -60,11 +269,7 @@ function SelectTrigger({
60
269
  {...props}
61
270
  >
62
271
  {children}
63
- <SelectPrimitive.Icon
64
- render={
65
- <ChevronDownIcon className="pointer-events-none size-4 text-muted-foreground" />
66
- }
67
- />
272
+ <SelectPrimitive.Icon render={<ChevronDownIcon className="pointer-events-none size-4 text-muted-foreground" />} />
68
273
  </SelectPrimitive.Trigger>
69
274
  )
70
275
  }
@@ -92,7 +297,10 @@ function SelectContent({
92
297
  <SelectPrimitive.Popup
93
298
  data-slot="select-content"
94
299
  data-align-trigger={alignItemWithTrigger}
95
- className={cn("relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-52 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-[var(--aui-card-radius,var(--radius-lg))] border border-[color:var(--aui-card-border,var(--border))] bg-popover p-1.5 text-popover-foreground shadow-[var(--aui-control-panel-shadow,0_18px_40px_rgba(15,23,42,0.14))] backdrop-blur 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", className)}
300
+ className={cn(
301
+ "relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-52 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-[var(--aui-card-radius,var(--radius-lg))] border border-[color:var(--aui-card-border,var(--border))] bg-popover p-1.5 text-popover-foreground shadow-[var(--aui-control-panel-shadow,0_18px_40px_rgba(15,23,42,0.14))] backdrop-blur 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",
302
+ className
303
+ )}
96
304
  {...props}
97
305
  >
98
306
  <SelectScrollUpButton />
@@ -104,24 +312,20 @@ function SelectContent({
104
312
  )
105
313
  }
106
314
 
107
- function SelectLabel({
108
- className,
109
- ...props
110
- }: SelectLabelProps) {
315
+ function SelectLabel({ className, ...props }: SelectLabelProps) {
111
316
  return (
112
317
  <SelectPrimitive.GroupLabel
113
318
  data-slot="select-label"
114
- className={cn("px-2 py-1.5 text-[11px] font-semibold tracking-[0.14em] uppercase text-muted-foreground/90", className)}
319
+ className={cn(
320
+ "px-2 py-1.5 text-[11px] font-semibold tracking-[0.14em] uppercase text-muted-foreground/90",
321
+ className
322
+ )}
115
323
  {...props}
116
324
  />
117
325
  )
118
326
  }
119
327
 
120
- function SelectItem({
121
- className,
122
- children,
123
- ...props
124
- }: SelectItemProps) {
328
+ function SelectItem({ className, children, ...props }: SelectItemProps) {
125
329
  return (
126
330
  <SelectPrimitive.Item
127
331
  data-slot="select-item"
@@ -135,9 +339,7 @@ function SelectItem({
135
339
  {children}
136
340
  </SelectPrimitive.ItemText>
137
341
  <SelectPrimitive.ItemIndicator
138
- render={
139
- <span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center" />
140
- }
342
+ render={<span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center" />}
141
343
  >
142
344
  <CheckIcon className="pointer-events-none" />
143
345
  </SelectPrimitive.ItemIndicator>
@@ -145,10 +347,7 @@ function SelectItem({
145
347
  )
146
348
  }
147
349
 
148
- function SelectSeparator({
149
- className,
150
- ...props
151
- }: SelectSeparatorProps) {
350
+ function SelectSeparator({ className, ...props }: SelectSeparatorProps) {
152
351
  return (
153
352
  <SelectPrimitive.Separator
154
353
  data-slot="select-separator"
@@ -158,10 +357,7 @@ function SelectSeparator({
158
357
  )
159
358
  }
160
359
 
161
- function SelectScrollUpButton({
162
- className,
163
- ...props
164
- }: SelectScrollUpButtonProps) {
360
+ function SelectScrollUpButton({ className, ...props }: SelectScrollUpButtonProps) {
165
361
  return (
166
362
  <SelectPrimitive.ScrollUpArrow
167
363
  data-slot="select-scroll-up-button"
@@ -176,10 +372,7 @@ function SelectScrollUpButton({
176
372
  )
177
373
  }
178
374
 
179
- function SelectScrollDownButton({
180
- className,
181
- ...props
182
- }: SelectScrollDownButtonProps) {
375
+ function SelectScrollDownButton({ className, ...props }: SelectScrollDownButtonProps) {
183
376
  return (
184
377
  <SelectPrimitive.ScrollDownArrow
185
378
  data-slot="select-scroll-down-button"
@@ -63,16 +63,17 @@ function createCodeSnippet(definition: ShowcaseDemoDefinition) {
63
63
  }
64
64
 
65
65
  const exactCodeSnippets: Record<string, string> = {
66
- "search-input": `import { SearchInput } from "tembro"
67
-
68
- export function Demo() {
69
- return (
70
- <SearchInput
71
- value="invoice"
72
- onValueChange={(value) => console.log(value)}
73
- placeholder="Search invoices..."
74
- resultCount={12}
75
- shortcut="Ctrl K"
66
+ "search-input": `import { Input } from "tembro"
67
+
68
+ export function Demo() {
69
+ return (
70
+ <Input
71
+ type="search"
72
+ value="invoice"
73
+ onValueChange={(value) => console.log(value)}
74
+ placeholder="Search invoices..."
75
+ resultCount={12}
76
+ shortcut="Ctrl K"
76
77
  />
77
78
  )
78
79
  }`,
@@ -133,12 +134,12 @@ export function Demo() {
133
134
  />
134
135
  )
135
136
  }`,
136
- "filter-bar": `import { Button, FilterBar, SearchInput } from "tembro"
137
-
138
- export function Demo() {
139
- return (
137
+ "filter-bar": `import { Button, FilterBar, Input } from "tembro"
138
+
139
+ export function Demo() {
140
+ return (
140
141
  <FilterBar
141
- search={<SearchInput value="" placeholder="Search rows..." readOnly />}
142
+ search={<Input type="search" value="" placeholder="Search rows..." readOnly />}
142
143
  chips={[
143
144
  { key: "status", label: "Status", value: "Active", tone: "success" },
144
145
  { key: "owner", label: "Owner", value: "Azamat" },
@@ -7,7 +7,7 @@ import {
7
7
  DataTable,
8
8
  DataTableColumnVisibilityMenu,
9
9
  DataTableSortableHeader,
10
- SimpleSelect,
10
+ Select,
11
11
  } from "@/index"
12
12
  import { createDataTableActionsColumn } from "@/components/data-table/data-table-actions-column"
13
13
  import { createDataTableSelectColumn } from "@/components/data-table/data-table-select-column"
@@ -127,7 +127,7 @@ export function DataTableShowcase() {
127
127
  }}
128
128
  filters={
129
129
  <>
130
- <SimpleSelect
130
+ <Select
131
131
  value={statusFilter}
132
132
  onValueChange={(value) => setStatusFilter(value ?? "all")}
133
133
  options={[
@@ -138,7 +138,7 @@ export function DataTableShowcase() {
138
138
  ]}
139
139
  triggerClassName="min-w-40"
140
140
  />
141
- <SimpleSelect
141
+ <Select
142
142
  value={ownerFilter}
143
143
  onValueChange={(value) => setOwnerFilter(value ?? "all")}
144
144
  options={[
@@ -9,7 +9,7 @@ import {
9
9
  DataTableColumnVisibilityMenu,
10
10
  DataTableSortableHeader,
11
11
  DataTableViewPresets,
12
- SearchInput,
12
+ Input,
13
13
  } from "@/index"
14
14
  import { createDataTableActionsColumn } from "@/components/data-table/data-table-actions-column"
15
15
  import { DataTableSavedFilters } from "@/components/data-table/data-table-saved-filters"
@@ -147,7 +147,7 @@ export function DataTablePartShowcase({
147
147
  size="sm"
148
148
  />
149
149
  <div className="w-full max-w-[320px]">
150
- <SearchInput value={search} onValueChange={setSearch} placeholder="Search invoice, customer..." />
150
+ <Input type="search" value={search} onValueChange={setSearch} placeholder="Search invoice, customer..." />
151
151
  </div>
152
152
  </div>
153
153
  </div>
@@ -168,7 +168,7 @@ export function DataTablePartShowcase({
168
168
  toolbarProps={(table) => ({
169
169
  title: "Invoices",
170
170
  description: "Premium subroute preview for reusable DataTable parts.",
171
- search: <SearchInput value={search} onValueChange={setSearch} placeholder="Search rows..." />,
171
+ search: <Input type="search" value={search} onValueChange={setSearch} placeholder="Search rows..." />,
172
172
  actions: (
173
173
  <div className="flex items-center gap-2">
174
174
  <DataTableColumnVisibilityMenu table={table} />
@@ -1,7 +1,7 @@
1
1
  import type { ComponentDemoMock } from "../types"
2
2
 
3
3
  export const selectMock: ComponentDemoMock = {
4
- code: `import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from "tembro"\n\nexport function Example() {\n return (\n <Select defaultValue="starter">\n <SelectTrigger><SelectValue /></SelectTrigger>\n <SelectContent>\n <SelectItem value="starter">Starter</SelectItem>\n </SelectContent>\n </Select>\n )\n}`,
4
+ code: `import { Select } from "tembro"\n\nconst planOptions = [\n { value: "starter", label: "Starter" },\n { value: "growth", label: "Growth" },\n { value: "enterprise", label: "Enterprise" },\n]\n\nexport function Example() {\n return <Select defaultValue="starter" options={planOptions} />\n}`,
5
5
  cliCommand: "npx tembro add select",
6
6
  highlights: ["Primary selection surface", "Controlled selection", "Searchable local flow", "Compact trigger options"],
7
7
  relatedBlockSlugs: ["crm-dashboard", "users-table", "dashboard-starter"],
@@ -1,6 +1,6 @@
1
1
  import * as React from "react"
2
2
 
3
- import { Badge, Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/index"
3
+ import { Badge, Button, Select } from "@/index"
4
4
 
5
5
  import type { ComponentDemoProps } from "../types"
6
6
 
@@ -75,27 +75,19 @@ export function SelectShowcase({ state, setState }: ComponentDemoProps) {
75
75
  <p className="text-sm font-medium aui-text-muted">{surface === "plan" ? "Workspace plan" : "Toolbar density"}</p>
76
76
  <div className="mt-3">
77
77
  {surface === "plan" ? (
78
- <Select value={selectedValue} onValueChange={(value: string | null | undefined) => setState({ selectValue: value ?? planOptions[0].value })}>
79
- <SelectTrigger className="w-full">
80
- <SelectValue />
81
- </SelectTrigger>
82
- <SelectContent>
83
- {planOptions.map((option) => (
84
- <SelectItem key={option.value} value={option.value}>{option.label}</SelectItem>
85
- ))}
86
- </SelectContent>
87
- </Select>
78
+ <Select
79
+ value={selectedValue}
80
+ onValueChange={(value) => setState({ selectValue: value ?? planOptions[0].value })}
81
+ options={planOptions}
82
+ triggerClassName="w-full"
83
+ />
88
84
  ) : (
89
- <Select defaultValue={compactOptions[0].value}>
90
- <SelectTrigger size="sm" className="w-full">
91
- <SelectValue />
92
- </SelectTrigger>
93
- <SelectContent>
94
- {compactOptions.map((option) => (
95
- <SelectItem key={option.value} value={option.value}>{option.label}</SelectItem>
96
- ))}
97
- </SelectContent>
98
- </Select>
85
+ <Select
86
+ defaultValue={compactOptions[0].value}
87
+ options={compactOptions}
88
+ size="sm"
89
+ triggerClassName="w-full"
90
+ />
99
91
  )}
100
92
  </div>
101
93
  </div>