| 1 | "use client"; |
| 2 | |
| 3 | import * as React from |
| 4 | import { Combobox as |
| 5 | import { cn } |
| 6 | |
| 7 | import { Button } |
| 8 | import { |
| 9 | InputGroup, |
| 10 | InputGroupAddon, |
| 11 | InputGroupButton, |
| 12 | InputGroupInput, |
| 13 | } from "@/components/ui/input-group"; |
| 14 | import { ChevronDownIcon, XIcon |
| 15 | |
| 16 | const Combobox = ComboboxPrimitive |
| 17 | |
| 18 | function ComboboxValue({ ... |
| 19 | return <ComboboxPrimitive.Value data-slot |
| 20 | } |
| 21 | |
| 22 | function ComboboxTrigger({ |
| 23 | className, |
| 24 | children, |
| 25 | ...props |
| 26 | }: ComboboxPrimitive. |
| 27 | return ( |
| 28 | <ComboboxPrimitive.Trigger |
| 29 | data-slot="combobox-trigger" |
| 30 | className={cn(" |
| 31 | {...props} |
| 32 | > |
| 33 | {children} |
| 34 | <ChevronDownIcon className=" |
| 35 | </ComboboxPrimitive.Trigger> |
| 36 | ); |
| 37 | } |
| 38 | |
| 39 | function ComboboxClear({ className |
| 40 | return ( |
| 41 | <ComboboxPrimitive.Clear |
| 42 | data-slot="combobox-clear" |
| 43 | render={<InputGroupButton |
| 44 | className={cn(className |
| 45 | {...props} |
| 46 | > |
| 47 | <XIcon className=" |
| 48 | </ComboboxPrimitive.Clear> |
| 49 | ); |
| 50 | } |
| 51 | |
| 52 | function ComboboxInput({ |
| 53 | className, |
| 54 | children, |
| 55 | disabled = false, |
| 56 | showTrigger = true, |
| 57 | showClear = false, |
| 58 | ...props |
| 59 | }: ComboboxPrimitive. |
| 60 | showTrigger?: boolean |
| 61 | showClear?: boolean |
| 62 | }) { |
| 63 | return ( |
| 64 | <InputGroup className={ |
| 65 | <ComboboxPrimitive.Input |
| 66 | render={<InputGroupInput |
| 67 | {...props} |
| 68 | /> |
| 69 | <InputGroupAddon align=" |
| 70 | {showTrigger && ( |
| 71 | <InputGroupButton |
| 72 | size="icon-xs" |
| 73 | variant="ghost" |
| 74 | asChild |
| 75 | data-slot="input-group-button" |
| 76 | className="group-has-data-[slot=combobox-clear]/input-group:hidden data-pressed:bg-transparent" |
| 77 | disabled={disabled} |
| 78 | > |
| 79 | <ComboboxTrigger /> |
| 80 | </InputGroupButton> |
| 81 | )} |
| 82 | {showClear && <ComboboxClear |
| 83 | </InputGroupAddon> |
| 84 | {children} |
| 85 | </InputGroup> |
| 86 | ); |
| 87 | } |
| 88 | |
| 89 | function ComboboxContent({ |
| 90 | className, |
| 91 | side = "bottom" |
| 92 | sideOffset = 6, |
| 93 | align = "start" |
| 94 | alignOffset = 0, |
| 95 | anchor, |
| 96 | ...props |
| 97 | }: ComboboxPrimitive. |
| 98 | Pick< |
| 99 | ComboboxPrimitive.Positioner |
| 100 | "side" | |
| 101 | >) { |
| 102 | return ( |
| 103 | <ComboboxPrimitive.Portal> |
| 104 | <ComboboxPrimitive.Positioner |
| 105 | side={side} |
| 106 | sideOffset={sideOffset} |
| 107 | align={align} |
| 108 | alignOffset={alignOffset} |
| 109 | anchor={anchor} |
| 110 | className="isolate z-50" |
| 111 | > |
| 112 | <ComboboxPrimitive.Popup |
| 113 | data-slot="combobox-content" |
| 114 | data-chips={!!anchor} |
| 115 | className={cn( |
| 116 | "group/combobox-content relative max-h-(--available-height) w-(--anchor-width) max-w-(--available-width) min-w-[calc(var(--anchor-width)+--spacing(7))] origin-(--transform-origin) overflow-hidden rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[chips=true]:min-w-(--anchor-width) 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-[slot=input-group]:m-1 *:data-[slot=input-group]:mb-0 *:data-[slot=input-group]:h-8 *:data-[slot=input-group]:border-input/30 *:data-[slot=input-group]:bg-input/30 *:data-[slot=input-group]:shadow-none 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", |
| 117 | className, |
| 118 | )} |
| 119 | {...props} |
| 120 | /> |
| 121 | </ComboboxPrimitive.Positioner> |
| 122 | </ComboboxPrimitive.Portal> |
| 123 | ); |
| 124 | } |
| 125 | |
| 126 | function ComboboxList({ className |
| 127 | return ( |
| 128 | <ComboboxPrimitive.List |
| 129 | data-slot="combobox-list" |
| 130 | className={cn( |
| 131 | "no-scrollbar max-h-[min(calc(--spacing(72)---spacing(9)),calc(var(--available-height)---spacing(9)))] scroll-py-1 overflow-y-auto overscroll-contain p-1 data-empty:p-0", |
| 132 | className, |
| 133 | )} |
| 134 | {...props} |
| 135 | /> |
| 136 | ); |
| 137 | } |
| 138 | |
| 139 | function ComboboxItem({ |
| 140 | className, |
| 141 | children, |
| 142 | ...props |
| 143 | }: ComboboxPrimitive. |
| 144 | return ( |
| 145 | <ComboboxPrimitive.Item |
| 146 | data-slot="combobox-item" |
| 147 | className={cn( |
| 148 | "relative flex w-full cursor-default items-center gap-2 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none data-highlighted:bg-accent data-highlighted:text-accent-foreground not-data-[variant=destructive]:data-highlighted:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", |
| 149 | className, |
| 150 | )} |
| 151 | {...props} |
| 152 | > |
| 153 | {children} |
| 154 | <ComboboxPrimitive.ItemIndicator |
| 155 | render={ |
| 156 | <span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center |
| 157 | } |
| 158 | > |
| 159 | <CheckIcon className=" |
| 160 | </ComboboxPrimitive.ItemIndicator> |
| 161 | </ComboboxPrimitive.Item> |
| 162 | ); |
| 163 | } |
| 164 | |
| 165 | function ComboboxGroup({ className |
| 166 | return ( |
| 167 | <ComboboxPrimitive.Group |
| 168 | data-slot="combobox-group" |
| 169 | className={cn(className |
| 170 | {...props} |
| 171 | /> |
| 172 | ); |
| 173 | } |
| 174 | |
| 175 | function ComboboxLabel({ |
| 176 | className, |
| 177 | ...props |
| 178 | }: ComboboxPrimitive. |
| 179 | return ( |
| 180 | <ComboboxPrimitive.GroupLabel |
| 181 | data-slot="combobox-label" |
| 182 | className={cn(" |
| 183 | {...props} |
| 184 | /> |
| 185 | ); |
| 186 | } |
| 187 | |
| 188 | function ComboboxCollection({ ... |
| 189 | return ( |
| 190 | <ComboboxPrimitive.Collection data-slot=" |
| 191 | ); |
| 192 | } |
| 193 | |
| 194 | function ComboboxEmpty({ className |
| 195 | return ( |
| 196 | <ComboboxPrimitive.Empty |
| 197 | data-slot="combobox-empty" |
| 198 | className={cn( |
| 199 | "hidden w-full justify-center py-2 text-center text-sm text-muted-foreground group-data-empty/combobox-content:flex", |
| 200 | className, |
| 201 | )} |
| 202 | {...props} |
| 203 | /> |
| 204 | ); |
| 205 | } |
| 206 | |
| 207 | function ComboboxSeparator({ |
| 208 | className, |
| 209 | ...props |
| 210 | }: ComboboxPrimitive. |
| 211 | return ( |
| 212 | <ComboboxPrimitive.Separator |
| 213 | data-slot="combobox-separator" |
| 214 | className={cn(" |
| 215 | {...props} |
| 216 | /> |
| 217 | ); |
| 218 | } |
| 219 | |
| 220 | function ComboboxChips({ |
| 221 | className, |
| 222 | ...props |
| 223 | }: React. |
| 224 | ComboboxPrimitive.Chips |
| 225 | return ( |
| 226 | <ComboboxPrimitive.Chips |
| 227 | data-slot="combobox-chips" |
| 228 | className={cn( |
| 229 | "flex min-h-8 flex-wrap items-center gap-1 rounded-lg border border-input bg-transparent bg-clip-padding px-2.5 py-1 text-sm transition-colors focus-within:border-ring focus-within:ring-3 focus-within:ring-ring/50 has-aria-invalid:border-destructive has-aria-invalid:ring-3 has-aria-invalid:ring-destructive/20 has-data-[slot=combobox-chip]:px-1 dark:bg-input/30 dark:has-aria-invalid:border-destructive/50 dark:has-aria-invalid:ring-destructive/40", |
| 230 | className, |
| 231 | )} |
| 232 | {...props} |
| 233 | /> |
| 234 | ); |
| 235 | } |
| 236 | |
| 237 | function ComboboxChip({ |
| 238 | className, |
| 239 | children, |
| 240 | showRemove = true, |
| 241 | ...props |
| 242 | }: ComboboxPrimitive. |
| 243 | showRemove?: boolean |
| 244 | }) { |
| 245 | return ( |
| 246 | <ComboboxPrimitive.Chip |
| 247 | data-slot="combobox-chip" |
| 248 | className={cn( |
| 249 | "flex h-[calc(--spacing(5.25))] w-fit items-center justify-center gap-1 rounded-sm bg-muted px-1.5 text-xs font-medium whitespace-nowrap text-foreground has-disabled:pointer-events-none has-disabled:cursor-not-allowed has-disabled:opacity-50 has-data-[slot=combobox-chip-remove]:pr-0", |
| 250 | className, |
| 251 | )} |
| 252 | {...props} |
| 253 | > |
| 254 | {children} |
| 255 | {showRemove && ( |
| 256 | <ComboboxPrimitive.ChipRemove |
| 257 | render={<Button |
| 258 | className="-ml-1 opacity-50 hover:opacity-100" |
| 259 | data-slot="combobox-chip-remove" |
| 260 | > |
| 261 | <XIcon className=" |
| 262 | </ComboboxPrimitive.ChipRemove> |
| 263 | )} |
| 264 | </ComboboxPrimitive.Chip> |
| 265 | ); |
| 266 | } |
| 267 | |
| 268 | function ComboboxChipsInput({ |
| 269 | className, |
| 270 | ...props |
| 271 | }: ComboboxPrimitive. |
| 272 | return ( |
| 273 | <ComboboxPrimitive.Input |
| 274 | data-slot="combobox-chip-input" |
| 275 | className={cn(" |
| 276 | {...props} |
| 277 | /> |
| 278 | ); |
| 279 | } |
| 280 | |
| 281 | function useComboboxAnchor() { |
| 282 | return React.useRef |
| 283 | } |
| 284 | |
| 285 | export { |
| 286 | Combobox, |
| 287 | ComboboxInput, |
| 288 | ComboboxContent, |
| 289 | ComboboxList, |
| 290 | ComboboxItem, |
| 291 | ComboboxGroup, |
| 292 | ComboboxLabel, |
| 293 | ComboboxCollection, |
| 294 | ComboboxEmpty, |
| 295 | ComboboxSeparator, |
| 296 | ComboboxChips, |
| 297 | ComboboxChip, |
| 298 | ComboboxChipsInput, |
| 299 | ComboboxTrigger, |
| 300 | ComboboxValue, |
| 301 | useComboboxAnchor, |
| 302 | }; |