@pantoia/ui 0.2.0 → 0.4.0
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/accordion/index.module.css +4 -4
- package/accordion/index.tsx +30 -58
- package/alert-dialog/index.tsx +38 -44
- package/badge/index.tsx +2 -2
- package/button/index.tsx +31 -30
- package/callout/index.tsx +29 -8
- package/checkbox/index.tsx +24 -88
- package/combobox/index.tsx +133 -323
- package/command-palette/index.tsx +55 -34
- package/dialog/index.tsx +30 -26
- package/dist/index.js +155 -27
- package/drawer/index.module.css +2 -2
- package/drawer/index.tsx +54 -132
- package/dropdown-menu/index.tsx +95 -100
- package/field/index.tsx +43 -94
- package/input/index.tsx +13 -9
- package/kbd/index.tsx +2 -2
- package/label/index.tsx +7 -9
- package/number-field/index.tsx +129 -176
- package/package.json +12 -15
- package/palette.css +114 -0
- package/popover/index.tsx +62 -33
- package/radio-group/index.tsx +36 -39
- package/registry.json +65 -169
- package/select/index.tsx +154 -76
- package/switch/index.tsx +13 -6
- package/table/index.tsx +5 -6
- package/tabs/index.module.css +5 -3
- package/tabs/index.tsx +26 -77
- package/tailwind.css +3 -0
- package/textarea/index.tsx +25 -13
- package/toast/index.module.css +187 -0
- package/toast/index.tsx +141 -46
- package/toggle/index.tsx +8 -7
- package/{styles.css → tokens.css} +82 -303
- package/tooltip/index.tsx +62 -31
- package/tv.ts +0 -1
- package/utilities.css +124 -0
- package/accordion/AGENTS.md +0 -60
- package/alert-dialog/AGENTS.md +0 -78
- package/badge/AGENTS.md +0 -35
- package/button/AGENTS.md +0 -39
- package/callout/AGENTS.md +0 -41
- package/checkbox/AGENTS.md +0 -54
- package/combobox/AGENTS.md +0 -150
- package/command-palette/AGENTS.md +0 -45
- package/dialog/AGENTS.md +0 -81
- package/drawer/AGENTS.md +0 -66
- package/dropdown-menu/AGENTS.md +0 -133
- package/field/AGENTS.md +0 -96
- package/input/AGENTS.md +0 -35
- package/kbd/AGENTS.md +0 -17
- package/label/AGENTS.md +0 -21
- package/number-field/AGENTS.md +0 -53
- package/popover/AGENTS.md +0 -64
- package/press.css +0 -37
- package/radio-group/AGENTS.md +0 -81
- package/select/AGENTS.md +0 -90
- package/switch/AGENTS.md +0 -37
- package/table/AGENTS.md +0 -82
- package/tabs/AGENTS.md +0 -60
- package/test-utils.ts +0 -10
- package/textarea/AGENTS.md +0 -35
- package/toast/AGENTS.md +0 -34
- package/toggle/AGENTS.md +0 -37
- package/tooltip/AGENTS.md +0 -50
package/combobox/index.tsx
CHANGED
|
@@ -1,155 +1,24 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
createContext,
|
|
7
|
-
use,
|
|
8
|
-
useEffect,
|
|
9
|
-
useId,
|
|
10
|
-
useRef,
|
|
11
|
-
useState,
|
|
12
|
-
type ComponentProps,
|
|
13
|
-
type InputHTMLAttributes,
|
|
14
|
-
type KeyboardEvent as ReactKeyboardEvent,
|
|
15
|
-
type ReactNode,
|
|
16
|
-
} from 'react'
|
|
3
|
+
import { Combobox as ComboboxPrimitive, type ComboboxFilter, type ComboboxFilterOptions } from '@base-ui/react/combobox'
|
|
4
|
+
import { isValidElement, type ComponentProps, type ReactNode } from 'react'
|
|
17
5
|
|
|
18
6
|
import { tv } from '../tv'
|
|
19
7
|
|
|
20
|
-
|
|
21
|
-
id: string
|
|
22
|
-
value: string | undefined
|
|
23
|
-
open: boolean
|
|
24
|
-
active: string | undefined
|
|
25
|
-
search: string
|
|
26
|
-
setSearch: (search: string) => void
|
|
27
|
-
setNotify: (notify: ((search: string) => void) | undefined) => void
|
|
28
|
-
setOpen: (open: boolean) => void
|
|
29
|
-
pick: (value: string) => void
|
|
30
|
-
move: (step: number) => void
|
|
31
|
-
setActive: (value: string | undefined) => void
|
|
32
|
-
itemId: (value: string) => string
|
|
33
|
-
count: number
|
|
34
|
-
ignoreFilter: boolean
|
|
35
|
-
see: (visible: boolean) => () => void
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const USABLE = '[role="option"]:not([data-disabled])'
|
|
39
|
-
|
|
40
|
-
const Context = createContext<ComboboxContext | null>(null)
|
|
41
|
-
|
|
42
|
-
const useCombobox = () => {
|
|
43
|
-
const ctx = use(Context)
|
|
44
|
-
if (!ctx) throw new Error('Combobox 부품은 Combobox 안에서만 선다')
|
|
45
|
-
return ctx
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export interface ComboboxProps {
|
|
49
|
-
value?: string
|
|
50
|
-
defaultValue?: string
|
|
51
|
-
onValueChange?: (value: string) => void
|
|
52
|
-
open?: boolean
|
|
53
|
-
defaultOpen?: boolean
|
|
54
|
-
onOpenChange?: (open: boolean) => void
|
|
55
|
-
ignoreFilter?: boolean
|
|
8
|
+
export type ComboboxProps<Value = string, Item = Value> = Omit<ComboboxPrimitive.Root.Props<Value, false, Item>, 'className'> & {
|
|
56
9
|
className?: string
|
|
57
|
-
children?: ReactNode
|
|
58
10
|
}
|
|
59
11
|
|
|
60
|
-
export function Combobox({
|
|
61
|
-
|
|
62
|
-
defaultValue,
|
|
63
|
-
onValueChange,
|
|
64
|
-
open,
|
|
65
|
-
defaultOpen = false,
|
|
66
|
-
onOpenChange,
|
|
67
|
-
ignoreFilter = true,
|
|
12
|
+
export function Combobox<Value = string, Item = Value>({
|
|
13
|
+
openOnInputClick = false,
|
|
68
14
|
className,
|
|
69
15
|
children,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const [ownValue, setOwnValue] = useState(defaultValue)
|
|
73
|
-
const [ownOpen, setOwnOpen] = useState(defaultOpen)
|
|
74
|
-
const [active, setActive] = useState<string | undefined>(undefined)
|
|
75
|
-
const [search, setSearch] = useState('')
|
|
76
|
-
const [count, setCount] = useState(0)
|
|
77
|
-
const [seen, setSeen] = useState(false)
|
|
78
|
-
const notifyRef = useRef<((search: string) => void) | undefined>(undefined)
|
|
79
|
-
|
|
80
|
-
const isOpen = open ?? ownOpen
|
|
81
|
-
const current = value ?? ownValue
|
|
82
|
-
|
|
83
|
-
if (seen !== isOpen) {
|
|
84
|
-
setSeen(isOpen)
|
|
85
|
-
if (!isOpen) setActive(undefined)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const setOpen = (next: boolean) => {
|
|
89
|
-
if (open === undefined) setOwnOpen(next)
|
|
90
|
-
onOpenChange?.(next)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const setNotify = (next: ((search: string) => void) | undefined) => {
|
|
94
|
-
notifyRef.current = next
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const changeSearch = (next: string) => {
|
|
98
|
-
setSearch(next)
|
|
99
|
-
notifyRef.current?.(next)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const pick = (next: string) => {
|
|
103
|
-
if (value === undefined) setOwnValue(next)
|
|
104
|
-
onValueChange?.(next)
|
|
105
|
-
changeSearch(next)
|
|
106
|
-
setOpen(false)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const see = (visible: boolean) => {
|
|
110
|
-
if (!visible) return () => {}
|
|
111
|
-
setCount(n => n + 1)
|
|
112
|
-
return () => setCount(n => n - 1)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const move = (step: number) => {
|
|
116
|
-
const list = document.getElementById(`${id}-list`)
|
|
117
|
-
if (!list) return
|
|
118
|
-
|
|
119
|
-
const usable = [...list.querySelectorAll<HTMLElement>(USABLE)].map(el => el.dataset.value!)
|
|
120
|
-
if (usable.length === 0) return
|
|
121
|
-
|
|
122
|
-
const at = active === undefined ? -1 : usable.indexOf(active)
|
|
123
|
-
const next = at === -1
|
|
124
|
-
? (step > 0 ? 0 : usable.length - 1)
|
|
125
|
-
: (at + step + usable.length) % usable.length
|
|
126
|
-
|
|
127
|
-
setActive(usable[next])
|
|
128
|
-
}
|
|
129
|
-
|
|
16
|
+
...rest
|
|
17
|
+
}: ComboboxProps<Value, Item>) {
|
|
130
18
|
return (
|
|
131
|
-
<
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
open: isOpen,
|
|
135
|
-
active,
|
|
136
|
-
search,
|
|
137
|
-
setSearch: changeSearch,
|
|
138
|
-
setNotify,
|
|
139
|
-
setOpen,
|
|
140
|
-
pick,
|
|
141
|
-
see,
|
|
142
|
-
move,
|
|
143
|
-
setActive,
|
|
144
|
-
itemId: (item: string) => `${id}-${item}`,
|
|
145
|
-
count,
|
|
146
|
-
ignoreFilter,
|
|
147
|
-
}}
|
|
148
|
-
>
|
|
149
|
-
<PopoverPrimitive.Root open={isOpen} onOpenChange={setOpen}>
|
|
150
|
-
<div className={className}>{children}</div>
|
|
151
|
-
</PopoverPrimitive.Root>
|
|
152
|
-
</Context>
|
|
19
|
+
<ComboboxPrimitive.Root<Value, false, Item> {...rest} openOnInputClick={openOnInputClick}>
|
|
20
|
+
<div className={className}>{children}</div>
|
|
21
|
+
</ComboboxPrimitive.Root>
|
|
153
22
|
)
|
|
154
23
|
}
|
|
155
24
|
|
|
@@ -164,7 +33,7 @@ export const comboboxAnchor = tv({
|
|
|
164
33
|
variants: {
|
|
165
34
|
surface: {
|
|
166
35
|
bare: 'ring-1 ring-inset ring-line-strong',
|
|
167
|
-
well: 'bg-
|
|
36
|
+
well: 'bg-bg-well',
|
|
168
37
|
},
|
|
169
38
|
size: { sm: 'h-8', md: 'h-9', lg: 'h-10 [&_input]:text-t16' },
|
|
170
39
|
},
|
|
@@ -172,238 +41,166 @@ export const comboboxAnchor = tv({
|
|
|
172
41
|
defaultVariants: { surface: 'bare', size: 'md' },
|
|
173
42
|
})
|
|
174
43
|
|
|
175
|
-
export
|
|
44
|
+
export type ComboboxAnchorProps = Omit<ComponentProps<typeof ComboboxPrimitive.InputGroup>, 'className'> & {
|
|
176
45
|
surface?: 'bare' | 'well'
|
|
177
46
|
size?: 'sm' | 'md' | 'lg'
|
|
178
47
|
className?: string
|
|
179
|
-
children?: ReactNode
|
|
180
48
|
}
|
|
181
49
|
|
|
182
|
-
export function ComboboxAnchor({ surface, size, className,
|
|
183
|
-
return (
|
|
184
|
-
<PopoverPrimitive.Anchor className={comboboxAnchor({ surface, size, className })}>
|
|
185
|
-
{children}
|
|
186
|
-
</PopoverPrimitive.Anchor>
|
|
187
|
-
)
|
|
50
|
+
export function ComboboxAnchor({ surface, size, className, ...rest }: ComboboxAnchorProps) {
|
|
51
|
+
return <ComboboxPrimitive.InputGroup {...rest} className={comboboxAnchor({ surface, size, className })} />
|
|
188
52
|
}
|
|
189
53
|
|
|
190
|
-
export
|
|
191
|
-
|
|
192
|
-
onValueChange?: (value: string) => void
|
|
54
|
+
export type ComboboxInputProps = Omit<ComponentProps<typeof ComboboxPrimitive.Input>, 'className'> & {
|
|
55
|
+
className?: string
|
|
193
56
|
}
|
|
194
57
|
|
|
195
58
|
const comboboxInput = tv({
|
|
196
|
-
base: 'text-value text-
|
|
59
|
+
base: 'text-value text-fg placeholder:text-fg-placeholder'
|
|
197
60
|
+ ' w-full min-w-0 bg-transparent outline-none'
|
|
198
|
-
+ ' disabled:text-
|
|
61
|
+
+ ' disabled:text-fg-disabled disabled:cursor-not-allowed',
|
|
199
62
|
})
|
|
200
63
|
|
|
201
|
-
export function ComboboxInput({
|
|
202
|
-
|
|
203
|
-
const text = value ?? ctx.search
|
|
204
|
-
const { setNotify } = ctx
|
|
205
|
-
|
|
206
|
-
useEffect(() => {
|
|
207
|
-
setNotify(onValueChange)
|
|
208
|
-
return () => setNotify(undefined)
|
|
209
|
-
})
|
|
210
|
-
|
|
211
|
-
const change = (next: string) => {
|
|
212
|
-
ctx.setSearch(next)
|
|
213
|
-
if (!ctx.open) ctx.setOpen(true)
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const onKeyDown = (e: ReactKeyboardEvent<HTMLInputElement>) => {
|
|
217
|
-
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
|
|
218
|
-
e.preventDefault()
|
|
219
|
-
if (!ctx.open) ctx.setOpen(true)
|
|
220
|
-
else ctx.move(e.key === 'ArrowDown' ? 1 : -1)
|
|
221
|
-
return
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
if (e.key === 'Enter' && ctx.open && ctx.active !== undefined) {
|
|
225
|
-
e.preventDefault()
|
|
226
|
-
ctx.pick(ctx.active)
|
|
227
|
-
return
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
if (e.key === 'Escape' && ctx.open) {
|
|
231
|
-
e.preventDefault()
|
|
232
|
-
ctx.setOpen(false)
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
return (
|
|
237
|
-
<input
|
|
238
|
-
{...rest}
|
|
239
|
-
role="combobox"
|
|
240
|
-
aria-expanded={ctx.open}
|
|
241
|
-
aria-controls={ctx.open ? `${ctx.id}-list` : undefined}
|
|
242
|
-
aria-autocomplete="list"
|
|
243
|
-
aria-activedescendant={ctx.active === undefined ? undefined : ctx.itemId(ctx.active)}
|
|
244
|
-
value={text}
|
|
245
|
-
onChange={e => change(e.target.value)}
|
|
246
|
-
onKeyDown={onKeyDown}
|
|
247
|
-
className={comboboxInput({ className })}
|
|
248
|
-
/>
|
|
249
|
-
)
|
|
64
|
+
export function ComboboxInput({ className, ...rest }: ComboboxInputProps) {
|
|
65
|
+
return <ComboboxPrimitive.Input {...rest} className={comboboxInput({ className })} />
|
|
250
66
|
}
|
|
251
67
|
|
|
252
68
|
const comboboxTrigger = tv({
|
|
253
|
-
base: 'text-
|
|
254
|
-
+ ' transition-state press-shrink hover:text-
|
|
69
|
+
base: 'text-fg-subtle grid size-5 shrink-0 place-items-center rounded-r8'
|
|
70
|
+
+ ' transition-state press-shrink hover:text-fg'
|
|
255
71
|
+ ' focus-visible:outline-2 focus-visible:outline-offset-2'
|
|
256
72
|
+ ' focus-visible:outline-line-focus-ring',
|
|
257
73
|
})
|
|
258
74
|
|
|
259
|
-
export
|
|
75
|
+
export type ComboboxTriggerProps = Omit<ComponentProps<typeof ComboboxPrimitive.Trigger>, 'className'> & {
|
|
260
76
|
label?: string
|
|
261
|
-
disabled?: boolean
|
|
262
77
|
className?: string
|
|
263
|
-
}
|
|
264
|
-
const ctx = useCombobox()
|
|
78
|
+
}
|
|
265
79
|
|
|
80
|
+
export function ComboboxTrigger({ label = '목록 열기', className, children, ...rest }: ComboboxTriggerProps) {
|
|
266
81
|
return (
|
|
267
|
-
<
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
className={comboboxTrigger({ className })}
|
|
275
|
-
>
|
|
276
|
-
<ChevronDown
|
|
277
|
-
className="size-4 transition-transform open:rotate-180 motion-reduce:transition-none"
|
|
278
|
-
/>
|
|
279
|
-
</button>
|
|
82
|
+
<ComboboxPrimitive.Trigger aria-label={label} {...rest} className={comboboxTrigger({ className })}>
|
|
83
|
+
{children ?? (
|
|
84
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" className="size-4 transition-transform in-data-popup-open:rotate-180 motion-reduce:transition-none">
|
|
85
|
+
<path d="m6 9 6 6 6-6" />
|
|
86
|
+
</svg>
|
|
87
|
+
)}
|
|
88
|
+
</ComboboxPrimitive.Trigger>
|
|
280
89
|
)
|
|
281
90
|
}
|
|
282
91
|
|
|
283
92
|
const comboboxCancel = tv({
|
|
284
|
-
base: 'text-
|
|
285
|
-
+ ' transition-state press-shrink hover:text-
|
|
93
|
+
base: 'text-fg-subtle grid size-5 shrink-0 place-items-center rounded-full'
|
|
94
|
+
+ ' transition-state press-shrink hover:text-fg'
|
|
286
95
|
+ ' focus-visible:outline-2 focus-visible:outline-offset-2'
|
|
287
96
|
+ ' focus-visible:outline-line-focus-ring',
|
|
288
97
|
})
|
|
289
98
|
|
|
290
|
-
export
|
|
291
|
-
|
|
99
|
+
export type ComboboxCancelProps = Omit<ComponentProps<typeof ComboboxPrimitive.Clear>, 'className'> & {
|
|
100
|
+
label?: string
|
|
101
|
+
className?: string
|
|
102
|
+
}
|
|
292
103
|
|
|
104
|
+
export function ComboboxCancel({ label = '지우기', keepMounted = true, className, children, ...rest }: ComboboxCancelProps) {
|
|
293
105
|
return (
|
|
294
|
-
<
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
</button>
|
|
106
|
+
<ComboboxPrimitive.Clear aria-label={label} keepMounted={keepMounted} {...rest} className={comboboxCancel({ className })}>
|
|
107
|
+
{children ?? (
|
|
108
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" className="size-4">
|
|
109
|
+
<path d="M18 6 6 18" />
|
|
110
|
+
<path d="m6 6 12 12" />
|
|
111
|
+
</svg>
|
|
112
|
+
)}
|
|
113
|
+
</ComboboxPrimitive.Clear>
|
|
303
114
|
)
|
|
304
115
|
}
|
|
305
116
|
|
|
306
|
-
export type ComboboxContentProps =
|
|
307
|
-
|
|
117
|
+
export type ComboboxContentProps = Omit<ComponentProps<typeof ComboboxPrimitive.Popup>, 'className' | 'children'> & {
|
|
118
|
+
className?: string
|
|
119
|
+
children?: ComboboxPrimitive.List.Props['children'] | ComboboxPrimitive.List.Props['children'][]
|
|
120
|
+
}
|
|
308
121
|
|
|
309
122
|
const comboboxContent = tv({
|
|
310
|
-
base: 'float text-
|
|
311
|
-
+ ' w-(--
|
|
123
|
+
base: 'float text-fg rounded-r12 z-overlay'
|
|
124
|
+
+ ' w-(--anchor-width)'
|
|
312
125
|
+ ' open:animate-enter closed:animate-exit',
|
|
313
126
|
})
|
|
314
127
|
|
|
315
|
-
|
|
316
|
-
|
|
128
|
+
const isEmpty = (child: unknown) => isValidElement(child) && child.type === ComboboxEmpty
|
|
129
|
+
|
|
130
|
+
export function ComboboxContent({ 'aria-label': label, className, children, ...rest }: ComboboxContentProps) {
|
|
131
|
+
const nodes = Array.isArray(children) ? children : [children]
|
|
132
|
+
const empty = nodes.filter(isEmpty) as ReactNode[]
|
|
133
|
+
const list = nodes.filter(node => !isEmpty(node))
|
|
317
134
|
|
|
318
135
|
return (
|
|
319
|
-
<
|
|
320
|
-
<
|
|
321
|
-
{...rest}
|
|
136
|
+
<ComboboxPrimitive.Portal>
|
|
137
|
+
<ComboboxPrimitive.Positioner
|
|
322
138
|
align="start"
|
|
323
139
|
sideOffset={4}
|
|
324
|
-
|
|
325
|
-
className={comboboxContent({ className })}
|
|
140
|
+
className="z-overlay min-w-max will-change-transform"
|
|
326
141
|
>
|
|
327
|
-
<
|
|
328
|
-
{
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
142
|
+
<ComboboxPrimitive.Popup {...rest} className={comboboxContent({ className })}>
|
|
143
|
+
{empty}
|
|
144
|
+
<ComboboxViewport aria-label={label} className="max-h-72">
|
|
145
|
+
{list.length === 1 ? list[0] : list as ReactNode[]}
|
|
146
|
+
</ComboboxViewport>
|
|
147
|
+
</ComboboxPrimitive.Popup>
|
|
148
|
+
</ComboboxPrimitive.Positioner>
|
|
149
|
+
</ComboboxPrimitive.Portal>
|
|
332
150
|
)
|
|
333
151
|
}
|
|
334
152
|
|
|
335
|
-
const comboboxViewport = tv({ base: 'flex flex-col gap-px overflow-y-auto p-1' })
|
|
153
|
+
const comboboxViewport = tv({ base: 'flex flex-col gap-px overflow-y-auto p-1 data-empty:p-0' })
|
|
336
154
|
|
|
337
|
-
export
|
|
338
|
-
label?: string
|
|
155
|
+
export type ComboboxViewportProps = Omit<ComponentProps<typeof ComboboxPrimitive.List>, 'className'> & {
|
|
339
156
|
className?: string
|
|
340
|
-
|
|
341
|
-
}) {
|
|
342
|
-
const ctx = useCombobox()
|
|
157
|
+
}
|
|
343
158
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
id={`${ctx.id}-list`}
|
|
347
|
-
role={ctx.count > 0 ? 'listbox' : undefined}
|
|
348
|
-
aria-label={ctx.count > 0 ? label : undefined}
|
|
349
|
-
className={comboboxViewport({ className })}
|
|
350
|
-
>
|
|
351
|
-
{children}
|
|
352
|
-
</div>
|
|
353
|
-
)
|
|
159
|
+
export function ComboboxViewport({ className, ...rest }: ComboboxViewportProps) {
|
|
160
|
+
return <ComboboxPrimitive.List {...rest} className={comboboxViewport({ className })} />
|
|
354
161
|
}
|
|
355
162
|
|
|
356
|
-
export
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
163
|
+
export type ComboboxCollectionProps = ComboboxPrimitive.Collection.Props
|
|
164
|
+
|
|
165
|
+
export function ComboboxCollection(props: ComboboxCollectionProps) {
|
|
166
|
+
return <ComboboxPrimitive.Collection {...props} />
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export type ComboboxItemProps = Omit<ComponentProps<typeof ComboboxPrimitive.Item>, 'className'> & {
|
|
360
170
|
className?: string
|
|
361
|
-
children?: ReactNode
|
|
362
171
|
}
|
|
363
172
|
|
|
364
173
|
const comboboxItem = tv({
|
|
365
174
|
base: 'flex min-h-9 cursor-default items-center justify-between gap-stack'
|
|
366
175
|
+ ' shrink-0 rounded-r8 px-2 py-1 transition-state'
|
|
367
176
|
+ ' data-highlighted:bg-bg-neutral-hover data-highlighted:outline-none'
|
|
368
|
-
+ ' data-disabled:text-
|
|
177
|
+
+ ' data-disabled:text-fg-disabled data-disabled:pointer-events-none',
|
|
369
178
|
})
|
|
370
179
|
|
|
371
|
-
export function ComboboxItem({
|
|
372
|
-
const ctx = useCombobox()
|
|
373
|
-
const shown = ctx.ignoreFilter || contains(textValue ?? value, ctx.search)
|
|
374
|
-
|
|
375
|
-
useEffect(() => ctx.see(shown), [ctx, shown])
|
|
376
|
-
|
|
377
|
-
if (!shown) return null
|
|
378
|
-
|
|
180
|
+
export function ComboboxItem({ className, children, ...rest }: ComboboxItemProps) {
|
|
379
181
|
return (
|
|
380
|
-
<
|
|
381
|
-
id={ctx.itemId(value)}
|
|
382
|
-
role="option"
|
|
383
|
-
data-value={value}
|
|
384
|
-
aria-selected={ctx.value === value}
|
|
385
|
-
data-highlighted={ctx.active === value ? '' : undefined}
|
|
386
|
-
data-disabled={disabled ? '' : undefined}
|
|
387
|
-
onPointerMove={() => !disabled && ctx.setActive(value)}
|
|
388
|
-
onClick={() => !disabled && ctx.pick(value)}
|
|
389
|
-
className={comboboxItem({ className })}
|
|
390
|
-
>
|
|
182
|
+
<ComboboxPrimitive.Item {...rest} className={comboboxItem({ className })}>
|
|
391
183
|
<span className="flex min-w-0 flex-col gap-0.5">
|
|
392
184
|
{children}
|
|
393
185
|
</span>
|
|
394
186
|
|
|
395
|
-
|
|
396
|
-
<
|
|
397
|
-
<
|
|
398
|
-
</
|
|
399
|
-
|
|
400
|
-
</
|
|
187
|
+
<ComboboxPrimitive.ItemIndicator className="text-fg-muted flex shrink-0">
|
|
188
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" className="size-4">
|
|
189
|
+
<path d="M20 6 9 17l-5-5" />
|
|
190
|
+
</svg>
|
|
191
|
+
</ComboboxPrimitive.ItemIndicator>
|
|
192
|
+
</ComboboxPrimitive.Item>
|
|
401
193
|
)
|
|
402
194
|
}
|
|
403
195
|
|
|
404
|
-
|
|
196
|
+
export interface ComboboxItemTextProps {
|
|
197
|
+
className?: string
|
|
198
|
+
children?: ReactNode
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const comboboxItemText = tv({ base: 'text-label text-fg' })
|
|
405
202
|
|
|
406
|
-
export function ComboboxItemText({ className, children }:
|
|
203
|
+
export function ComboboxItemText({ className, children }: ComboboxItemTextProps) {
|
|
407
204
|
return (
|
|
408
205
|
<span className={comboboxItemText({ className })}>
|
|
409
206
|
{children}
|
|
@@ -411,9 +208,14 @@ export function ComboboxItemText({ className, children }: { className?: string,
|
|
|
411
208
|
)
|
|
412
209
|
}
|
|
413
210
|
|
|
414
|
-
|
|
211
|
+
export interface ComboboxItemDescriptionProps {
|
|
212
|
+
className?: string
|
|
213
|
+
children?: ReactNode
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const comboboxItemDescription = tv({ base: 'text-caption text-fg-subtle' })
|
|
415
217
|
|
|
416
|
-
export function ComboboxItemDescription({ className, children }:
|
|
218
|
+
export function ComboboxItemDescription({ className, children }: ComboboxItemDescriptionProps) {
|
|
417
219
|
return (
|
|
418
220
|
<span className={comboboxItemDescription({ className })}>
|
|
419
221
|
{children}
|
|
@@ -421,44 +223,52 @@ export function ComboboxItemDescription({ className, children }: { className?: s
|
|
|
421
223
|
)
|
|
422
224
|
}
|
|
423
225
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
export function ComboboxGroup({ label, className, children }: {
|
|
226
|
+
export type ComboboxGroupProps = Omit<ComponentProps<typeof ComboboxPrimitive.Group>, 'className'> & {
|
|
427
227
|
label?: string
|
|
428
228
|
className?: string
|
|
429
|
-
|
|
430
|
-
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const comboboxGroup = tv({ base: 'flex flex-col gap-px' })
|
|
232
|
+
|
|
233
|
+
export function ComboboxGroup({ label, className, children, ...rest }: ComboboxGroupProps) {
|
|
431
234
|
return (
|
|
432
|
-
<
|
|
235
|
+
<ComboboxPrimitive.Group {...rest} className={comboboxGroup({ className })}>
|
|
433
236
|
{label && (
|
|
434
|
-
<
|
|
237
|
+
<ComboboxPrimitive.GroupLabel className="text-caption text-fg-subtle px-2 pb-1 pt-2">
|
|
435
238
|
{label}
|
|
436
|
-
</
|
|
239
|
+
</ComboboxPrimitive.GroupLabel>
|
|
437
240
|
)}
|
|
438
241
|
{children}
|
|
439
|
-
</
|
|
242
|
+
</ComboboxPrimitive.Group>
|
|
440
243
|
)
|
|
441
244
|
}
|
|
442
245
|
|
|
443
|
-
|
|
246
|
+
export type ComboboxEmptyProps = Omit<ComponentProps<typeof ComboboxPrimitive.Empty>, 'className'> & {
|
|
247
|
+
className?: string
|
|
248
|
+
}
|
|
444
249
|
|
|
445
|
-
|
|
446
|
-
const ctx = useCombobox()
|
|
447
|
-
if (ctx.count > 0) return null
|
|
250
|
+
const comboboxEmpty = tv({ base: 'text-caption text-fg-subtle m-1 px-2 py-3 text-center' })
|
|
448
251
|
|
|
252
|
+
export function ComboboxEmpty({ className, children, ...rest }: ComboboxEmptyProps) {
|
|
449
253
|
return (
|
|
450
|
-
<
|
|
451
|
-
{
|
|
452
|
-
|
|
254
|
+
<ComboboxPrimitive.Empty {...rest}>
|
|
255
|
+
<div className={comboboxEmpty({ className })}>
|
|
256
|
+
{children}
|
|
257
|
+
</div>
|
|
258
|
+
</ComboboxPrimitive.Empty>
|
|
453
259
|
)
|
|
454
260
|
}
|
|
455
261
|
|
|
262
|
+
export type ComboboxSeparatorProps = Omit<ComponentProps<typeof ComboboxPrimitive.Separator>, 'className'> & {
|
|
263
|
+
className?: string
|
|
264
|
+
}
|
|
265
|
+
|
|
456
266
|
const comboboxSeparator = tv({ base: 'bg-line -mx-1 my-1 h-px' })
|
|
457
267
|
|
|
458
|
-
export function ComboboxSeparator({ className }:
|
|
459
|
-
return <
|
|
268
|
+
export function ComboboxSeparator({ className, ...rest }: ComboboxSeparatorProps) {
|
|
269
|
+
return <ComboboxPrimitive.Separator {...rest} className={comboboxSeparator({ className })} />
|
|
460
270
|
}
|
|
461
271
|
|
|
462
|
-
export function
|
|
463
|
-
return
|
|
272
|
+
export function useComboboxFilter(options?: ComboboxFilterOptions): ComboboxFilter {
|
|
273
|
+
return ComboboxPrimitive.useFilter(options)
|
|
464
274
|
}
|