@hanzo/ui 4.1.3 → 4.2.1
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/ui",
|
|
3
|
-
"version": "4.1
|
|
3
|
+
"version": "4.2.1",
|
|
4
4
|
"description": "Library that contains shared UI primitives, support for a common design system, and other boilerplate support.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/",
|
|
@@ -105,6 +105,7 @@
|
|
|
105
105
|
"devDependencies": {
|
|
106
106
|
"@mdx-js/loader": "^3.0.0",
|
|
107
107
|
"@mdx-js/react": "^3.0.0",
|
|
108
|
+
"@radix-ui/react-primitive": "^2.0.1",
|
|
108
109
|
"@types/facebook-pixel": "^0.0.30",
|
|
109
110
|
"@types/gtag.js": "^0.0.19",
|
|
110
111
|
"@types/lodash.merge": "^4.6.9",
|
|
@@ -2,7 +2,7 @@ import React from 'react'
|
|
|
2
2
|
|
|
3
3
|
const BreakpointIndicator: React.FC = () => {
|
|
4
4
|
|
|
5
|
-
if (process.env.NODE_ENV === "production") return null
|
|
5
|
+
if (process?.env?.NODE_ENV !== undefined && process.env.NODE_ENV === "production") return null
|
|
6
6
|
|
|
7
7
|
return (
|
|
8
8
|
<div className="fixed bottom-1 left-1 z-floating flex h-6 w-6 items-center justify-center rounded-full bg-gray-800 p-3 font-mono text-xs text-primary">
|
package/primitives/combobox.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
import React, { useState } from 'react'
|
|
3
|
+
|
|
3
4
|
import { Check, ChevronDown } from 'lucide-react'
|
|
4
5
|
|
|
5
6
|
import { cn } from '../util'
|
|
@@ -21,107 +22,168 @@ import {
|
|
|
21
22
|
|
|
22
23
|
import type ListAdaptor from './list-adaptor'
|
|
23
24
|
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
src={url}
|
|
39
|
-
alt={alt ?? 'image'}
|
|
40
|
-
height={h}
|
|
41
|
-
width={w}
|
|
42
|
-
loading="eager"
|
|
43
|
-
className={className}
|
|
44
|
-
/>
|
|
45
|
-
) : null
|
|
46
|
-
)
|
|
47
|
-
// "rounded-sm object-contain"
|
|
25
|
+
const DEFAULT_IMAGE_SIZE = 32
|
|
26
|
+
|
|
27
|
+
interface ComboboxTriggerProps<T> {
|
|
28
|
+
current: T | null
|
|
29
|
+
currentLabel: string | null
|
|
30
|
+
imageUrl: string | null
|
|
31
|
+
placeholder?: string
|
|
32
|
+
buttonClx?: string
|
|
33
|
+
imageClx?: string
|
|
34
|
+
disabled?: boolean
|
|
35
|
+
imageSize?: number
|
|
36
|
+
noChevron?: boolean
|
|
37
|
+
open: boolean
|
|
38
|
+
}
|
|
48
39
|
|
|
49
|
-
const
|
|
40
|
+
const DefaultTriggerInner = <T,>(
|
|
50
41
|
{
|
|
51
|
-
|
|
52
|
-
|
|
42
|
+
current,
|
|
43
|
+
currentLabel,
|
|
44
|
+
imageUrl,
|
|
53
45
|
buttonClx='',
|
|
54
|
-
popoverClx='',
|
|
55
46
|
imageClx='',
|
|
56
|
-
|
|
57
|
-
searchPlaceholder='Search...',
|
|
58
|
-
buttonPlaceholder='Select...',
|
|
59
|
-
noneFoundMessage='None found.',
|
|
60
|
-
elementSelected,
|
|
47
|
+
placeholder='(select)',
|
|
61
48
|
disabled=false,
|
|
62
|
-
imageSize=
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
49
|
+
imageSize=DEFAULT_IMAGE_SIZE,
|
|
50
|
+
noChevron=false,
|
|
51
|
+
open,
|
|
52
|
+
...rest
|
|
53
|
+
}: ComboboxTriggerProps<T>,
|
|
54
|
+
ref: React.ForwardedRef<HTMLButtonElement>
|
|
55
|
+
) => (
|
|
56
|
+
<Button
|
|
57
|
+
ref={ref}
|
|
58
|
+
{...rest}
|
|
59
|
+
variant='outline'
|
|
60
|
+
role='combobox'
|
|
61
|
+
aria-expanded={open}
|
|
62
|
+
className={cn(
|
|
63
|
+
'flex',
|
|
64
|
+
noChevron ? 'justify-start' : 'justify-between',
|
|
65
|
+
buttonClx
|
|
66
|
+
)}
|
|
67
|
+
disabled={disabled}
|
|
68
|
+
>
|
|
69
|
+
<div className='flex justify-start items-center gap-2'>
|
|
70
|
+
{(current && imageUrl) ? (
|
|
71
|
+
<img
|
|
72
|
+
src={imageUrl}
|
|
73
|
+
alt={currentLabel + ' image'}
|
|
74
|
+
height={imageSize}
|
|
75
|
+
width={imageSize}
|
|
76
|
+
loading="eager"
|
|
77
|
+
className={cn('block', imageClx)}
|
|
78
|
+
/>
|
|
79
|
+
) : (
|
|
80
|
+
<div style={{width: imageSize, height: imageSize}} />
|
|
81
|
+
)}
|
|
82
|
+
<span className='block'>{currentLabel}</span>
|
|
83
|
+
</div>
|
|
84
|
+
{!noChevron && (<ChevronDown className={cn('block', open ? '' : 'opacity-50')} />)}
|
|
85
|
+
</Button>
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
const DefaultTrigger = React.forwardRef(DefaultTriggerInner) as <T, P>(props: P & { ref?: React.ForwardedRef<HTMLButtonElement> }) => React.ReactNode
|
|
89
|
+
|
|
90
|
+
const Combobox = <T, P extends ComboboxTriggerProps<T>>({
|
|
91
|
+
elements,
|
|
92
|
+
initial,
|
|
93
|
+
current,
|
|
94
|
+
setCurrent,
|
|
95
|
+
closeOnSelect=true,
|
|
96
|
+
adaptor,
|
|
97
|
+
popoverClx='',
|
|
98
|
+
listItemClx='',
|
|
99
|
+
listItemSelectedClx='',
|
|
100
|
+
noCheckmark=false,
|
|
101
|
+
listItemImageClx='',
|
|
102
|
+
searchPlaceholder='Search...',
|
|
103
|
+
noneFoundMessage='None found.',
|
|
104
|
+
listItemImageSize=DEFAULT_IMAGE_SIZE,
|
|
105
|
+
noSearch=false,
|
|
106
|
+
popoverAlign = 'center',
|
|
107
|
+
popoverSideOffset = 4,
|
|
108
|
+
Trigger,
|
|
109
|
+
triggerProps
|
|
110
|
+
}: {
|
|
111
|
+
elements: T[]
|
|
112
|
+
initial?: T | null
|
|
113
|
+
current?: T | null
|
|
114
|
+
setCurrent: (c: T | null) => void
|
|
115
|
+
closeOnSelect?: boolean
|
|
116
|
+
adaptor: ListAdaptor<T>
|
|
117
|
+
popoverClx?: string
|
|
118
|
+
listItemClx?: string
|
|
119
|
+
listItemSelectedClx?: string
|
|
120
|
+
listItemImageClx?: string
|
|
121
|
+
listItemImageSize?: number
|
|
122
|
+
noCheckmark?: boolean
|
|
123
|
+
searchPlaceholder?: string
|
|
124
|
+
noneFoundMessage?: string
|
|
125
|
+
noSearch?: boolean
|
|
126
|
+
popoverAlign?: "center" | "end" | "start"
|
|
127
|
+
popoverSideOffset?: number
|
|
128
|
+
/** If (custom) Trigger is not supplied,
|
|
129
|
+
* passed to default trigger */
|
|
130
|
+
triggerProps: P
|
|
131
|
+
Trigger?:
|
|
132
|
+
<T, P>(props: P & { ref?: React.ForwardedRef<HTMLButtonElement> }) => React.ReactNode
|
|
76
133
|
}) => {
|
|
77
134
|
|
|
78
|
-
const [
|
|
79
|
-
|
|
135
|
+
const [_open, _setOpen] = useState<boolean>(false)
|
|
136
|
+
// for non-controlled base (must declare the hook either way)
|
|
137
|
+
const [_current, _setCurrent] = useState<T | null>(initial ?? null)
|
|
80
138
|
|
|
81
139
|
const handleSelect = (selString: string) => {
|
|
82
140
|
|
|
83
141
|
const found = elements.find((el: T) => (adaptor.valueEquals(el, selString)))
|
|
84
142
|
if (found) {
|
|
143
|
+
// non-controlled ('initial' supplied (may have been null))
|
|
144
|
+
if (initial !== undefined) {
|
|
145
|
+
_setCurrent(found)
|
|
146
|
+
}
|
|
85
147
|
setCurrent(found)
|
|
86
|
-
elementSelected(found)
|
|
87
148
|
}
|
|
88
|
-
|
|
149
|
+
if (closeOnSelect) {
|
|
150
|
+
_setOpen(false)
|
|
151
|
+
}
|
|
89
152
|
}
|
|
90
153
|
|
|
91
|
-
const isCurrent = (el: T): boolean =>
|
|
154
|
+
const isCurrent = (el: T): boolean => {
|
|
92
155
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
156
|
+
// non-controlled?
|
|
157
|
+
const curr = (current === undefined) ? _current : current
|
|
158
|
+
return !!curr && adaptor.equals(el, curr)
|
|
159
|
+
}
|
|
96
160
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
161
|
+
const _triggerProps = current ? {
|
|
162
|
+
...triggerProps,
|
|
163
|
+
current,
|
|
164
|
+
currentLabel: adaptor.getLabel ? adaptor.getLabel(current) : adaptor.getValue(current),
|
|
165
|
+
imageUrl: adaptor.getImageUrl ? adaptor.getImageUrl(current) : null,
|
|
166
|
+
open: _open
|
|
167
|
+
} : {
|
|
168
|
+
...triggerProps,
|
|
169
|
+
current: null,
|
|
170
|
+
currentLabel: null,
|
|
171
|
+
imageUrl: null,
|
|
172
|
+
open: _open
|
|
101
173
|
}
|
|
102
174
|
|
|
103
175
|
return (
|
|
104
|
-
<Popover open={
|
|
176
|
+
<Popover open={_open} onOpenChange={_setOpen}>
|
|
105
177
|
<PopoverTrigger asChild>
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
disabled={disabled}
|
|
112
|
-
>
|
|
113
|
-
<div className='flex justify-start items-center gap-2'>
|
|
114
|
-
{current && (
|
|
115
|
-
<ElementImage url={currentImageUrl} w={imageSize} h={imageSize} className={imageClx} alt={currentValue + ' image'}/>
|
|
116
|
-
)}
|
|
117
|
-
<span>{ current ? (currentLabel ?? currentValue) : buttonPlaceholder }</span>
|
|
118
|
-
</div>
|
|
119
|
-
<ChevronDown className={open ? '' : 'opacity-50'} />
|
|
120
|
-
</Button>
|
|
178
|
+
{Trigger ? (
|
|
179
|
+
<Trigger<T, P> {..._triggerProps} />
|
|
180
|
+
) : (
|
|
181
|
+
<DefaultTrigger<T, P> {..._triggerProps} />
|
|
182
|
+
)}
|
|
121
183
|
</PopoverTrigger>
|
|
122
|
-
<PopoverContent className={'p-0
|
|
184
|
+
<PopoverContent className={cn('p-0', popoverClx)} align={popoverAlign} sideOffset={popoverSideOffset}>
|
|
123
185
|
<Command>
|
|
124
|
-
<CommandInput placeholder={searchPlaceholder} />
|
|
186
|
+
{!noSearch && (<CommandInput placeholder={searchPlaceholder} />)}
|
|
125
187
|
<CommandList>
|
|
126
188
|
<CommandEmpty>{noneFoundMessage}</CommandEmpty>
|
|
127
189
|
<CommandGroup>
|
|
@@ -130,21 +192,33 @@ const Combobox = <T,>(
|
|
|
130
192
|
key={adaptor.getValue(el)}
|
|
131
193
|
value={adaptor.getValue(el)}
|
|
132
194
|
onSelect={handleSelect}
|
|
133
|
-
className=
|
|
195
|
+
className={cn(
|
|
196
|
+
'flex',
|
|
197
|
+
noCheckmark ? 'justify-start' : 'justify-between',
|
|
198
|
+
listItemClx,
|
|
199
|
+
(isCurrent(el) ? listItemSelectedClx : '')
|
|
200
|
+
)}
|
|
134
201
|
>
|
|
135
202
|
<div className='flex justify-start items-center gap-2'>
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
h={imageSize}
|
|
140
|
-
className={imageClx}
|
|
203
|
+
{ (adaptor.getImageUrl && adaptor.getImageUrl(el)) ? (
|
|
204
|
+
<img
|
|
205
|
+
src={adaptor.getImageUrl(el)!}
|
|
141
206
|
alt={adaptor.getValue(el) + ' image'}
|
|
207
|
+
height={listItemImageSize}
|
|
208
|
+
width={listItemImageSize}
|
|
209
|
+
loading="eager"
|
|
210
|
+
className={listItemImageClx}
|
|
142
211
|
/>
|
|
212
|
+
) : (
|
|
213
|
+
<div style={{width: listItemImageSize, height: listItemImageSize}} />
|
|
214
|
+
)}
|
|
143
215
|
<span>{ adaptor.getLabel ? adaptor.getLabel(el) : adaptor.getValue(el) }</span>
|
|
144
216
|
</div>
|
|
217
|
+
{!noCheckmark && (
|
|
145
218
|
<div>
|
|
146
219
|
<Check className={cn('ml-auto', (isCurrent(el)) ? '' : 'invisible' )} />
|
|
147
220
|
</div>
|
|
221
|
+
)}
|
|
148
222
|
</CommandItem>
|
|
149
223
|
))}
|
|
150
224
|
</CommandGroup>
|
|
@@ -155,4 +229,7 @@ const Combobox = <T,>(
|
|
|
155
229
|
)
|
|
156
230
|
}
|
|
157
231
|
|
|
158
|
-
export
|
|
232
|
+
export {
|
|
233
|
+
Combobox as default,
|
|
234
|
+
type ComboboxTriggerProps
|
|
235
|
+
}
|
package/primitives/command.tsx
CHANGED
|
@@ -198,7 +198,7 @@ export { default as Badge } from './badge'
|
|
|
198
198
|
export { default as BreakpointIndicator } from './breakpoint-indicator'
|
|
199
199
|
export { default as Calendar } from './calendar'
|
|
200
200
|
export { default as Checkbox } from './checkbox'
|
|
201
|
-
export { default as Combobox } from './combobox'
|
|
201
|
+
export { default as Combobox, type ComboboxTriggerProps } from './combobox'
|
|
202
202
|
export { default as DialogVideoController } from './dialog-video-controller'
|
|
203
203
|
export { default as Input } from './input'
|
|
204
204
|
export { default as Label } from './label'
|
package/util/index.ts
CHANGED
|
@@ -6,8 +6,7 @@ export { cva, type VariantProps } from 'class-variance-authority'
|
|
|
6
6
|
|
|
7
7
|
import type { Dimensions } from '../types'
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
import _merge from 'lodash.merge'
|
|
9
|
+
import { default as _merge } from 'lodash.merge'
|
|
11
10
|
|
|
12
11
|
export const cn = (...inputs: ClassValue[]) => (
|
|
13
12
|
twMerge(clsx(inputs))
|