1
1
import { useRef } from "react" ;
2
2
import clsx from "clsx" ;
3
- import { Combobox as HeadlessCombobox , ComboboxInput , ComboboxOption , ComboboxOptions } from "@headlessui/react" ;
3
+ import {
4
+ Combobox as HeadlessCombobox ,
5
+ ComboboxInput ,
6
+ ComboboxOption ,
7
+ ComboboxOptions ,
8
+ } from "@headlessui/react" ;
9
+
4
10
import { cva } from "@/cva.config" ;
11
+
5
12
import Card from "./Card" ;
6
13
7
14
export interface ComboboxOption {
@@ -22,7 +29,7 @@ const comboboxVariants = cva({
22
29
23
30
type BaseProps = React . ComponentProps < typeof HeadlessCombobox > ;
24
31
25
- interface ComboboxProps extends Omit < BaseProps , ' displayValue' > {
32
+ interface ComboboxProps extends Omit < BaseProps , " displayValue" > {
26
33
displayValue : ( option : ComboboxOption ) => string ;
27
34
onInputChange : ( option : string ) => void ;
28
35
options : ( ) => ComboboxOption [ ] ;
@@ -48,72 +55,68 @@ export function Combobox({
48
55
const classes = comboboxVariants ( { size } ) ;
49
56
50
57
return (
51
- < HeadlessCombobox
52
- onChange = { onChange }
53
- { ...otherProps }
54
- >
58
+ < HeadlessCombobox onChange = { onChange } { ...otherProps } >
55
59
{ ( ) => (
56
60
< >
57
61
< Card className = "w-auto !border border-solid !border-slate-800/30 shadow outline-0 dark:!border-slate-300/30" >
58
62
< ComboboxInput
59
- ref = { inputRef }
60
- className = { clsx (
61
- classes ,
62
-
63
- // General styling
64
- "block w-full cursor-pointer rounded border-none py-0 font-medium shadow-none outline-0 transition duration-300" ,
65
-
66
- // Hover
67
- "hover:bg-blue-50/80 active:bg-blue-100/60" ,
68
-
69
- // Dark mode
70
- "dark:bg-slate-800 dark:text-white dark:hover:bg-slate-700 dark:active:bg-slate-800/60" ,
71
-
72
- // Focus
73
- "focus:outline-blue-600 focus:ring-2 focus:ring-blue-700 focus:ring-offset-2 dark:focus:outline-blue-500 dark:focus:ring-blue-500" ,
74
-
75
- // Disabled
76
- disabled && "pointer-events-none select-none bg-slate-50 text-slate-500/80 dark:bg-slate-800 dark:text-slate-400/80 disabled:hover:bg-white dark:disabled:hover:bg-slate-800"
77
- ) }
78
- placeholder = { disabled ? disabledMessage : placeholder }
79
- displayValue = { displayValue }
80
- onChange = { ( event ) => onInputChange ( event . target . value ) }
81
- disabled = { disabled }
63
+ ref = { inputRef }
64
+ className = { clsx (
65
+ classes ,
66
+
67
+ // General styling
68
+ "block w-full cursor-pointer rounded border-none py-0 font-medium shadow-none outline-0 transition duration-300" ,
69
+
70
+ // Hover
71
+ "hover:bg-blue-50/80 active:bg-blue-100/60" ,
72
+
73
+ // Dark mode
74
+ "dark:bg-slate-800 dark:text-white dark:hover:bg-slate-700 dark:active:bg-slate-800/60" ,
75
+
76
+ // Focus
77
+ "focus:outline-blue-600 focus:ring-2 focus:ring-blue-700 focus:ring-offset-2 dark:focus:outline-blue-500 dark:focus:ring-blue-500" ,
78
+
79
+ // Disabled
80
+ disabled &&
81
+ "pointer-events-none select-none bg-slate-50 text-slate-500/80 disabled:hover:bg-white dark:bg-slate-800 dark:text-slate-400/80 dark:disabled:hover:bg-slate-800" ,
82
+ ) }
83
+ placeholder = { disabled ? disabledMessage : placeholder }
84
+ displayValue = { displayValue }
85
+ onChange = { event => onInputChange ( event . target . value ) }
86
+ disabled = { disabled }
82
87
/>
83
88
</ Card >
84
-
89
+
85
90
{ options ( ) . length > 0 && (
86
- < ComboboxOptions className = "absolute left-0 z-[100] mt-1 w-full max-h-60 overflow-auto rounded-md bg-white py-1 text-sm shadow-lg ring-1 ring-black/5 dark:bg-slate-800 dark:ring-slate-700 hide-scrollbar " >
87
- { options ( ) . map ( ( option ) => (
88
- < ComboboxOption
89
- key = { option . value }
90
- value = { option }
91
- className = { clsx (
92
- // General styling
93
- "cursor-default select-none py-2 px-4" ,
94
-
95
- // Hover and active states
96
- "hover:bg-blue-50/80 ui-active:bg-blue-50/80 ui-active:text-blue-900" ,
97
-
98
- // Dark mode
99
- "dark:text-slate-300 dark:hover:bg-slate-700 dark:ui-active:bg-slate-700 dark:ui-active:text-blue-200"
100
- ) }
91
+ < ComboboxOptions className = "hide-scrollbar absolute left-0 z-[100] mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-sm shadow-lg ring-1 ring-black/5 dark:bg-slate-800 dark:ring-slate-700" >
92
+ { options ( ) . map ( option => (
93
+ < ComboboxOption
94
+ key = { option . value }
95
+ value = { option }
96
+ className = { clsx (
97
+ // General styling
98
+ "cursor-default select-none px-4 py-2 " ,
99
+
100
+ // Hover and active states
101
+ "hover:bg-blue-50/80 ui-active:bg-blue-50/80 ui-active:text-blue-900" ,
102
+
103
+ // Dark mode
104
+ "dark:text-slate-300 dark:hover:bg-slate-700 dark:ui-active:bg-slate-700 dark:ui-active:text-blue-200" ,
105
+ ) }
101
106
>
102
107
{ option . label }
103
108
</ ComboboxOption >
104
109
) ) }
105
110
</ ComboboxOptions >
106
111
) }
107
-
112
+
108
113
{ options ( ) . length === 0 && inputRef . current ?. value && (
109
- < div className = "absolute left-0 z-[100] mt-1 w-full rounded-md bg-white dark:bg-slate-800 py-2 px-4 text-sm shadow-lg ring-1 ring-black/5 dark:ring-slate-700" >
110
- < div className = "text-slate-500 dark:text-slate-400" >
111
- { emptyMessage }
112
- </ div >
114
+ < div className = "absolute left-0 z-[100] mt-1 w-full rounded-md bg-white px-4 py-2 text-sm shadow-lg ring-1 ring-black/5 dark:bg-slate-800 dark:ring-slate-700" >
115
+ < div className = "text-slate-500 dark:text-slate-400" > { emptyMessage } </ div >
113
116
</ div >
114
117
) }
115
118
</ >
116
119
) }
117
120
</ HeadlessCombobox >
118
121
) ;
119
- }
122
+ }
0 commit comments