banhaten 0.2.0 → 0.4.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.
- package/CHANGELOG.md +55 -0
- package/README.md +5 -5
- package/docs/design-system/component-implementation-ownership-audit.md +245 -0
- package/docs/design-system/component-quality-audit-2026-08-20.md +225 -0
- package/docs/design-system/component-showcase-consistency-report.md +2 -0
- package/docs/design-system/components/README.md +1 -0
- package/docs/design-system/components/activity-feed.md +2 -0
- package/docs/design-system/components/activity-heatmap.md +73 -0
- package/docs/design-system/components/alert-dialog.md +1 -1
- package/docs/design-system/components/alert.md +3 -3
- package/docs/design-system/components/attribute.md +1 -1
- package/docs/design-system/components/avatar.md +2 -2
- package/docs/design-system/components/badge.md +1 -1
- package/docs/design-system/components/banner.md +6 -2
- package/docs/design-system/components/breadcrumbs.md +5 -2
- package/docs/design-system/components/button-group.md +3 -2
- package/docs/design-system/components/button.md +2 -2
- package/docs/design-system/components/card.md +11 -3
- package/docs/design-system/components/catalog-components.md +3 -0
- package/docs/design-system/components/date-picker.md +3 -1
- package/docs/design-system/components/file-upload.md +5 -1
- package/docs/design-system/components/input.md +8 -6
- package/docs/design-system/components/item.md +3 -1
- package/docs/design-system/components/kbd.md +11 -8
- package/docs/design-system/components/menu.md +2 -2
- package/docs/design-system/components/modal.md +1 -1
- package/docs/design-system/components/navigation-menu.md +1 -1
- package/docs/design-system/components/onboarding-step-list-item.md +3 -2
- package/docs/design-system/components/page-header.md +3 -1
- package/docs/design-system/components/pagination.md +3 -2
- package/docs/design-system/components/radio-card.md +1 -1
- package/docs/design-system/components/resizable.md +1 -1
- package/docs/design-system/components/scroll-area.md +7 -3
- package/docs/design-system/components/select.md +1 -1
- package/docs/design-system/components/sidebar.md +60 -18
- package/docs/design-system/components/slideout.md +4 -1
- package/docs/design-system/components/steps.md +7 -2
- package/docs/design-system/components/table.md +25 -7
- package/docs/design-system/components/tag.md +3 -3
- package/docs/design-system/components/timeline.md +4 -1
- package/docs/design-system/components/toggle.md +1 -1
- package/docs/design-system/components/toolbar.md +1 -1
- package/docs/design-system/components/tooltip.md +1 -1
- package/docs/design-system/density-and-elevation.md +23 -2
- package/docs/design-system/gap-promotion-report.md +1 -67
- package/docs/design-system/icons.md +6 -1
- package/docs/design-system/installation.md +1 -1
- package/docs/design-system/launch-comparison-shadcn.md +10 -10
- package/docs/design-system/token-governance.md +7 -8
- package/package.json +3 -2
- package/registry/components/alert-dialog.tsx +1 -1
- package/registry/components/alert.tsx +17 -13
- package/registry/components/attribute.tsx +8 -2
- package/registry/components/avatar.tsx +28 -20
- package/registry/components/badge.tsx +17 -17
- package/registry/components/button-group.tsx +4 -2
- package/registry/components/button.tsx +3 -3
- package/registry/components/card.tsx +19 -3
- package/registry/components/carousel.tsx +14 -0
- package/registry/components/command.tsx +37 -3
- package/registry/components/date-picker.tsx +83 -35
- package/registry/components/expanded/ActivityFeed.tsx +3 -7
- package/registry/components/expanded/ActivityHeatmap.tsx +64 -0
- package/registry/components/expanded/Banner.tsx +0 -1
- package/registry/components/expanded/Breadcrumbs.tsx +3 -0
- package/registry/components/expanded/CatalogComponentsShowcase.tsx +3 -4
- package/registry/components/expanded/CatalogDivider.tsx +3 -2
- package/registry/components/expanded/FileUpload.tsx +7 -5
- package/registry/components/expanded/OnboardingStepListItem.tsx +6 -7
- package/registry/components/expanded/PageHeader.tsx +35 -44
- package/registry/components/expanded/Slideout.tsx +18 -13
- package/registry/components/expanded/Steps.tsx +147 -9
- package/registry/components/expanded/Table.tsx +50 -18
- package/registry/components/expanded/Timeline.tsx +11 -24
- package/registry/components/expanded/activityFeed.css +8 -8
- package/registry/components/expanded/activityHeatmap.css +66 -0
- package/registry/components/expanded/banner.css +30 -15
- package/registry/components/expanded/breadcrumbs.css +5 -5
- package/registry/components/expanded/fileUpload.css +4 -4
- package/registry/components/expanded/onboardingStepListItem.css +16 -7
- package/registry/components/expanded/pageHeader.css +14 -11
- package/registry/components/expanded/slideout.css +3 -7
- package/registry/components/expanded/steps.css +75 -12
- package/registry/components/expanded/table.css +63 -69
- package/registry/components/expanded/timeline.css +17 -16
- package/registry/components/field.tsx +61 -8
- package/registry/components/input.tsx +12 -5
- package/registry/components/item.tsx +1 -1
- package/registry/components/kbd.tsx +109 -15
- package/registry/components/menu.tsx +57 -7
- package/registry/components/modal.tsx +1 -1
- package/registry/components/navigation-menu.tsx +1 -1
- package/registry/components/pagination.css +36 -0
- package/registry/components/pagination.tsx +20 -13
- package/registry/components/progress-slider.tsx +1 -1
- package/registry/components/radio-card.tsx +1 -2
- package/registry/components/resizable.tsx +36 -2
- package/registry/components/scroll-area.tsx +30 -6
- package/registry/components/select.tsx +35 -12
- package/registry/components/sidebar.tsx +940 -62
- package/registry/components/social-button.tsx +12 -5
- package/registry/components/table-elements.tsx +128 -0
- package/registry/components/tabs.tsx +5 -10
- package/registry/components/tag.tsx +2 -2
- package/registry/components/toggle.tsx +2 -2
- package/registry/components/toolbar.tsx +6 -6
- package/registry/components/tooltip.tsx +1 -1
- package/registry/examples/attribute-demo.tsx +1 -1
- package/registry/examples/avatar-demo.tsx +7 -4
- package/registry/examples/command-palette-demo.tsx +0 -1
- package/registry/examples/expanded/activity-heatmap-demo.tsx +29 -0
- package/registry/examples/expanded/breadcrumbs-demo.tsx +3 -3
- package/registry/examples/kbd-demo.tsx +2 -1
- package/registry/examples/pagination-demo.tsx +5 -5
- package/registry/examples/sidebar-demo.tsx +59 -13
- package/registry/guidance/USAGE.md +1 -1
- package/registry/index.json +621 -60
- package/registry/styles/globals.css +211 -151
- package/registry.json +44 -6
- package/tokens/banhaten.tokens.json +325 -95
|
@@ -9,7 +9,7 @@ const kbdVariants = cva(
|
|
|
9
9
|
[
|
|
10
10
|
"inline-flex min-w-[var(--bh-space-5xl-24)] items-center justify-center whitespace-nowrap rounded-[var(--bh-radius-md-6)]",
|
|
11
11
|
"border border-[var(--bh-border-default)] bg-[var(--bh-interactive-soft-default)] text-[var(--bh-content-subtle)]",
|
|
12
|
-
"font-
|
|
12
|
+
"[font-family:var(--bh-font-family)] text-[length:var(--bh-text-body-xs-medium-font-size)] font-[var(--bh-text-body-xs-medium-font-weight)]",
|
|
13
13
|
"leading-[var(--bh-text-body-xs-medium-line-height)] tracking-[var(--bh-text-body-xs-medium-letter-spacing)] shadow-[var(--shadow-button-soft)]",
|
|
14
14
|
"data-[disabled=true]:border-[var(--bh-border-disabled)] data-[disabled=true]:bg-[var(--bh-interactive-soft-disabled)] data-[disabled=true]:text-[var(--bh-content-disabled)] data-[disabled=true]:shadow-none",
|
|
15
15
|
],
|
|
@@ -26,6 +26,14 @@ const kbdVariants = cva(
|
|
|
26
26
|
}
|
|
27
27
|
)
|
|
28
28
|
|
|
29
|
+
const shortcutVariants = cva([
|
|
30
|
+
"inline-flex h-[var(--bh-space-4xl-20)] min-w-[var(--bh-space-4xl-20)] shrink-0 items-center justify-center whitespace-nowrap rounded-[var(--bh-radius-sm-4)] px-[var(--bh-space-xs-4)]",
|
|
31
|
+
"border border-[var(--bh-border-default)] bg-[var(--bh-interactive-soft-default)] text-[var(--bh-content-subtle)]",
|
|
32
|
+
"[font-family:var(--bh-font-family)] text-[length:var(--bh-text-body-2xs-medium-font-size)] font-[var(--bh-text-body-2xs-medium-font-weight)]",
|
|
33
|
+
"leading-[var(--bh-text-body-2xs-medium-line-height)] tracking-[var(--bh-text-body-2xs-medium-letter-spacing)]",
|
|
34
|
+
"data-[disabled=true]:border-[var(--bh-border-disabled)] data-[disabled=true]:bg-[var(--bh-interactive-soft-disabled)] data-[disabled=true]:text-[var(--bh-content-disabled)]",
|
|
35
|
+
])
|
|
36
|
+
|
|
29
37
|
type KbdProps = React.ComponentProps<"kbd"> &
|
|
30
38
|
VariantProps<typeof kbdVariants> & {
|
|
31
39
|
disabled?: boolean
|
|
@@ -48,6 +56,55 @@ const Kbd = React.forwardRef<HTMLElement, KbdProps>(function Kbd(
|
|
|
48
56
|
|
|
49
57
|
type ShortcutPlatform = "auto" | "mac" | "windows" | "linux"
|
|
50
58
|
|
|
59
|
+
type ShortcutProps = Omit<React.ComponentProps<"kbd">, "children"> & {
|
|
60
|
+
children?: React.ReactNode
|
|
61
|
+
disabled?: boolean
|
|
62
|
+
keys?: readonly string[]
|
|
63
|
+
platform?: ShortcutPlatform
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const Shortcut = React.forwardRef<HTMLElement, ShortcutProps>(function Shortcut(
|
|
67
|
+
{
|
|
68
|
+
"aria-label": ariaLabel,
|
|
69
|
+
children,
|
|
70
|
+
className,
|
|
71
|
+
disabled = false,
|
|
72
|
+
keys = [],
|
|
73
|
+
platform = "auto",
|
|
74
|
+
...props
|
|
75
|
+
},
|
|
76
|
+
ref
|
|
77
|
+
) {
|
|
78
|
+
const resolvedPlatform = useShortcutPlatform(platform)
|
|
79
|
+
const resolvedKeys = keys.map((key) =>
|
|
80
|
+
resolveShortcutKey(key, resolvedPlatform)
|
|
81
|
+
)
|
|
82
|
+
const content =
|
|
83
|
+
children ?? formatShortcutKeys(resolvedKeys, resolvedPlatform)
|
|
84
|
+
const accessibleLabel =
|
|
85
|
+
ariaLabel ??
|
|
86
|
+
(resolvedKeys.length > 0
|
|
87
|
+
? resolvedKeys.map(readableShortcutKey).join(" plus ")
|
|
88
|
+
: typeof children === "string"
|
|
89
|
+
? readableShortcutText(children)
|
|
90
|
+
: undefined)
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<kbd
|
|
94
|
+
aria-label={accessibleLabel}
|
|
95
|
+
className={cn(shortcutVariants(), className)}
|
|
96
|
+
data-disabled={disabled ? "true" : undefined}
|
|
97
|
+
data-platform={resolvedPlatform}
|
|
98
|
+
data-slot="shortcut"
|
|
99
|
+
dir="ltr"
|
|
100
|
+
ref={ref}
|
|
101
|
+
{...props}
|
|
102
|
+
>
|
|
103
|
+
{content}
|
|
104
|
+
</kbd>
|
|
105
|
+
)
|
|
106
|
+
})
|
|
107
|
+
|
|
51
108
|
type KbdShortcutProps = Omit<React.ComponentProps<"span">, "children"> & {
|
|
52
109
|
disabled?: boolean
|
|
53
110
|
keys: readonly string[]
|
|
@@ -68,18 +125,7 @@ const KbdShortcut = React.forwardRef<HTMLSpanElement, KbdShortcutProps>(
|
|
|
68
125
|
},
|
|
69
126
|
ref
|
|
70
127
|
) {
|
|
71
|
-
const
|
|
72
|
-
Exclude<ShortcutPlatform, "auto">
|
|
73
|
-
>(platform === "auto" ? "mac" : platform)
|
|
74
|
-
|
|
75
|
-
React.useEffect(() => {
|
|
76
|
-
if (platform !== "auto") {
|
|
77
|
-
setResolvedPlatform(platform)
|
|
78
|
-
return
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
setResolvedPlatform(detectShortcutPlatform())
|
|
82
|
-
}, [platform])
|
|
128
|
+
const resolvedPlatform = useShortcutPlatform(platform)
|
|
83
129
|
|
|
84
130
|
const resolvedKeys = keys.map((key) =>
|
|
85
131
|
resolveShortcutKey(key, resolvedPlatform)
|
|
@@ -117,6 +163,25 @@ const KbdShortcut = React.forwardRef<HTMLSpanElement, KbdShortcutProps>(
|
|
|
117
163
|
}
|
|
118
164
|
)
|
|
119
165
|
|
|
166
|
+
function useShortcutPlatform(
|
|
167
|
+
platform: ShortcutPlatform
|
|
168
|
+
): Exclude<ShortcutPlatform, "auto"> {
|
|
169
|
+
const [resolvedPlatform, setResolvedPlatform] = React.useState<
|
|
170
|
+
Exclude<ShortcutPlatform, "auto">
|
|
171
|
+
>(platform === "auto" ? "mac" : platform)
|
|
172
|
+
|
|
173
|
+
React.useEffect(() => {
|
|
174
|
+
if (platform !== "auto") {
|
|
175
|
+
setResolvedPlatform(platform)
|
|
176
|
+
return
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
setResolvedPlatform(detectShortcutPlatform())
|
|
180
|
+
}, [platform])
|
|
181
|
+
|
|
182
|
+
return resolvedPlatform
|
|
183
|
+
}
|
|
184
|
+
|
|
120
185
|
function detectShortcutPlatform(): Exclude<ShortcutPlatform, "auto"> {
|
|
121
186
|
if (typeof navigator === "undefined") return "mac"
|
|
122
187
|
const platform = navigator.platform.toLowerCase()
|
|
@@ -147,5 +212,34 @@ function readableShortcutKey(key: string) {
|
|
|
147
212
|
return key
|
|
148
213
|
}
|
|
149
214
|
|
|
150
|
-
|
|
151
|
-
|
|
215
|
+
function formatShortcutKeys(
|
|
216
|
+
keys: readonly string[],
|
|
217
|
+
platform: Exclude<ShortcutPlatform, "auto">
|
|
218
|
+
) {
|
|
219
|
+
if (platform === "mac" && keys.slice(0, -1).every(isMacShortcutSymbol)) {
|
|
220
|
+
return keys.join("")
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return keys.join(" ")
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function isMacShortcutSymbol(key: string) {
|
|
227
|
+
return ["⌘", "⌥", "⇧", "⌃"].includes(key)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function readableShortcutText(value: string) {
|
|
231
|
+
return value
|
|
232
|
+
.replaceAll("⌘", "Command ")
|
|
233
|
+
.replaceAll("⌥", "Option ")
|
|
234
|
+
.replaceAll("⇧", "Shift ")
|
|
235
|
+
.replaceAll("⌃", "Control ")
|
|
236
|
+
.trim()
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export { Kbd, KbdShortcut, Shortcut, kbdVariants, shortcutVariants }
|
|
240
|
+
export type {
|
|
241
|
+
KbdProps,
|
|
242
|
+
KbdShortcutProps,
|
|
243
|
+
ShortcutPlatform,
|
|
244
|
+
ShortcutProps,
|
|
245
|
+
}
|
|
@@ -90,7 +90,7 @@ const menuItemIconVariants = cva(
|
|
|
90
90
|
variants: {
|
|
91
91
|
position: {
|
|
92
92
|
leading:
|
|
93
|
-
"h-[var(--bh-menu-leading-icon-size)] ps-[var(--bh-menu-icon-padding)] [&_svg]:size-[var(--bh-menu-leading-icon-size)]",
|
|
93
|
+
"h-[var(--bh-menu-leading-icon-slot-size)] ps-[var(--bh-menu-icon-padding)] [&_svg]:size-[var(--bh-menu-leading-icon-size)]",
|
|
94
94
|
trailing:
|
|
95
95
|
"size-[var(--bh-menu-trailing-icon-size)] text-[var(--bh-content-muted)] [&_svg]:size-[var(--bh-menu-trailing-icon-size)]",
|
|
96
96
|
},
|
|
@@ -283,11 +283,12 @@ type MenuSubTriggerProps = React.ComponentProps<"div"> &
|
|
|
283
283
|
}
|
|
284
284
|
|
|
285
285
|
const MenuItem = React.forwardRef<HTMLDivElement, MenuItemProps>(function MenuItem({
|
|
286
|
+
"aria-checked": ariaChecked,
|
|
286
287
|
className,
|
|
287
288
|
kind = "default",
|
|
288
289
|
disabled = false,
|
|
289
290
|
asChild = false,
|
|
290
|
-
role
|
|
291
|
+
role,
|
|
291
292
|
tabIndex,
|
|
292
293
|
children,
|
|
293
294
|
onSelect,
|
|
@@ -298,15 +299,23 @@ const MenuItem = React.forwardRef<HTMLDivElement, MenuItemProps>(function MenuIt
|
|
|
298
299
|
const isStaticMenu = React.useContext(MenuStaticContext)
|
|
299
300
|
const selectedKind: MenuItemKind = kind ?? "default"
|
|
300
301
|
const isDisabled = disabled
|
|
302
|
+
const switchState = findMenuItemSwitchState(children)
|
|
303
|
+
const resolvedRole = role ??
|
|
304
|
+
(switchState === undefined ? "menuitem" : "menuitemcheckbox")
|
|
305
|
+
const resolvedChecked =
|
|
306
|
+
resolvedRole === "menuitemcheckbox"
|
|
307
|
+
? (ariaChecked ?? switchState ?? false)
|
|
308
|
+
: undefined
|
|
301
309
|
|
|
302
310
|
const item = (
|
|
303
311
|
<Comp
|
|
312
|
+
aria-checked={resolvedChecked}
|
|
304
313
|
aria-disabled={isDisabled ? true : undefined}
|
|
305
314
|
data-disabled={isDisabled ? "true" : "false"}
|
|
306
315
|
data-kind={selectedKind}
|
|
307
316
|
data-slot="menu-item"
|
|
308
317
|
ref={ref}
|
|
309
|
-
role={
|
|
318
|
+
role={resolvedRole}
|
|
310
319
|
tabIndex={isDisabled ? -1 : (tabIndex ?? 0)}
|
|
311
320
|
className={cn(menuItemVariants({ kind: selectedKind, className }))}
|
|
312
321
|
{...props}
|
|
@@ -319,6 +328,20 @@ const MenuItem = React.forwardRef<HTMLDivElement, MenuItemProps>(function MenuIt
|
|
|
319
328
|
|
|
320
329
|
if (isStaticMenu) return item
|
|
321
330
|
|
|
331
|
+
if (switchState !== undefined) {
|
|
332
|
+
return (
|
|
333
|
+
<DropdownMenuPrimitive.CheckboxItem
|
|
334
|
+
asChild
|
|
335
|
+
checked={switchState}
|
|
336
|
+
disabled={isDisabled}
|
|
337
|
+
onSelect={onSelect}
|
|
338
|
+
textValue={textValue ?? getMenuItemTextValue(children)}
|
|
339
|
+
>
|
|
340
|
+
{item}
|
|
341
|
+
</DropdownMenuPrimitive.CheckboxItem>
|
|
342
|
+
)
|
|
343
|
+
}
|
|
344
|
+
|
|
322
345
|
return (
|
|
323
346
|
<DropdownMenuPrimitive.Item
|
|
324
347
|
asChild
|
|
@@ -333,6 +356,27 @@ const MenuItem = React.forwardRef<HTMLDivElement, MenuItemProps>(function MenuIt
|
|
|
333
356
|
|
|
334
357
|
MenuItem.displayName = "MenuItem"
|
|
335
358
|
|
|
359
|
+
function findMenuItemSwitchState(children: React.ReactNode): boolean | undefined {
|
|
360
|
+
let state: boolean | undefined
|
|
361
|
+
|
|
362
|
+
React.Children.forEach(children, (child) => {
|
|
363
|
+
if (state !== undefined || !React.isValidElement(child)) return
|
|
364
|
+
|
|
365
|
+
const element = child as React.ReactElement<{
|
|
366
|
+
active?: boolean
|
|
367
|
+
children?: React.ReactNode
|
|
368
|
+
}>
|
|
369
|
+
if (element.type === MenuItemSwitch) {
|
|
370
|
+
state = Boolean(element.props.active)
|
|
371
|
+
return
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
state = findMenuItemSwitchState(element.props.children)
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
return state
|
|
378
|
+
}
|
|
379
|
+
|
|
336
380
|
const MenuSubTrigger = React.forwardRef<HTMLDivElement, MenuSubTriggerProps>(function MenuSubTrigger({
|
|
337
381
|
className,
|
|
338
382
|
kind = "default",
|
|
@@ -576,7 +620,10 @@ function MenuItemBadge({
|
|
|
576
620
|
)
|
|
577
621
|
}
|
|
578
622
|
|
|
579
|
-
type MenuItemSwitchProps = Omit<
|
|
623
|
+
type MenuItemSwitchProps = Omit<
|
|
624
|
+
React.ComponentProps<"span">,
|
|
625
|
+
"aria-checked" | "aria-label" | "aria-labelledby" | "role"
|
|
626
|
+
> & {
|
|
580
627
|
active?: boolean
|
|
581
628
|
disabled?: boolean
|
|
582
629
|
}
|
|
@@ -589,12 +636,10 @@ function MenuItemSwitch({
|
|
|
589
636
|
}: MenuItemSwitchProps) {
|
|
590
637
|
return (
|
|
591
638
|
<span
|
|
592
|
-
aria-
|
|
593
|
-
aria-disabled={disabled || undefined}
|
|
639
|
+
aria-hidden="true"
|
|
594
640
|
data-active={active ? "true" : "false"}
|
|
595
641
|
data-disabled={disabled ? "true" : "false"}
|
|
596
642
|
data-slot="menu-item-switch"
|
|
597
|
-
role="switch"
|
|
598
643
|
className={cn(
|
|
599
644
|
"flex h-[var(--bh-menu-switch-height)] w-[var(--bh-menu-switch-width)] shrink-0 items-center rounded-[var(--bh-radius-full)] p-[var(--bh-menu-switch-padding)] group-data-[disabled=true]/menu-item:bg-[var(--bh-interactive-switch-disabled)]",
|
|
600
645
|
active
|
|
@@ -677,6 +722,8 @@ function MenuProgress({
|
|
|
677
722
|
...props
|
|
678
723
|
}: MenuProgressProps) {
|
|
679
724
|
const clampedValue = Math.min(100, Math.max(0, value))
|
|
725
|
+
const labelId = React.useId()
|
|
726
|
+
const hasLabel = hasRenderableContent(label)
|
|
680
727
|
|
|
681
728
|
return (
|
|
682
729
|
<div
|
|
@@ -688,6 +735,8 @@ function MenuProgress({
|
|
|
688
735
|
{...props}
|
|
689
736
|
>
|
|
690
737
|
<div
|
|
738
|
+
aria-label={hasLabel ? undefined : "Progress"}
|
|
739
|
+
aria-labelledby={hasLabel ? labelId : undefined}
|
|
691
740
|
aria-valuemax={100}
|
|
692
741
|
aria-valuemin={0}
|
|
693
742
|
aria-valuenow={clampedValue}
|
|
@@ -717,6 +766,7 @@ function MenuProgress({
|
|
|
717
766
|
>
|
|
718
767
|
{hasRenderableContent(label) ? (
|
|
719
768
|
<span
|
|
769
|
+
id={labelId}
|
|
720
770
|
data-slot="menu-progress-label-text"
|
|
721
771
|
dir="auto"
|
|
722
772
|
className="truncate text-[length:var(--bh-text-body-xs-medium-font-size)] font-[var(--bh-text-body-xs-medium-font-weight)] leading-[var(--bh-text-body-xs-medium-line-height)] tracking-[var(--bh-text-body-xs-medium-letter-spacing)] text-[var(--bh-content-default)]"
|
|
@@ -39,7 +39,7 @@ type ConfirmModalMessages = {
|
|
|
39
39
|
|
|
40
40
|
const modalSurfaceBaseClassName = [
|
|
41
41
|
"flex max-h-[calc(100vh-(var(--bh-space-8xl-48)*2))] w-[var(--bh-modal-width)] max-w-[calc(100vw-(var(--bh-space-4xl-20)*2))] flex-col overflow-hidden rounded-[var(--bh-radius-2xl-12)] bg-[var(--bh-bg-raised)] text-[var(--bh-content-default)] shadow-[var(--shadow-modal)] outline-none",
|
|
42
|
-
"[--bh-modal-border:var(--bh-border-subtle)] [--shadow-modal:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-modal-border),var(--shadow-
|
|
42
|
+
"[--bh-modal-border:var(--bh-border-subtle)] [--shadow-modal:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-modal-border),var(--shadow-2xl)]",
|
|
43
43
|
]
|
|
44
44
|
|
|
45
45
|
const modalSizeVariants = {
|
|
@@ -145,7 +145,7 @@ const NavigationMenuIndicator = React.forwardRef<
|
|
|
145
145
|
className={cn("top-full z-[var(--bh-z-popover)] flex h-[var(--bh-space-lg-10)] items-end justify-center overflow-hidden", className)}
|
|
146
146
|
{...props}
|
|
147
147
|
>
|
|
148
|
-
<div className="relative top-[var(--bh-space-xs-4)] size-[var(--bh-space-lg-10)] rotate-45 rounded-tl-[var(--bh-radius-sm-4)] bg-[var(--bh-bg-raised)] shadow-[
|
|
148
|
+
<div className="relative top-[var(--bh-space-xs-4)] size-[var(--bh-space-lg-10)] rotate-45 rounded-tl-[var(--bh-radius-sm-4)] bg-[var(--bh-bg-raised)] shadow-[inset_0_0_0_var(--bh-border-width-default)_var(--bh-border-subtle)]" />
|
|
149
149
|
</NavigationMenuPrimitive.Indicator>
|
|
150
150
|
)
|
|
151
151
|
})
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
.ds-pagination {
|
|
2
|
+
container-type: inline-size;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.ds-pagination__controls {
|
|
6
|
+
overflow-x: auto;
|
|
7
|
+
overscroll-behavior-inline: contain;
|
|
8
|
+
scrollbar-width: thin;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/* Approved exception: container-query breakpoints stay literal because CSS vars are not reliable in query conditions. */
|
|
12
|
+
@container (max-width: 28rem) {
|
|
13
|
+
.ds-pagination [data-slot="pagination-caption"] {
|
|
14
|
+
width: 100%;
|
|
15
|
+
flex: 1 0 100%;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.ds-pagination[data-type="numeric"] .ds-pagination__controls {
|
|
19
|
+
width: 100%;
|
|
20
|
+
justify-content: center;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.ds-pagination[data-type="numeric"] .ds-pagination__pages > :nth-child(n + 4):not(:last-child) {
|
|
24
|
+
display: none;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.ds-pagination[data-type="simple"] .ds-pagination__controls {
|
|
28
|
+
margin-inline-start: auto;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.ds-pagination[data-type="summary"] [data-slot="pagination-summary"] {
|
|
32
|
+
order: -1;
|
|
33
|
+
width: 100%;
|
|
34
|
+
text-align: center;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -5,6 +5,8 @@ import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react"
|
|
|
5
5
|
import { cn } from "@/lib/utils"
|
|
6
6
|
import { useResolvedDirection } from "@/hooks/use-direction"
|
|
7
7
|
|
|
8
|
+
import "./pagination.css"
|
|
9
|
+
|
|
8
10
|
type PaginationType = "numeric" | "simple" | "summary"
|
|
9
11
|
type PaginationVariant = "soft" | "ghost"
|
|
10
12
|
type PaginationPage =
|
|
@@ -65,7 +67,7 @@ const arabicPaginationMessages: PaginationMessages = {
|
|
|
65
67
|
|
|
66
68
|
const paginationVariants = cva(
|
|
67
69
|
[
|
|
68
|
-
"flex max-w-full items-center",
|
|
70
|
+
"ds-pagination flex w-full max-w-full min-w-0 flex-wrap items-center",
|
|
69
71
|
"text-[length:var(--bh-pagination-font-size)]",
|
|
70
72
|
"leading-[var(--bh-pagination-line-height)]",
|
|
71
73
|
"tracking-[var(--bh-pagination-letter-spacing)]",
|
|
@@ -74,11 +76,11 @@ const paginationVariants = cva(
|
|
|
74
76
|
variants: {
|
|
75
77
|
type: {
|
|
76
78
|
numeric:
|
|
77
|
-
"w-[var(--bh-pagination-numeric-width)] justify-between gap-[var(--bh-pagination-root-gap)]",
|
|
79
|
+
"max-w-[var(--bh-pagination-numeric-width)] justify-between gap-[var(--bh-pagination-root-gap)]",
|
|
78
80
|
simple:
|
|
79
|
-
"w-[var(--bh-pagination-simple-width)] justify-between gap-[var(--bh-pagination-root-gap)]",
|
|
81
|
+
"max-w-[var(--bh-pagination-simple-width)] justify-between gap-[var(--bh-pagination-root-gap)]",
|
|
80
82
|
summary:
|
|
81
|
-
"w-[var(--bh-pagination-simple-width)] justify-between gap-[var(--bh-pagination-root-gap)]",
|
|
83
|
+
"max-w-[var(--bh-pagination-simple-width)] justify-between gap-[var(--bh-pagination-root-gap)]",
|
|
82
84
|
},
|
|
83
85
|
showCaption: {
|
|
84
86
|
true: "",
|
|
@@ -109,7 +111,7 @@ const paginationControlVariants = cva(
|
|
|
109
111
|
"outline-none transition-[background-color,border-color,color,box-shadow]",
|
|
110
112
|
"focus-visible:shadow-[var(--shadow-pagination-focus)]",
|
|
111
113
|
"disabled:pointer-events-none disabled:text-[var(--bh-content-disabled)]",
|
|
112
|
-
"[&_svg]:pointer-events-none [&_svg]:
|
|
114
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
113
115
|
"rtl:[&_svg[data-rtl-flip='true']]:-scale-x-100",
|
|
114
116
|
],
|
|
115
117
|
{
|
|
@@ -321,7 +323,7 @@ const Pagination = React.forwardRef<HTMLElement, PaginationProps>(function Pagin
|
|
|
321
323
|
/>
|
|
322
324
|
<div
|
|
323
325
|
data-slot="pagination-pages"
|
|
324
|
-
className="flex shrink-0 items-center gap-[var(--bh-pagination-page-gap)]"
|
|
326
|
+
className="ds-pagination__pages flex min-w-0 shrink-0 items-center gap-[var(--bh-pagination-page-gap)]"
|
|
325
327
|
>
|
|
326
328
|
{pages.map((item, index) => (
|
|
327
329
|
<PaginationPageItem
|
|
@@ -381,7 +383,7 @@ function PaginationControls({
|
|
|
381
383
|
<div
|
|
382
384
|
data-slot="pagination-controls"
|
|
383
385
|
className={cn(
|
|
384
|
-
"flex shrink-0 items-center",
|
|
386
|
+
"ds-pagination__controls flex max-w-full min-w-0 shrink-0 items-center",
|
|
385
387
|
type === "numeric"
|
|
386
388
|
? "gap-[var(--bh-pagination-control-gap)]"
|
|
387
389
|
: "gap-[var(--bh-pagination-action-gap)]"
|
|
@@ -505,13 +507,18 @@ function PaginationArrowIcon({ type }: { type: "previous" | "next" }) {
|
|
|
505
507
|
const Icon = type === "previous" ? ChevronLeftIcon : ChevronRightIcon
|
|
506
508
|
|
|
507
509
|
return (
|
|
508
|
-
<
|
|
509
|
-
aria-hidden="true"
|
|
510
|
-
data-rtl-flip="true"
|
|
510
|
+
<span
|
|
511
511
|
data-slot="pagination-icon"
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
512
|
+
className="inline-flex size-[var(--bh-pagination-icon-slot-size)] shrink-0 items-center justify-center"
|
|
513
|
+
>
|
|
514
|
+
<Icon
|
|
515
|
+
aria-hidden="true"
|
|
516
|
+
className="size-[var(--bh-pagination-icon-size)]"
|
|
517
|
+
data-rtl-flip="true"
|
|
518
|
+
focusable="false"
|
|
519
|
+
strokeWidth="var(--bh-icon-stroke-225)"
|
|
520
|
+
/>
|
|
521
|
+
</span>
|
|
515
522
|
)
|
|
516
523
|
}
|
|
517
524
|
|
|
@@ -12,7 +12,7 @@ type ProgressSliderSize = "default" | "lg"
|
|
|
12
12
|
const progressSliderVariants = cva(
|
|
13
13
|
[
|
|
14
14
|
"group/progress-slider relative flex touch-none select-none items-center",
|
|
15
|
-
"w-[var(--bh-progress-slider-width-current)]
|
|
15
|
+
"w-full max-w-[var(--bh-progress-slider-width-current)] min-w-0",
|
|
16
16
|
"data-[disabled]:pointer-events-none data-[disabled]:opacity-[var(--bh-opacity-60)]",
|
|
17
17
|
"[--bh-progress-slider-width-current:var(--bh-progress-slider-width-md)]",
|
|
18
18
|
"[--bh-progress-slider-height-current:var(--bh-progress-slider-height-md)]",
|
|
@@ -579,7 +579,7 @@ function RadioCardMedia({
|
|
|
579
579
|
<span
|
|
580
580
|
data-slot="radio-card-icon"
|
|
581
581
|
className={cn(
|
|
582
|
-
"flex size-[var(--bh-
|
|
582
|
+
"flex size-[var(--bh-icon-slot-size-default)] shrink-0 items-center justify-center text-[var(--bh-content-subtle)] [&_svg]:size-[var(--bh-icon-size-default)]",
|
|
583
583
|
disabled && "text-[var(--bh-content-disabled)]"
|
|
584
584
|
)}
|
|
585
585
|
>
|
|
@@ -592,7 +592,6 @@ function RadioCardUserIcon() {
|
|
|
592
592
|
return (
|
|
593
593
|
<UserIcon
|
|
594
594
|
aria-hidden="true"
|
|
595
|
-
className="size-[var(--bh-space-4xl-20)]"
|
|
596
595
|
strokeWidth="var(--bh-icon-stroke-200)"
|
|
597
596
|
/>
|
|
598
597
|
)
|
|
@@ -17,7 +17,9 @@ import { cn } from "@/lib/utils"
|
|
|
17
17
|
type ResizablePanelGroupProps = Omit<GroupProps, "orientation"> & {
|
|
18
18
|
direction?: Orientation
|
|
19
19
|
}
|
|
20
|
-
type ResizablePanelProps = PanelProps
|
|
20
|
+
type ResizablePanelProps = PanelProps & {
|
|
21
|
+
contentTabIndex?: number
|
|
22
|
+
}
|
|
21
23
|
type ResizableHandleProps = SeparatorProps & {
|
|
22
24
|
withHandle?: boolean
|
|
23
25
|
}
|
|
@@ -41,7 +43,39 @@ function ResizablePanelGroup({
|
|
|
41
43
|
)
|
|
42
44
|
}
|
|
43
45
|
|
|
44
|
-
|
|
46
|
+
function ResizablePanel({
|
|
47
|
+
className,
|
|
48
|
+
contentTabIndex = 0,
|
|
49
|
+
elementRef,
|
|
50
|
+
...props
|
|
51
|
+
}: ResizablePanelProps) {
|
|
52
|
+
const setElementRef = React.useCallback(
|
|
53
|
+
(node: HTMLDivElement | null) => {
|
|
54
|
+
const content = node?.firstElementChild
|
|
55
|
+
if (content instanceof HTMLElement) {
|
|
56
|
+
content.tabIndex = contentTabIndex
|
|
57
|
+
content.setAttribute("data-slot", "resizable-panel-content")
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (typeof elementRef === "function") elementRef(node)
|
|
61
|
+
else if (elementRef) {
|
|
62
|
+
;(elementRef as React.MutableRefObject<HTMLDivElement | null>).current = node
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
[contentTabIndex, elementRef]
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<Panel
|
|
70
|
+
className={cn(
|
|
71
|
+
"outline-none focus-visible:shadow-[var(--shadow-component-focus-ring)]",
|
|
72
|
+
className
|
|
73
|
+
)}
|
|
74
|
+
elementRef={setElementRef}
|
|
75
|
+
{...props}
|
|
76
|
+
/>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
45
79
|
|
|
46
80
|
function ResizableHandle({
|
|
47
81
|
className,
|
|
@@ -5,9 +5,15 @@ import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
|
|
|
5
5
|
|
|
6
6
|
import { cn } from "@/lib/utils"
|
|
7
7
|
|
|
8
|
+
type ScrollAreaOrientation = "vertical" | "horizontal" | "both"
|
|
8
9
|
type ScrollAreaProps = React.ComponentPropsWithoutRef<
|
|
9
10
|
typeof ScrollAreaPrimitive.Root
|
|
10
|
-
>
|
|
11
|
+
> & {
|
|
12
|
+
orientation?: ScrollAreaOrientation
|
|
13
|
+
viewportProps?: React.ComponentPropsWithoutRef<
|
|
14
|
+
typeof ScrollAreaPrimitive.Viewport
|
|
15
|
+
>
|
|
16
|
+
}
|
|
11
17
|
type ScrollBarProps = React.ComponentPropsWithoutRef<
|
|
12
18
|
typeof ScrollAreaPrimitive.ScrollAreaScrollbar
|
|
13
19
|
>
|
|
@@ -15,9 +21,24 @@ type ScrollBarProps = React.ComponentPropsWithoutRef<
|
|
|
15
21
|
const ScrollArea = React.forwardRef<
|
|
16
22
|
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
|
|
17
23
|
ScrollAreaProps
|
|
18
|
-
>(function ScrollArea(
|
|
24
|
+
>(function ScrollArea(
|
|
25
|
+
{
|
|
26
|
+
children,
|
|
27
|
+
className,
|
|
28
|
+
orientation = "vertical",
|
|
29
|
+
viewportProps,
|
|
30
|
+
...props
|
|
31
|
+
},
|
|
32
|
+
ref
|
|
33
|
+
) {
|
|
34
|
+
const hasVerticalScrollbar =
|
|
35
|
+
orientation === "vertical" || orientation === "both"
|
|
36
|
+
const hasHorizontalScrollbar =
|
|
37
|
+
orientation === "horizontal" || orientation === "both"
|
|
38
|
+
|
|
19
39
|
return (
|
|
20
40
|
<ScrollAreaPrimitive.Root
|
|
41
|
+
data-orientation={orientation}
|
|
21
42
|
data-slot="scroll-area"
|
|
22
43
|
ref={ref}
|
|
23
44
|
className={cn("relative overflow-hidden", className)}
|
|
@@ -25,12 +46,15 @@ const ScrollArea = React.forwardRef<
|
|
|
25
46
|
>
|
|
26
47
|
<ScrollAreaPrimitive.Viewport
|
|
27
48
|
data-slot="scroll-area-viewport"
|
|
28
|
-
|
|
49
|
+
{...viewportProps}
|
|
50
|
+
tabIndex={viewportProps?.tabIndex ?? 0}
|
|
51
|
+
className={cn("size-full rounded-[inherit] outline-none focus-visible:shadow-[var(--shadow-component-focus-ring)]", viewportProps?.className)}
|
|
29
52
|
>
|
|
30
53
|
{children}
|
|
31
54
|
</ScrollAreaPrimitive.Viewport>
|
|
32
|
-
<ScrollBar />
|
|
33
|
-
<
|
|
55
|
+
{hasVerticalScrollbar ? <ScrollBar orientation="vertical" /> : null}
|
|
56
|
+
{hasHorizontalScrollbar ? <ScrollBar orientation="horizontal" /> : null}
|
|
57
|
+
{orientation === "both" ? <ScrollAreaPrimitive.Corner /> : null}
|
|
34
58
|
</ScrollAreaPrimitive.Root>
|
|
35
59
|
)
|
|
36
60
|
})
|
|
@@ -64,4 +88,4 @@ const ScrollBar = React.forwardRef<
|
|
|
64
88
|
})
|
|
65
89
|
|
|
66
90
|
export { ScrollArea, ScrollBar }
|
|
67
|
-
export type { ScrollAreaProps, ScrollBarProps }
|
|
91
|
+
export type { ScrollAreaOrientation, ScrollAreaProps, ScrollBarProps }
|