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.
- package/dist/components/data-table/data-table-pagination.cjs +1 -1
- package/dist/components/data-table/data-table-pagination.js +4 -4
- package/dist/components/data-table/data-table.cjs +1 -1
- package/dist/components/data-table/data-table.d.ts +2 -2
- package/dist/components/data-table/data-table.js +2 -1
- package/dist/components/display/carousel.cjs +1 -1
- package/dist/components/display/carousel.d.ts +7 -1
- package/dist/components/display/carousel.js +44 -44
- package/dist/components/display/smart-card.d.ts +1 -1
- package/dist/components/display/status-legend.d.ts +1 -1
- package/dist/components/form/form-input.cjs +1 -1
- package/dist/components/form/form-input.d.ts +3 -4
- package/dist/components/form/form-input.js +257 -257
- package/dist/components/form/form-select.cjs +1 -1
- package/dist/components/form/form-select.d.ts +2 -2
- package/dist/components/form/form-select.js +6 -6
- package/dist/components/inputs/async-select.d.ts +3 -0
- package/dist/components/inputs/search-input.d.ts +3 -0
- package/dist/components/inputs/simple-select.cjs +1 -1
- package/dist/components/inputs/simple-select.d.ts +10 -32
- package/dist/components/inputs/simple-select.js +5 -101
- package/dist/components/ui/card/index.cjs +1 -1
- package/dist/components/ui/card/index.d.ts +13 -2
- package/dist/components/ui/card/index.js +74 -49
- package/dist/components/ui/input/index.cjs +1 -1
- package/dist/components/ui/input/index.d.ts +8 -0
- package/dist/components/ui/input/index.js +63 -30
- package/dist/components/ui/select/index.cjs +1 -1
- package/dist/components/ui/select/index.d.ts +33 -1
- package/dist/components/ui/select/index.js +154 -48
- package/dist/showcase/create-demo.cjs +5 -4
- package/dist/showcase/create-demo.js +2 -2
- package/dist/showcase/premium/data-table/showcase.cjs +1 -1
- package/dist/showcase/premium/data-table/showcase.js +1 -1
- package/dist/showcase/premium/data-table-parts/showcase.cjs +1 -1
- package/dist/showcase/premium/data-table-parts/showcase.js +3 -1
- package/dist/showcase/premium/select/mock.cjs +8 -9
- package/dist/showcase/premium/select/mock.js +1 -1
- package/dist/showcase/premium/select/showcase.cjs +1 -1
- package/dist/showcase/premium/select/showcase.js +76 -86
- package/dist/showcase/registry-specific.cjs +1 -1
- package/dist/showcase/registry-specific.js +78 -74
- package/dist/showcase/render-registry-preview.cjs +1 -1
- package/dist/showcase/render-registry-preview.js +91 -87
- package/dist/showcase/tembro-registry.json.cjs +1 -1
- package/dist/showcase/tembro-registry.json.js +1 -1
- package/package.json +1 -1
- package/packages/cli/dist/index.cjs +1 -1
- package/packages/cli/vendor/src/components/data-table/data-table-pagination.tsx +7 -7
- package/packages/cli/vendor/src/components/data-table/data-table.tsx +21 -20
- package/packages/cli/vendor/src/components/display/carousel.tsx +18 -6
- package/packages/cli/vendor/src/components/display/smart-card.tsx +1 -1
- package/packages/cli/vendor/src/components/display/status-legend.tsx +1 -1
- package/packages/cli/vendor/src/components/form/form-input.tsx +9 -12
- package/packages/cli/vendor/src/components/form/form-select.tsx +4 -4
- package/packages/cli/vendor/src/components/inputs/async-select.tsx +11 -8
- package/packages/cli/vendor/src/components/inputs/search-input.tsx +5 -2
- package/packages/cli/vendor/src/components/inputs/simple-select.tsx +20 -190
- package/packages/cli/vendor/src/components/ui/card/index.tsx +95 -25
- package/packages/cli/vendor/src/components/ui/input/index.tsx +98 -0
- package/packages/cli/vendor/src/components/ui/select/index.tsx +227 -34
- package/packages/cli/vendor/src/showcase/create-demo.tsx +16 -15
- package/packages/cli/vendor/src/showcase/premium/data-table/showcase.tsx +3 -3
- package/packages/cli/vendor/src/showcase/premium/data-table-parts/showcase.tsx +3 -3
- package/packages/cli/vendor/src/showcase/premium/select/mock.ts +1 -1
- package/packages/cli/vendor/src/showcase/premium/select/showcase.tsx +13 -21
- package/packages/cli/vendor/src/showcase/registry-specific.tsx +7 -8
- package/packages/cli/vendor/src/showcase/render-registry-preview.tsx +5 -6
- package/packages/cli/vendor/src/showcase/tembro-registry.json +1 -1
- 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
|
-
|
|
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(
|
|
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(
|
|
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 {
|
|
67
|
-
|
|
68
|
-
export function Demo() {
|
|
69
|
-
return (
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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,
|
|
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={<
|
|
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
|
-
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
|
|
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
|
-
<
|
|
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: <
|
|
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
|
|
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
|
|
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
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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>
|