@gentleduck/registry-ui 0.2.6 → 0.2.8

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.
@@ -32,77 +32,97 @@ export type ComboboxProps<TData extends readonly ComboboxItemType[], TType exten
32
32
  children: (item: TData) => React.ReactNode
33
33
  }
34
34
 
35
- export function Combobox<TData extends readonly ComboboxItemType[], TType extends 'single' | 'multiple' = 'single'>({
36
- value,
37
- defaultValue,
38
- items,
39
- command,
40
- commandInput,
41
- commandEmpty = 'Nothing found.',
42
- commandTriggerPlaceholder = 'Select item...',
43
- popover,
44
- popoverTrigger,
45
- popoverContent,
46
- withSearch = true,
47
- showSelected = true,
48
- children,
49
- }: ComboboxProps<TData, TType>) {
50
- const { dir, ...popoverProps } = popover ?? {}
51
- const direction = useDirection(dir as Direction)
52
- const MAX_SELECTION = 2
53
- const _value = value ?? defaultValue
35
+ export const Combobox = React.forwardRef<
36
+ HTMLButtonElement,
37
+ ComboboxProps<readonly ComboboxItemType[], 'single' | 'multiple'>
38
+ >(
39
+ (
40
+ {
41
+ value,
42
+ defaultValue,
43
+ items,
44
+ command,
45
+ commandInput,
46
+ commandEmpty = 'Nothing found.',
47
+ commandTriggerPlaceholder = 'Select item...',
48
+ popover,
49
+ popoverTrigger,
50
+ popoverContent,
51
+ withSearch = true,
52
+ showSelected = true,
53
+ children,
54
+ },
55
+ ref,
56
+ ) => {
57
+ const { dir, ...popoverProps } = popover ?? {}
58
+ const direction = useDirection(dir as Direction)
59
+ const MAX_SELECTION = 2
60
+ const _value = value ?? defaultValue
54
61
 
55
- return (
56
- <Popover {...popoverProps} dir={direction}>
57
- <PopoverTrigger asChild>
58
- <Button {...popoverTrigger} variant={popoverTrigger?.variant ?? 'dashed'}>
59
- {popoverTrigger?.children}
60
- {showSelected &&
61
- (_value ? (
62
- _value instanceof Array && _value.length ? (
63
- <>
64
- <Separator orientation="vertical" />
65
- <div className="flex gap-1">
66
- {_value.length > MAX_SELECTION ? (
67
- <Badge className="px-2 py-0.75 rounded-sm font-normal" variant={'secondary'}>
68
- +{_value.length} Selected
69
- </Badge>
70
- ) : (
71
- _value.map((item) => (
72
- <Badge className="px-2 py-0.5 rounded-[3px] capitalize" key={item} variant={'secondary'}>
73
- {item}
62
+ return (
63
+ <Popover {...popoverProps} dir={direction}>
64
+ <PopoverTrigger asChild>
65
+ <Button ref={ref} {...popoverTrigger} variant={popoverTrigger?.variant ?? 'dashed'}>
66
+ {popoverTrigger?.children}
67
+ {showSelected &&
68
+ (_value ? (
69
+ _value instanceof Array && _value.length ? (
70
+ <>
71
+ <Separator orientation="vertical" />
72
+ <div className="flex gap-1">
73
+ {_value.length > MAX_SELECTION ? (
74
+ <Badge className="px-2 py-0.75 rounded-sm font-normal" variant={'secondary'}>
75
+ +{_value.length} Selected
74
76
  </Badge>
75
- ))
76
- )}
77
- </div>
78
- </>
77
+ ) : (
78
+ _value.map((item) => (
79
+ <Badge className="px-2 py-0.5 rounded-[3px] capitalize" key={item} variant={'secondary'}>
80
+ {item}
81
+ </Badge>
82
+ ))
83
+ )}
84
+ </div>
85
+ </>
86
+ ) : (
87
+ _value
88
+ )
79
89
  ) : (
80
- _value
81
- )
82
- ) : (
83
- commandTriggerPlaceholder
84
- ))}
85
- </Button>
86
- </PopoverTrigger>
87
- <PopoverContent
88
- {...popoverContent}
89
- dir={direction}
90
- className={cn('w-(--gentleduck-popover-trigger-width) p-0', popoverContent?.className)}>
91
- <Command {...command}>
92
- {withSearch && <CommandInput {...commandInput} className={cn('h-8 [&_svg]:size-4.5 px-2', commandInput)} />}
93
- <CommandList>
94
- {commandEmpty && <CommandEmpty>{commandEmpty}</CommandEmpty>}
95
- {children(items)}
96
- </CommandList>
97
- </Command>
98
- </PopoverContent>
99
- </Popover>
100
- )
101
- }
90
+ commandTriggerPlaceholder
91
+ ))}
92
+ </Button>
93
+ </PopoverTrigger>
94
+ <PopoverContent
95
+ {...popoverContent}
96
+ dir={direction}
97
+ className={cn('w-(--gentleduck-popover-trigger-width) p-0', popoverContent?.className)}>
98
+ <Command {...command}>
99
+ {withSearch && <CommandInput {...commandInput} className={cn('h-8 [&_svg]:size-4.5 px-2', commandInput)} />}
100
+ <CommandList>
101
+ {commandEmpty && <CommandEmpty>{commandEmpty}</CommandEmpty>}
102
+ {children(items)}
103
+ </CommandList>
104
+ </Command>
105
+ </PopoverContent>
106
+ </Popover>
107
+ )
108
+ },
109
+ ) as <TData extends readonly ComboboxItemType[], TType extends 'single' | 'multiple' = 'single'>(
110
+ props: ComboboxProps<TData, TType> & React.RefAttributes<HTMLButtonElement>,
111
+ ) => React.ReactElement
112
+ ;(Combobox as React.FC).displayName = 'Combobox'
102
113
 
103
- export function ComboxGroup({ children, ...props }: React.ComponentPropsWithoutRef<typeof CommandGroup>) {
104
- return <CommandGroup {...props}>{children}</CommandGroup>
105
- }
114
+ const ComboxGroup = React.forwardRef<
115
+ React.ComponentRef<typeof CommandGroup>,
116
+ React.ComponentPropsWithoutRef<typeof CommandGroup>
117
+ >(({ children, ...props }, ref) => {
118
+ return (
119
+ <CommandGroup ref={ref} {...props}>
120
+ {children}
121
+ </CommandGroup>
122
+ )
123
+ })
124
+ ComboxGroup.displayName = 'ComboxGroup'
125
+ export { ComboxGroup }
106
126
 
107
127
  type ComboboxItemProps<T extends ComboboxItemType> = Omit<
108
128
  React.ComponentPropsWithoutRef<typeof CommandItem>,
@@ -113,9 +133,13 @@ type ComboboxItemProps<T extends ComboboxItemType> = Omit<
113
133
  checked?: React.ComponentPropsWithoutRef<typeof Checkbox>['checked']
114
134
  }
115
135
 
116
- export function ComboboxItem<T extends ComboboxItemType>({ item, onSelect, checked, ...props }: ComboboxItemProps<T>) {
136
+ export const ComboboxItem = React.forwardRef<
137
+ React.ComponentRef<typeof CommandItem>,
138
+ ComboboxItemProps<ComboboxItemType>
139
+ >(({ item, onSelect, checked, ...props }, ref) => {
117
140
  return (
118
141
  <CommandItem
142
+ ref={ref}
119
143
  onSelect={() => {
120
144
  onSelect?.(item.value)
121
145
  }}
@@ -129,4 +153,7 @@ export function ComboboxItem<T extends ComboboxItemType>({ item, onSelect, check
129
153
  {item?.label}
130
154
  </CommandItem>
131
155
  )
132
- }
156
+ }) as <T extends ComboboxItemType>(
157
+ props: ComboboxItemProps<T> & React.RefAttributes<React.ComponentRef<typeof CommandItem>>,
158
+ ) => React.ReactElement
159
+ ;(ComboboxItem as React.FC).displayName = 'ComboboxItem'
@@ -122,51 +122,47 @@ const CommandSeparator = React.forwardRef<
122
122
  ))
123
123
  CommandSeparator.displayName = CommandPrimitive.Separator.displayName
124
124
 
125
- function CommandShortcut({
126
- className,
127
- keys,
128
- onKeysPressed,
129
- variant = 'default',
130
- ref,
131
- ...props
132
- }: CommandBadgeProps): React.JSX.Element {
133
- const commands = React.useMemo(
134
- () =>
135
- keys && onKeysPressed
136
- ? {
137
- [keys]: {
138
- description: keys,
139
- execute: () => {
140
- onKeysPressed()
125
+ const CommandShortcut = React.forwardRef<HTMLElement, CommandBadgeProps>(
126
+ ({ className, keys, onKeysPressed, variant = 'default', ...props }, ref) => {
127
+ const commands = React.useMemo(
128
+ () =>
129
+ keys && onKeysPressed
130
+ ? {
131
+ [keys]: {
132
+ description: keys,
133
+ execute: () => {
134
+ onKeysPressed()
135
+ },
136
+ name: keys,
141
137
  },
142
- name: keys,
143
- },
144
- }
145
- : {},
146
- [keys, onKeysPressed],
147
- )
138
+ }
139
+ : {},
140
+ [keys, onKeysPressed],
141
+ )
148
142
 
149
- useKeyCommands(commands, { preventDefault: true })
143
+ useKeyCommands(commands, { preventDefault: true })
150
144
 
151
- return (
152
- <kbd
153
- className={cn(
154
- 'focus:offset-2 pointer-events-none ms-auto inline-flex cursor-none select-none items-center gap-0.5 rounded-sm px-2 py-[.12rem] font-sans text-secondary-foreground text-sm tracking-widest transition-colors focus:outline-hidden focus:ring-2 focus:ring-ring [&_svg]:size-3',
155
- variant === 'secondary' && 'bg-secondary',
156
- className,
157
- )}
158
- data-slot="command-badge"
159
- ref={ref}
160
- {...props}
161
- />
162
- )
163
- }
145
+ return (
146
+ <kbd
147
+ className={cn(
148
+ 'focus:offset-2 pointer-events-none ms-auto inline-flex cursor-none select-none items-center gap-0.5 rounded-sm px-2 py-[.12rem] font-sans text-secondary-foreground text-sm tracking-widest transition-colors focus:outline-hidden focus:ring-2 focus:ring-ring [&_svg]:size-3',
149
+ variant === 'secondary' && 'bg-secondary',
150
+ className,
151
+ )}
152
+ data-slot="command-badge"
153
+ ref={ref}
154
+ {...props}
155
+ />
156
+ )
157
+ },
158
+ )
159
+ CommandShortcut.displayName = 'CommandShortcut'
164
160
 
165
161
  function CommandDialog({
166
162
  children,
167
163
  shouldFilter,
168
164
  ...props
169
- }: React.ComponentPropsWithRef<typeof Dialog> & { shouldFilter?: boolean }): React.JSX.Element {
165
+ }: React.ComponentPropsWithoutRef<typeof Dialog> & { shouldFilter?: boolean }) {
170
166
  return (
171
167
  <Dialog {...props}>
172
168
  <DialogContent className="h-125 max-w-full p-0 lg:w-[700px]">
@@ -179,6 +175,7 @@ function CommandDialog({
179
175
  </Dialog>
180
176
  )
181
177
  }
178
+ CommandDialog.displayName = 'CommandDialog'
182
179
 
183
180
  function useCommandListContext(__scopeCommand: Parameters<typeof CommandPrimitive.useCommandContext>[1]) {
184
181
  return CommandPrimitive.useCommandContext('Command', __scopeCommand)
@@ -6,16 +6,22 @@ import { Check, ChevronRight, Circle } from 'lucide-react'
6
6
  import * as React from 'react'
7
7
 
8
8
  const ContextMenu = ContextMenuPrimitive.Root
9
+ ContextMenu.displayName = 'ContextMenu'
9
10
 
10
11
  const ContextMenuTrigger = ContextMenuPrimitive.Trigger
12
+ ContextMenuTrigger.displayName = 'ContextMenuTrigger'
11
13
 
12
14
  const ContextMenuGroup = ContextMenuPrimitive.Group
15
+ ContextMenuGroup.displayName = 'ContextMenuGroup'
13
16
 
14
17
  const ContextMenuPortal = ContextMenuPrimitive.Portal
18
+ ContextMenuPortal.displayName = 'ContextMenuPortal'
15
19
 
16
20
  const ContextMenuSub = ContextMenuPrimitive.Sub
21
+ ContextMenuSub.displayName = 'ContextMenuSub'
17
22
 
18
23
  const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup
24
+ ContextMenuRadioGroup.displayName = 'ContextMenuRadioGroup'
19
25
 
20
26
  const ContextMenuSubTrigger = React.forwardRef<
21
27
  React.ComponentRef<typeof ContextMenuPrimitive.SubTrigger>,
@@ -154,9 +160,11 @@ const ContextMenuSeparator = React.forwardRef<
154
160
  ))
155
161
  ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName
156
162
 
157
- const ContextMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
158
- return <span className={cn('ms-auto text-muted-foreground text-xs tracking-widest', className)} {...props} />
159
- }
163
+ const ContextMenuShortcut = React.forwardRef<HTMLSpanElement, React.HTMLAttributes<HTMLSpanElement>>(
164
+ ({ className, ...props }, ref) => (
165
+ <span ref={ref} className={cn('ms-auto text-muted-foreground text-xs tracking-widest', className)} {...props} />
166
+ ),
167
+ )
160
168
  ContextMenuShortcut.displayName = 'ContextMenuShortcut'
161
169
 
162
170
  export {
@@ -127,6 +127,15 @@ function DialogCloseResponsive({
127
127
  return <DrawerClose {...props}>{children}</DrawerClose>
128
128
  }
129
129
 
130
+ DialogResponsive.displayName = 'DialogResponsive'
131
+ DialogTriggerResponsive.displayName = 'DialogTriggerResponsive'
132
+ DialogContentResponsive.displayName = 'DialogContentResponsive'
133
+ DialogHeaderResponsive.displayName = 'DialogHeaderResponsive'
134
+ DialogFooterResponsive.displayName = 'DialogFooterResponsive'
135
+ DialogTitleResponsive.displayName = 'DialogTitleResponsive'
136
+ DialogDescriptionResponsive.displayName = 'DialogDescriptionResponsive'
137
+ DialogCloseResponsive.displayName = 'DialogCloseResponsive'
138
+
130
139
  export {
131
140
  DialogResponsive,
132
141
  DialogTriggerResponsive,
@@ -6,12 +6,16 @@ import { X } from 'lucide-react'
6
6
  import * as React from 'react'
7
7
 
8
8
  const Dialog = DialogPrimitive.Root
9
+ Dialog.displayName = 'Dialog'
9
10
 
10
11
  const DialogTrigger = DialogPrimitive.Trigger
12
+ DialogTrigger.displayName = 'DialogTrigger'
11
13
 
12
14
  const DialogPortal = DialogPrimitive.Portal
15
+ DialogPortal.displayName = 'DialogPortal'
13
16
 
14
17
  const DialogClose = DialogPrimitive.Close
18
+ DialogClose.displayName = 'DialogClose'
15
19
 
16
20
  const DialogOverlay = React.forwardRef<
17
21
  React.ComponentRef<typeof DialogPrimitive.Overlay>,
@@ -53,13 +57,17 @@ const DialogContent = React.forwardRef<
53
57
  ))
54
58
  DialogContent.displayName = DialogPrimitive.Content.displayName
55
59
 
56
- const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
57
- <div className={cn('flex flex-col space-y-1.5 text-center sm:text-start', className)} {...props} />
60
+ const DialogHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
61
+ ({ className, ...props }, ref) => (
62
+ <div ref={ref} className={cn('flex flex-col space-y-1.5 text-center sm:text-start', className)} {...props} />
63
+ ),
58
64
  )
59
65
  DialogHeader.displayName = 'DialogHeader'
60
66
 
61
- const DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
62
- <div className={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:gap-2', className)} {...props} />
67
+ const DialogFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
68
+ ({ className, ...props }, ref) => (
69
+ <div ref={ref} className={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:gap-2', className)} {...props} />
70
+ ),
63
71
  )
64
72
  DialogFooter.displayName = 'DialogFooter'
65
73
 
@@ -6,16 +6,22 @@ import { Check, ChevronRight, Circle } from 'lucide-react'
6
6
  import * as React from 'react'
7
7
 
8
8
  const DropdownMenu = DropdownMenuPrimitive.Root
9
+ DropdownMenu.displayName = 'DropdownMenu'
9
10
 
10
11
  const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
12
+ DropdownMenuTrigger.displayName = 'DropdownMenuTrigger'
11
13
 
12
14
  const DropdownMenuGroup = DropdownMenuPrimitive.Group
15
+ DropdownMenuGroup.displayName = 'DropdownMenuGroup'
13
16
 
14
17
  const DropdownMenuPortal = DropdownMenuPrimitive.Portal
18
+ DropdownMenuPortal.displayName = 'DropdownMenuPortal'
15
19
 
16
20
  const DropdownMenuSub = DropdownMenuPrimitive.Sub
21
+ DropdownMenuSub.displayName = 'DropdownMenuSub'
17
22
 
18
23
  const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
24
+ DropdownMenuRadioGroup.displayName = 'DropdownMenuRadioGroup'
19
25
 
20
26
  const DropdownMenuSubTrigger = React.forwardRef<
21
27
  React.ComponentRef<typeof DropdownMenuPrimitive.SubTrigger>,
@@ -157,9 +163,11 @@ const DropdownMenuSeparator = React.forwardRef<
157
163
  ))
158
164
  DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
159
165
 
160
- const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
161
- return <span className={cn('ms-auto text-xs tracking-widest opacity-60', className)} {...props} />
162
- }
166
+ const DropdownMenuShortcut = React.forwardRef<HTMLSpanElement, React.HTMLAttributes<HTMLSpanElement>>(
167
+ ({ className, ...props }, ref) => (
168
+ <span ref={ref} className={cn('ms-auto text-xs tracking-widest opacity-60', className)} {...props} />
169
+ ),
170
+ )
163
171
  DropdownMenuShortcut.displayName = 'DropdownMenuShortcut'
164
172
 
165
173
  export {
@@ -1,12 +1,14 @@
1
1
  import { cn } from '@gentleduck/libs/cn'
2
2
  import { type Direction, useDirection } from '@gentleduck/primitives/direction'
3
3
  import type { VariantProps } from '@gentleduck/variants'
4
+ import React from 'react'
4
5
  import { emptyMediaVariants } from './empty.constants'
5
6
 
6
- function Empty({ className, dir, ...props }: React.ComponentProps<'div'>) {
7
+ const Empty = React.forwardRef<HTMLDivElement, React.ComponentProps<'div'>>(({ className, dir, ...props }, ref) => {
7
8
  const direction = useDirection(dir as Direction)
8
9
  return (
9
10
  <div
11
+ ref={ref}
10
12
  className={cn(
11
13
  'flex min-w-0 flex-1 flex-col items-center justify-center gap-6 text-balance rounded-lg border-dashed p-6 text-center md:p-12',
12
14
  className,
@@ -16,40 +18,48 @@ function Empty({ className, dir, ...props }: React.ComponentProps<'div'>) {
16
18
  {...props}
17
19
  />
18
20
  )
19
- }
21
+ })
22
+ Empty.displayName = 'Empty'
20
23
 
21
- function EmptyHeader({ className, ...props }: React.ComponentProps<'div'>) {
24
+ const EmptyHeader = React.forwardRef<HTMLDivElement, React.ComponentProps<'div'>>(({ className, ...props }, ref) => {
22
25
  return (
23
26
  <div
27
+ ref={ref}
24
28
  className={cn('flex max-w-sm flex-col items-center gap-2 text-center', className)}
25
29
  data-slot="empty-header"
26
30
  {...props}
27
31
  />
28
32
  )
29
- }
33
+ })
34
+ EmptyHeader.displayName = 'EmptyHeader'
30
35
 
31
- function EmptyMedia({
32
- className,
33
- variant = 'default',
34
- ...props
35
- }: React.ComponentProps<'div'> & VariantProps<typeof emptyMediaVariants>) {
36
+ const EmptyMedia = React.forwardRef<
37
+ HTMLDivElement,
38
+ React.ComponentProps<'div'> & VariantProps<typeof emptyMediaVariants>
39
+ >(({ className, variant = 'default', ...props }, ref) => {
36
40
  return (
37
41
  <div
42
+ ref={ref}
38
43
  className={cn(emptyMediaVariants({ className, variant }))}
39
44
  data-slot="empty-icon"
40
45
  data-variant={variant}
41
46
  {...props}
42
47
  />
43
48
  )
44
- }
49
+ })
50
+ EmptyMedia.displayName = 'EmptyMedia'
45
51
 
46
- function EmptyTitle({ className, ...props }: React.ComponentProps<'div'>) {
47
- return <div className={cn('font-medium text-lg tracking-tight', className)} data-slot="empty-title" {...props} />
48
- }
52
+ const EmptyTitle = React.forwardRef<HTMLDivElement, React.ComponentProps<'div'>>(({ className, ...props }, ref) => {
53
+ return (
54
+ <div ref={ref} className={cn('font-medium text-lg tracking-tight', className)} data-slot="empty-title" {...props} />
55
+ )
56
+ })
57
+ EmptyTitle.displayName = 'EmptyTitle'
49
58
 
50
- function EmptyDescription({ className, ...props }: React.ComponentProps<'p'>) {
59
+ const EmptyDescription = React.forwardRef<HTMLDivElement, React.ComponentProps<'p'>>(({ className, ...props }, ref) => {
51
60
  return (
52
61
  <div
62
+ ref={ref}
53
63
  className={cn(
54
64
  'text-muted-foreground text-sm/relaxed [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4',
55
65
  className,
@@ -58,16 +68,19 @@ function EmptyDescription({ className, ...props }: React.ComponentProps<'p'>) {
58
68
  {...props}
59
69
  />
60
70
  )
61
- }
71
+ })
72
+ EmptyDescription.displayName = 'EmptyDescription'
62
73
 
63
- function EmptyContent({ className, ...props }: React.ComponentProps<'div'>) {
74
+ const EmptyContent = React.forwardRef<HTMLDivElement, React.ComponentProps<'div'>>(({ className, ...props }, ref) => {
64
75
  return (
65
76
  <div
77
+ ref={ref}
66
78
  className={cn('flex w-full min-w-0 max-w-sm flex-col items-center gap-4 text-balance text-sm', className)}
67
79
  data-slot="empty-content"
68
80
  {...props}
69
81
  />
70
82
  )
71
- }
83
+ })
84
+ EmptyContent.displayName = 'EmptyContent'
72
85
 
73
86
  export { Empty, EmptyHeader, EmptyTitle, EmptyDescription, EmptyContent, EmptyMedia }