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