banhaten 0.1.0 → 0.1.2
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/README.md +21 -9
- package/package.json +8 -2
- package/registry/components/accordion.tsx +37 -1
- package/registry/components/alert.tsx +14 -28
- package/registry/components/attribute.tsx +6 -10
- package/registry/components/autocomplete.tsx +637 -0
- package/registry/components/avatar.tsx +259 -24
- package/registry/components/badge.tsx +97 -35
- package/registry/components/button-group.tsx +1 -1
- package/registry/components/card.tsx +1 -1
- package/registry/components/checkbox.tsx +19 -16
- package/registry/components/date-picker-state.ts +253 -0
- package/registry/components/date-picker.tsx +115 -158
- package/registry/components/expanded/ActivityFeed.tsx +37 -23
- package/registry/components/expanded/Banner.tsx +54 -19
- package/registry/components/expanded/Breadcrumbs.tsx +10 -38
- package/registry/components/expanded/CatalogComponentsShowcase.tsx +11 -16
- package/registry/components/expanded/CatalogTag.tsx +4 -11
- package/registry/components/expanded/CommandBar.tsx +33 -53
- package/registry/components/expanded/EmptyState.tsx +155 -0
- package/registry/components/expanded/FileUpload.tsx +362 -59
- package/registry/components/expanded/OnboardingStepListItem.tsx +6 -10
- package/registry/components/expanded/PageHeader.tsx +2 -11
- package/registry/components/expanded/Slideout.tsx +12 -23
- package/registry/components/expanded/Steps.tsx +6 -8
- package/registry/components/expanded/Table.tsx +18 -40
- package/registry/components/expanded/Timeline.tsx +5 -24
- package/registry/components/expanded/activityFeed.css +10 -54
- package/registry/components/expanded/banner.css +8 -75
- package/registry/components/expanded/breadcrumbs.css +1 -1
- package/registry/components/expanded/commandBar.css +23 -26
- package/registry/components/expanded/divider.css +1 -1
- package/registry/components/expanded/emptyState.css +111 -0
- package/registry/components/expanded/fileUpload.css +304 -75
- package/registry/components/expanded/pageHeader.css +1 -1
- package/registry/components/expanded/slideout.css +1 -0
- package/registry/components/expanded/steps.css +15 -51
- package/registry/components/expanded/table.css +6 -1
- package/registry/components/expanded/timeline.css +18 -15
- package/registry/components/input-otp.tsx +574 -0
- package/registry/components/input.tsx +140 -59
- package/registry/components/menu.tsx +470 -80
- package/registry/components/pagination.tsx +6 -18
- package/registry/components/popover.tsx +840 -0
- package/registry/components/radio-card.tsx +25 -31
- package/registry/components/select-content.tsx +28 -123
- package/registry/components/select.tsx +13 -9
- package/registry/components/skeleton.css +57 -0
- package/registry/components/skeleton.tsx +482 -0
- package/registry/components/social-button.tsx +24 -90
- package/registry/components/spinner.tsx +91 -7
- package/registry/components/textarea.tsx +21 -36
- package/registry/components/toggle.tsx +7 -23
- package/registry/components/tooltip.tsx +8 -4
- package/registry/examples/attribute-demo.tsx +2 -2
- package/registry/examples/autocomplete-demo.tsx +109 -0
- package/registry/examples/avatar-demo.tsx +102 -47
- package/registry/examples/badge-demo.tsx +16 -0
- package/registry/examples/checkbox-demo.tsx +3 -8
- package/registry/examples/date-picker-demo.tsx +75 -22
- package/registry/examples/expanded/banner-demo.tsx +31 -6
- package/registry/examples/expanded/breadcrumbs-demo.tsx +59 -0
- package/registry/examples/expanded/command-bar-demo.tsx +236 -0
- package/registry/examples/expanded/empty-state-demo.tsx +39 -0
- package/registry/examples/expanded/file-upload-demo.tsx +60 -0
- package/registry/examples/expanded/steps-demo.tsx +11 -0
- package/registry/examples/expanded/table-demo.tsx +142 -0
- package/registry/examples/input-demo.tsx +1 -1
- package/registry/examples/input-otp-demo.tsx +72 -0
- package/registry/examples/menu-demo.tsx +101 -88
- package/registry/examples/popover-demo.tsx +546 -0
- package/registry/examples/progress-demo.tsx +2 -2
- package/registry/examples/select-demo.tsx +32 -18
- package/registry/examples/skeleton-demo.tsx +56 -0
- package/registry/examples/social-button-demo.tsx +33 -33
- package/registry/examples/spinner-demo.tsx +59 -0
- package/registry/examples/tag-demo.tsx +1 -1
- package/registry/examples/textarea-demo.tsx +1 -1
- package/registry/index.json +266 -20
- package/registry/styles/globals.css +93 -3
- package/src/cli/index.js +997 -62
|
@@ -10,6 +10,31 @@ type MenuItemKind = "default" | "multiline" | "action" | "progress" | "button"
|
|
|
10
10
|
type MenuItemState = "default" | "disabled"
|
|
11
11
|
type MenuItemIconPosition = "leading" | "trailing"
|
|
12
12
|
type MenuItemAvatarSize = "sm" | "lg"
|
|
13
|
+
type MenuCollisionShift = {
|
|
14
|
+
x: number
|
|
15
|
+
y: number
|
|
16
|
+
}
|
|
17
|
+
type MenuCollisionPadding = NonNullable<
|
|
18
|
+
React.ComponentProps<typeof DropdownMenuPrimitive.Content>["collisionPadding"]
|
|
19
|
+
>
|
|
20
|
+
type MenuCollisionPaddingSides = {
|
|
21
|
+
bottom: number
|
|
22
|
+
left: number
|
|
23
|
+
right: number
|
|
24
|
+
top: number
|
|
25
|
+
}
|
|
26
|
+
type MenuResizeObserverConstructor = new (
|
|
27
|
+
callback: () => void
|
|
28
|
+
) => {
|
|
29
|
+
disconnect: () => void
|
|
30
|
+
observe: (target: Element) => void
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Default Radix collisionPadding mirrors --bh-space-md-8.
|
|
34
|
+
const MENU_COLLISION_PADDING_PX = 8
|
|
35
|
+
const zeroMenuCollisionShift: MenuCollisionShift = { x: 0, y: 0 }
|
|
36
|
+
const useSafeLayoutEffect =
|
|
37
|
+
typeof window === "undefined" ? React.useEffect : React.useLayoutEffect
|
|
13
38
|
|
|
14
39
|
const menuVariants = cva(
|
|
15
40
|
"flex flex-col items-start gap-[var(--bh-menu-gap)] overflow-hidden rounded-[var(--bh-menu-radius)] bg-[var(--bh-menu-bg)] py-[var(--bh-menu-padding-y)] shadow-[var(--shadow-menu)]",
|
|
@@ -106,50 +131,261 @@ type MenuContentProps = React.ComponentProps<typeof DropdownMenuPrimitive.Conten
|
|
|
106
131
|
asChild?: boolean
|
|
107
132
|
}
|
|
108
133
|
|
|
134
|
+
type MenuSubContentProps = React.ComponentProps<typeof DropdownMenuPrimitive.SubContent> &
|
|
135
|
+
VariantProps<typeof menuVariants> & {
|
|
136
|
+
asChild?: boolean
|
|
137
|
+
}
|
|
138
|
+
|
|
109
139
|
const MenuRoot = DropdownMenuPrimitive.Root
|
|
140
|
+
const MenuSub = DropdownMenuPrimitive.Sub
|
|
110
141
|
const MenuTrigger = DropdownMenuPrimitive.Trigger
|
|
142
|
+
const MenuStaticContext = React.createContext(false)
|
|
143
|
+
|
|
144
|
+
type MenuSurfaceProps = React.ComponentProps<"div"> &
|
|
145
|
+
VariantProps<typeof menuVariants> & {
|
|
146
|
+
asChild?: boolean
|
|
147
|
+
avoidViewportNudge?: boolean
|
|
148
|
+
collisionPadding?: MenuCollisionPadding
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const MenuSurface = React.forwardRef<HTMLDivElement, MenuSurfaceProps>(function MenuSurface({
|
|
152
|
+
asChild = false,
|
|
153
|
+
avoidViewportNudge = true,
|
|
154
|
+
children,
|
|
155
|
+
className,
|
|
156
|
+
collisionPadding = MENU_COLLISION_PADDING_PX,
|
|
157
|
+
role = "menu",
|
|
158
|
+
style,
|
|
159
|
+
width,
|
|
160
|
+
...props
|
|
161
|
+
}, ref) {
|
|
162
|
+
const Comp = asChild ? Slot : "div"
|
|
163
|
+
const surfaceRef = React.useRef<HTMLDivElement | null>(null)
|
|
164
|
+
const collisionShiftRef = React.useRef(zeroMenuCollisionShift)
|
|
165
|
+
const [collisionShift, setCollisionShift] = React.useState(
|
|
166
|
+
zeroMenuCollisionShift
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
function updateCollisionShift(nextShift: MenuCollisionShift) {
|
|
170
|
+
collisionShiftRef.current = nextShift
|
|
171
|
+
setCollisionShift((currentShift) =>
|
|
172
|
+
isSameMenuCollisionShift(currentShift, nextShift)
|
|
173
|
+
? currentShift
|
|
174
|
+
: nextShift
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function setSurfaceRef(node: HTMLDivElement | null) {
|
|
179
|
+
surfaceRef.current = node
|
|
180
|
+
|
|
181
|
+
if (typeof ref === "function") {
|
|
182
|
+
ref(node)
|
|
183
|
+
} else if (ref) {
|
|
184
|
+
ref.current = node
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
useSafeLayoutEffect(() => {
|
|
189
|
+
if (!avoidViewportNudge) {
|
|
190
|
+
updateCollisionShift(zeroMenuCollisionShift)
|
|
191
|
+
return
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const node = surfaceRef.current
|
|
195
|
+
if (!node) return
|
|
196
|
+
const surfaceNode: HTMLDivElement = node
|
|
197
|
+
|
|
198
|
+
const ownerWindow = surfaceNode.ownerDocument.defaultView
|
|
199
|
+
if (!ownerWindow) return
|
|
200
|
+
const surfaceWindow: Window = ownerWindow
|
|
201
|
+
|
|
202
|
+
let animationFrame = 0
|
|
203
|
+
const resolvedCollisionPadding = resolveMenuCollisionPadding(collisionPadding)
|
|
204
|
+
const requestFrame =
|
|
205
|
+
typeof surfaceWindow.requestAnimationFrame === "function"
|
|
206
|
+
? surfaceWindow.requestAnimationFrame.bind(surfaceWindow)
|
|
207
|
+
: (callback: FrameRequestCallback) =>
|
|
208
|
+
surfaceWindow.setTimeout(
|
|
209
|
+
() => callback(surfaceWindow.performance.now()),
|
|
210
|
+
0
|
|
211
|
+
)
|
|
212
|
+
const cancelFrame =
|
|
213
|
+
typeof surfaceWindow.cancelAnimationFrame === "function"
|
|
214
|
+
? surfaceWindow.cancelAnimationFrame.bind(surfaceWindow)
|
|
215
|
+
: surfaceWindow.clearTimeout.bind(surfaceWindow)
|
|
216
|
+
|
|
217
|
+
function measureCollisionShift() {
|
|
218
|
+
animationFrame = 0
|
|
219
|
+
|
|
220
|
+
const rect = surfaceNode.getBoundingClientRect()
|
|
221
|
+
const currentShift = collisionShiftRef.current
|
|
222
|
+
const unshiftedRect = {
|
|
223
|
+
bottom: rect.bottom - currentShift.y,
|
|
224
|
+
left: rect.left - currentShift.x,
|
|
225
|
+
right: rect.right - currentShift.x,
|
|
226
|
+
top: rect.top - currentShift.y,
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
updateCollisionShift(
|
|
230
|
+
getMenuCollisionShift({
|
|
231
|
+
collisionPadding: resolvedCollisionPadding,
|
|
232
|
+
rect: unshiftedRect,
|
|
233
|
+
viewportHeight: surfaceWindow.innerHeight,
|
|
234
|
+
viewportWidth: surfaceWindow.innerWidth,
|
|
235
|
+
})
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function queueCollisionShiftMeasure() {
|
|
240
|
+
if (animationFrame) {
|
|
241
|
+
cancelFrame(animationFrame)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
animationFrame = requestFrame(measureCollisionShift)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
queueCollisionShiftMeasure()
|
|
248
|
+
surfaceWindow.addEventListener("resize", queueCollisionShiftMeasure)
|
|
249
|
+
surfaceWindow.addEventListener("scroll", queueCollisionShiftMeasure, true)
|
|
250
|
+
|
|
251
|
+
const ResizeObserver = (
|
|
252
|
+
surfaceWindow as Window & {
|
|
253
|
+
ResizeObserver?: MenuResizeObserverConstructor
|
|
254
|
+
}
|
|
255
|
+
).ResizeObserver
|
|
256
|
+
const resizeObserver = ResizeObserver
|
|
257
|
+
? new ResizeObserver(queueCollisionShiftMeasure)
|
|
258
|
+
: undefined
|
|
259
|
+
resizeObserver?.observe(surfaceNode)
|
|
260
|
+
|
|
261
|
+
return () => {
|
|
262
|
+
if (animationFrame) {
|
|
263
|
+
cancelFrame(animationFrame)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
surfaceWindow.removeEventListener("resize", queueCollisionShiftMeasure)
|
|
267
|
+
surfaceWindow.removeEventListener("scroll", queueCollisionShiftMeasure, true)
|
|
268
|
+
resizeObserver?.disconnect()
|
|
269
|
+
}
|
|
270
|
+
}, [avoidViewportNudge, collisionPadding, width])
|
|
271
|
+
|
|
272
|
+
return (
|
|
273
|
+
<Comp
|
|
274
|
+
data-slot="menu"
|
|
275
|
+
data-width={width ?? "menu"}
|
|
276
|
+
ref={setSurfaceRef}
|
|
277
|
+
role={role}
|
|
278
|
+
style={{
|
|
279
|
+
...style,
|
|
280
|
+
transform: mergeMenuTransforms(
|
|
281
|
+
getMenuCollisionTransform(collisionShift),
|
|
282
|
+
style?.transform
|
|
283
|
+
),
|
|
284
|
+
}}
|
|
285
|
+
className={cn(
|
|
286
|
+
menuVariants({ width }),
|
|
287
|
+
"max-h-[var(--radix-dropdown-menu-content-available-height)] overflow-y-auto",
|
|
288
|
+
className
|
|
289
|
+
)}
|
|
290
|
+
{...props}
|
|
291
|
+
>
|
|
292
|
+
{children}
|
|
293
|
+
</Comp>
|
|
294
|
+
)
|
|
295
|
+
})
|
|
111
296
|
|
|
112
297
|
const MenuContent = React.forwardRef<HTMLDivElement, MenuContentProps>(function MenuContent({
|
|
298
|
+
children,
|
|
113
299
|
className,
|
|
300
|
+
collisionPadding = MENU_COLLISION_PADDING_PX,
|
|
114
301
|
width,
|
|
115
302
|
asChild = false,
|
|
116
303
|
role = "menu",
|
|
117
304
|
...props
|
|
118
305
|
}, ref) {
|
|
119
|
-
const Comp = asChild ? Slot : "div"
|
|
120
|
-
|
|
121
306
|
return (
|
|
122
|
-
<DropdownMenuPrimitive.Content
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
307
|
+
<DropdownMenuPrimitive.Content
|
|
308
|
+
asChild
|
|
309
|
+
collisionPadding={collisionPadding}
|
|
310
|
+
{...props}
|
|
311
|
+
>
|
|
312
|
+
<MenuSurface
|
|
313
|
+
asChild={asChild}
|
|
314
|
+
className={className}
|
|
315
|
+
collisionPadding={collisionPadding}
|
|
126
316
|
ref={ref}
|
|
127
317
|
role={role}
|
|
128
|
-
|
|
129
|
-
|
|
318
|
+
width={width}
|
|
319
|
+
>
|
|
320
|
+
{children}
|
|
321
|
+
</MenuSurface>
|
|
130
322
|
</DropdownMenuPrimitive.Content>
|
|
131
323
|
)
|
|
132
324
|
})
|
|
133
325
|
|
|
326
|
+
const MenuSubContent = React.forwardRef<HTMLDivElement, MenuSubContentProps>(function MenuSubContent({
|
|
327
|
+
children,
|
|
328
|
+
className,
|
|
329
|
+
collisionPadding = MENU_COLLISION_PADDING_PX,
|
|
330
|
+
sideOffset = 4,
|
|
331
|
+
width,
|
|
332
|
+
asChild = false,
|
|
333
|
+
role = "menu",
|
|
334
|
+
...props
|
|
335
|
+
}, ref) {
|
|
336
|
+
return (
|
|
337
|
+
<DropdownMenuPrimitive.SubContent
|
|
338
|
+
asChild
|
|
339
|
+
collisionPadding={collisionPadding}
|
|
340
|
+
sideOffset={sideOffset}
|
|
341
|
+
{...props}
|
|
342
|
+
>
|
|
343
|
+
<MenuSurface
|
|
344
|
+
asChild={asChild}
|
|
345
|
+
className={className}
|
|
346
|
+
collisionPadding={collisionPadding}
|
|
347
|
+
ref={ref}
|
|
348
|
+
role={role}
|
|
349
|
+
width={width}
|
|
350
|
+
>
|
|
351
|
+
{children}
|
|
352
|
+
</MenuSurface>
|
|
353
|
+
</DropdownMenuPrimitive.SubContent>
|
|
354
|
+
)
|
|
355
|
+
})
|
|
356
|
+
|
|
134
357
|
function Menu({
|
|
135
358
|
children,
|
|
359
|
+
className,
|
|
136
360
|
defaultOpen = true,
|
|
137
361
|
modal = false,
|
|
138
362
|
onOpenChange,
|
|
139
363
|
open,
|
|
364
|
+
role = "menu",
|
|
365
|
+
width,
|
|
366
|
+
asChild = false,
|
|
140
367
|
...props
|
|
141
368
|
}: MenuProps) {
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
369
|
+
const Comp = asChild ? Slot : "div"
|
|
370
|
+
const isOpen = open ?? defaultOpen
|
|
371
|
+
|
|
372
|
+
void modal
|
|
373
|
+
void onOpenChange
|
|
374
|
+
|
|
375
|
+
if (!isOpen) return null
|
|
146
376
|
|
|
147
377
|
return (
|
|
148
|
-
<
|
|
149
|
-
<
|
|
378
|
+
<MenuStaticContext.Provider value>
|
|
379
|
+
<Comp
|
|
380
|
+
data-slot="menu"
|
|
381
|
+
data-width={width ?? "menu"}
|
|
382
|
+
role={role}
|
|
383
|
+
className={cn(menuVariants({ width, className }))}
|
|
384
|
+
{...props}
|
|
385
|
+
>
|
|
150
386
|
{children}
|
|
151
|
-
</
|
|
152
|
-
</
|
|
387
|
+
</Comp>
|
|
388
|
+
</MenuStaticContext.Provider>
|
|
153
389
|
)
|
|
154
390
|
}
|
|
155
391
|
|
|
@@ -175,6 +411,18 @@ type MenuItemProps = React.ComponentProps<"div"> &
|
|
|
175
411
|
disabled?: boolean
|
|
176
412
|
}
|
|
177
413
|
|
|
414
|
+
type MenuSubTriggerPrimitiveProps = Pick<
|
|
415
|
+
React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger>,
|
|
416
|
+
"textValue"
|
|
417
|
+
>
|
|
418
|
+
|
|
419
|
+
type MenuSubTriggerProps = React.ComponentProps<"div"> &
|
|
420
|
+
VariantProps<typeof menuItemVariants> &
|
|
421
|
+
MenuSubTriggerPrimitiveProps & {
|
|
422
|
+
asChild?: boolean
|
|
423
|
+
disabled?: boolean
|
|
424
|
+
}
|
|
425
|
+
|
|
178
426
|
const MenuItem = React.forwardRef<HTMLDivElement, MenuItemProps>(function MenuItem({
|
|
179
427
|
className,
|
|
180
428
|
kind = "default",
|
|
@@ -188,9 +436,30 @@ const MenuItem = React.forwardRef<HTMLDivElement, MenuItemProps>(function MenuIt
|
|
|
188
436
|
...props
|
|
189
437
|
}, ref) {
|
|
190
438
|
const Comp = asChild ? Slot : "div"
|
|
439
|
+
const isStaticMenu = React.useContext(MenuStaticContext)
|
|
191
440
|
const selectedKind: MenuItemKind = kind ?? "default"
|
|
192
441
|
const isDisabled = disabled
|
|
193
442
|
|
|
443
|
+
const item = (
|
|
444
|
+
<Comp
|
|
445
|
+
aria-disabled={isDisabled ? true : undefined}
|
|
446
|
+
data-disabled={isDisabled ? "true" : "false"}
|
|
447
|
+
data-kind={selectedKind}
|
|
448
|
+
data-slot="menu-item"
|
|
449
|
+
ref={ref}
|
|
450
|
+
role={role}
|
|
451
|
+
tabIndex={isDisabled ? -1 : (tabIndex ?? 0)}
|
|
452
|
+
className={cn(menuItemVariants({ kind: selectedKind, className }))}
|
|
453
|
+
{...props}
|
|
454
|
+
>
|
|
455
|
+
<MenuItemContent kind={selectedKind}>
|
|
456
|
+
<MenuItemChildren kind={selectedKind}>{children}</MenuItemChildren>
|
|
457
|
+
</MenuItemContent>
|
|
458
|
+
</Comp>
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
if (isStaticMenu) return item
|
|
462
|
+
|
|
194
463
|
return (
|
|
195
464
|
<DropdownMenuPrimitive.Item
|
|
196
465
|
asChild
|
|
@@ -198,27 +467,62 @@ const MenuItem = React.forwardRef<HTMLDivElement, MenuItemProps>(function MenuIt
|
|
|
198
467
|
onSelect={onSelect}
|
|
199
468
|
textValue={textValue ?? getMenuItemTextValue(children)}
|
|
200
469
|
>
|
|
201
|
-
|
|
202
|
-
aria-disabled={isDisabled ? true : undefined}
|
|
203
|
-
data-disabled={isDisabled ? "true" : "false"}
|
|
204
|
-
data-kind={selectedKind}
|
|
205
|
-
data-slot="menu-item"
|
|
206
|
-
ref={ref}
|
|
207
|
-
role={role}
|
|
208
|
-
tabIndex={isDisabled ? -1 : (tabIndex ?? 0)}
|
|
209
|
-
className={cn(menuItemVariants({ kind: selectedKind, className }))}
|
|
210
|
-
{...props}
|
|
211
|
-
>
|
|
212
|
-
<MenuItemContent kind={selectedKind}>
|
|
213
|
-
<MenuItemChildren kind={selectedKind}>{children}</MenuItemChildren>
|
|
214
|
-
</MenuItemContent>
|
|
215
|
-
</Comp>
|
|
470
|
+
{item}
|
|
216
471
|
</DropdownMenuPrimitive.Item>
|
|
217
472
|
)
|
|
218
473
|
})
|
|
219
474
|
|
|
220
475
|
MenuItem.displayName = "MenuItem"
|
|
221
476
|
|
|
477
|
+
const MenuSubTrigger = React.forwardRef<HTMLDivElement, MenuSubTriggerProps>(function MenuSubTrigger({
|
|
478
|
+
className,
|
|
479
|
+
kind = "default",
|
|
480
|
+
disabled = false,
|
|
481
|
+
asChild = false,
|
|
482
|
+
role = "menuitem",
|
|
483
|
+
tabIndex,
|
|
484
|
+
children,
|
|
485
|
+
textValue,
|
|
486
|
+
...props
|
|
487
|
+
}, ref) {
|
|
488
|
+
const Comp = asChild ? Slot : "div"
|
|
489
|
+
const isStaticMenu = React.useContext(MenuStaticContext)
|
|
490
|
+
const selectedKind: MenuItemKind = kind ?? "default"
|
|
491
|
+
const isDisabled = disabled
|
|
492
|
+
|
|
493
|
+
const item = (
|
|
494
|
+
<Comp
|
|
495
|
+
aria-disabled={isDisabled ? true : undefined}
|
|
496
|
+
data-disabled={isDisabled ? "true" : "false"}
|
|
497
|
+
data-kind={selectedKind}
|
|
498
|
+
data-slot="menu-sub-trigger"
|
|
499
|
+
ref={ref}
|
|
500
|
+
role={role}
|
|
501
|
+
tabIndex={isDisabled ? -1 : (tabIndex ?? 0)}
|
|
502
|
+
className={cn(menuItemVariants({ kind: selectedKind, className }))}
|
|
503
|
+
{...props}
|
|
504
|
+
>
|
|
505
|
+
<MenuItemContent kind={selectedKind}>
|
|
506
|
+
<MenuItemChildren kind={selectedKind}>{children}</MenuItemChildren>
|
|
507
|
+
</MenuItemContent>
|
|
508
|
+
</Comp>
|
|
509
|
+
)
|
|
510
|
+
|
|
511
|
+
if (isStaticMenu) return item
|
|
512
|
+
|
|
513
|
+
return (
|
|
514
|
+
<DropdownMenuPrimitive.SubTrigger
|
|
515
|
+
asChild
|
|
516
|
+
disabled={isDisabled}
|
|
517
|
+
textValue={textValue ?? getMenuItemTextValue(children)}
|
|
518
|
+
>
|
|
519
|
+
{item}
|
|
520
|
+
</DropdownMenuPrimitive.SubTrigger>
|
|
521
|
+
)
|
|
522
|
+
})
|
|
523
|
+
|
|
524
|
+
MenuSubTrigger.displayName = "MenuSubTrigger"
|
|
525
|
+
|
|
222
526
|
type MenuItemContentProps = React.ComponentProps<"div"> &
|
|
223
527
|
VariantProps<typeof menuItemContentVariants>
|
|
224
528
|
|
|
@@ -620,42 +924,45 @@ function MenuLabel({
|
|
|
620
924
|
role = "presentation",
|
|
621
925
|
...props
|
|
622
926
|
}: MenuLabelProps) {
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
</DropdownMenuPrimitive.Label>
|
|
927
|
+
const isStaticMenu = React.useContext(MenuStaticContext)
|
|
928
|
+
const labelContent = (
|
|
929
|
+
<div
|
|
930
|
+
data-slot="menu-label"
|
|
931
|
+
role={role}
|
|
932
|
+
className={cn(
|
|
933
|
+
"flex h-[var(--bh-menu-item-label-height)] w-full items-center gap-0 bg-[var(--bh-menu-item-bg)] px-[var(--bh-menu-label-padding-x)] py-[var(--bh-space-xs-4)] text-[length:var(--bh-text-body-xs-regular-font-size)] leading-[var(--bh-text-body-xs-regular-line-height)] tracking-[var(--bh-text-body-xs-regular-letter-spacing)]",
|
|
934
|
+
className
|
|
935
|
+
)}
|
|
936
|
+
{...props}
|
|
937
|
+
>
|
|
938
|
+
{children ?? (
|
|
939
|
+
<>
|
|
940
|
+
{hasRenderableContent(label) ? (
|
|
941
|
+
<span
|
|
942
|
+
data-slot="menu-label-primary"
|
|
943
|
+
dir="auto"
|
|
944
|
+
className="min-w-0 flex-1 truncate text-start font-[var(--bh-text-body-xs-medium-font-weight)] text-[var(--bh-content-default)]"
|
|
945
|
+
>
|
|
946
|
+
{label}
|
|
947
|
+
</span>
|
|
948
|
+
) : null}
|
|
949
|
+
{hasRenderableContent(caption) ? (
|
|
950
|
+
<span
|
|
951
|
+
data-slot="menu-label-caption"
|
|
952
|
+
dir="auto"
|
|
953
|
+
className="shrink-0 whitespace-nowrap text-end font-[var(--bh-text-body-xs-regular-font-weight)] text-[var(--bh-content-subtle)]"
|
|
954
|
+
>
|
|
955
|
+
{caption}
|
|
956
|
+
</span>
|
|
957
|
+
) : null}
|
|
958
|
+
</>
|
|
959
|
+
)}
|
|
960
|
+
</div>
|
|
658
961
|
)
|
|
962
|
+
|
|
963
|
+
if (isStaticMenu) return labelContent
|
|
964
|
+
|
|
965
|
+
return <DropdownMenuPrimitive.Label asChild>{labelContent}</DropdownMenuPrimitive.Label>
|
|
659
966
|
}
|
|
660
967
|
|
|
661
968
|
function hasRenderableContent(content: React.ReactNode) {
|
|
@@ -696,21 +1003,99 @@ function MenuSeparator({
|
|
|
696
1003
|
role = "separator",
|
|
697
1004
|
...props
|
|
698
1005
|
}: React.ComponentProps<"div">) {
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
</DropdownMenuPrimitive.Separator>
|
|
1006
|
+
const isStaticMenu = React.useContext(MenuStaticContext)
|
|
1007
|
+
const separator = (
|
|
1008
|
+
<div
|
|
1009
|
+
data-slot="menu-separator"
|
|
1010
|
+
role={role}
|
|
1011
|
+
className={cn(
|
|
1012
|
+
"flex h-[var(--bh-menu-item-divider-height)] w-full items-center py-[var(--bh-menu-divider-padding-y)]",
|
|
1013
|
+
className
|
|
1014
|
+
)}
|
|
1015
|
+
{...props}
|
|
1016
|
+
>
|
|
1017
|
+
<span className="block w-full border-t border-[var(--bh-border-subtle)]" />
|
|
1018
|
+
</div>
|
|
713
1019
|
)
|
|
1020
|
+
|
|
1021
|
+
if (isStaticMenu) return separator
|
|
1022
|
+
|
|
1023
|
+
return <DropdownMenuPrimitive.Separator asChild>{separator}</DropdownMenuPrimitive.Separator>
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
function getMenuCollisionShift({
|
|
1027
|
+
collisionPadding,
|
|
1028
|
+
rect,
|
|
1029
|
+
viewportHeight,
|
|
1030
|
+
viewportWidth,
|
|
1031
|
+
}: {
|
|
1032
|
+
collisionPadding: MenuCollisionPaddingSides
|
|
1033
|
+
rect: Pick<DOMRect, "bottom" | "left" | "right" | "top">
|
|
1034
|
+
viewportHeight: number
|
|
1035
|
+
viewportWidth: number
|
|
1036
|
+
}): MenuCollisionShift {
|
|
1037
|
+
let x = 0
|
|
1038
|
+
let y = 0
|
|
1039
|
+
const minX = collisionPadding.left
|
|
1040
|
+
const minY = collisionPadding.top
|
|
1041
|
+
const maxX = viewportWidth - collisionPadding.right
|
|
1042
|
+
const maxY = viewportHeight - collisionPadding.bottom
|
|
1043
|
+
|
|
1044
|
+
if (rect.left < minX) {
|
|
1045
|
+
x = minX - rect.left
|
|
1046
|
+
} else if (rect.right > maxX) {
|
|
1047
|
+
x = maxX - rect.right
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
if (rect.top < minY) {
|
|
1051
|
+
y = minY - rect.top
|
|
1052
|
+
} else if (rect.bottom > maxY) {
|
|
1053
|
+
y = maxY - rect.bottom
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
return { x: Math.round(x), y: Math.round(y) }
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
function resolveMenuCollisionPadding(
|
|
1060
|
+
collisionPadding: MenuCollisionPadding
|
|
1061
|
+
): MenuCollisionPaddingSides {
|
|
1062
|
+
if (typeof collisionPadding === "number") {
|
|
1063
|
+
const padding = Math.max(0, collisionPadding)
|
|
1064
|
+
|
|
1065
|
+
return {
|
|
1066
|
+
bottom: padding,
|
|
1067
|
+
left: padding,
|
|
1068
|
+
right: padding,
|
|
1069
|
+
top: padding,
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
return {
|
|
1074
|
+
bottom: Math.max(0, collisionPadding.bottom ?? 0),
|
|
1075
|
+
left: Math.max(0, collisionPadding.left ?? 0),
|
|
1076
|
+
right: Math.max(0, collisionPadding.right ?? 0),
|
|
1077
|
+
top: Math.max(0, collisionPadding.top ?? 0),
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
function getMenuCollisionTransform(shift: MenuCollisionShift) {
|
|
1082
|
+
return `translate(${Math.round(shift.x)}px, ${Math.round(shift.y)}px)`
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
function mergeMenuTransforms(
|
|
1086
|
+
collisionTransform: string,
|
|
1087
|
+
customTransform: string | undefined
|
|
1088
|
+
) {
|
|
1089
|
+
return customTransform
|
|
1090
|
+
? `${collisionTransform} ${customTransform}`
|
|
1091
|
+
: collisionTransform
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
function isSameMenuCollisionShift(
|
|
1095
|
+
currentShift: MenuCollisionShift,
|
|
1096
|
+
nextShift: MenuCollisionShift
|
|
1097
|
+
) {
|
|
1098
|
+
return currentShift.x === nextShift.x && currentShift.y === nextShift.y
|
|
714
1099
|
}
|
|
715
1100
|
|
|
716
1101
|
export {
|
|
@@ -733,6 +1118,9 @@ export {
|
|
|
733
1118
|
MenuPortal,
|
|
734
1119
|
MenuRoot,
|
|
735
1120
|
MenuSeparator,
|
|
1121
|
+
MenuSub,
|
|
1122
|
+
MenuSubContent,
|
|
1123
|
+
MenuSubTrigger,
|
|
736
1124
|
MenuTrigger,
|
|
737
1125
|
menuItemContentVariants,
|
|
738
1126
|
menuItemIconVariants,
|
|
@@ -749,6 +1137,8 @@ export type {
|
|
|
749
1137
|
MenuItemIconProps,
|
|
750
1138
|
MenuItemKind,
|
|
751
1139
|
MenuItemProps,
|
|
1140
|
+
MenuSubContentProps,
|
|
1141
|
+
MenuSubTriggerProps,
|
|
752
1142
|
MenuItemSwitchProps,
|
|
753
1143
|
MenuItemState,
|
|
754
1144
|
MenuLabelProps,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as React from "react"
|
|
2
2
|
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react"
|
|
3
4
|
|
|
4
5
|
import { cn } from "@/lib/utils"
|
|
5
6
|
|
|
@@ -444,29 +445,16 @@ function PaginationPageItem({
|
|
|
444
445
|
}
|
|
445
446
|
|
|
446
447
|
function PaginationArrowIcon({ type }: { type: "previous" | "next" }) {
|
|
448
|
+
const Icon = type === "previous" ? ChevronLeftIcon : ChevronRightIcon
|
|
449
|
+
|
|
447
450
|
return (
|
|
448
|
-
<
|
|
451
|
+
<Icon
|
|
449
452
|
aria-hidden="true"
|
|
450
453
|
data-rtl-flip="true"
|
|
451
454
|
data-slot="pagination-icon"
|
|
452
|
-
fill="none"
|
|
453
455
|
focusable="false"
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
{type === "previous" ? (
|
|
457
|
-
<path
|
|
458
|
-
d="M2.357 5.30332L6.48183 9.42807L5.30333 10.6066L0 5.30332L5.30333 0L6.48183 1.17851L2.357 5.30332Z"
|
|
459
|
-
fill="currentColor"
|
|
460
|
-
transform="translate(6.666 4.697)"
|
|
461
|
-
/>
|
|
462
|
-
) : (
|
|
463
|
-
<path
|
|
464
|
-
d="M4.12482 5.30332L0 1.17851L1.17852 0L6.48182 5.30332L1.17852 10.6066L0 9.42807L4.12482 5.30332Z"
|
|
465
|
-
fill="currentColor"
|
|
466
|
-
transform="translate(6.852 4.697)"
|
|
467
|
-
/>
|
|
468
|
-
)}
|
|
469
|
-
</svg>
|
|
456
|
+
strokeWidth={2.25}
|
|
457
|
+
/>
|
|
470
458
|
)
|
|
471
459
|
}
|
|
472
460
|
|