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
|
@@ -116,7 +116,7 @@ const arabicSocialButtonMessages: SocialButtonMessages = {
|
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
const socialButtonVariants = cva(
|
|
119
|
-
"group/social-button relative inline-flex items-center justify-center whitespace-nowrap rounded-[var(--bh-social-button-radius)] bg-[var(--bh-social-button-bg)] text-[var(--bh-social-button-fg)] shadow-[var(--shadow-social-button)] outline-none transition-[background-color,box-shadow,color] [--bh-social-button-current-border:var(--bh-social-button-border)] [--shadow-social-button:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-social-button-current-border,var(--bh-social-button-border,transparent)),var(--shadow-component-default)]
|
|
119
|
+
"group/social-button relative inline-flex max-w-full min-w-0 items-center justify-center whitespace-nowrap rounded-[var(--bh-social-button-radius)] bg-[var(--bh-social-button-bg)] text-[var(--bh-social-button-fg)] shadow-[var(--shadow-social-button)] outline-none transition-[background-color,box-shadow,color] [--bh-social-button-current-border:var(--bh-social-button-border)] [--shadow-social-button:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-social-button-current-border,var(--bh-social-button-border,transparent)),var(--shadow-component-default)] hover:bg-[var(--bh-social-button-bg-hover)] focus-visible:[--bh-social-button-current-border:var(--bh-social-button-focus-border)] focus-visible:shadow-[var(--shadow-social-button-focus)] disabled:pointer-events-none disabled:opacity-[var(--bh-opacity-50)] [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
120
120
|
{
|
|
121
121
|
variants: {
|
|
122
122
|
variant: {
|
|
@@ -184,7 +184,12 @@ const SocialButton = React.forwardRef<HTMLButtonElement, SocialButtonProps>(func
|
|
|
184
184
|
? String(visibleLabel)
|
|
185
185
|
: defaultLabel
|
|
186
186
|
const Comp = asChild ? Slot : "button"
|
|
187
|
-
const cssVars = getSocialButtonVars(
|
|
187
|
+
const cssVars = getSocialButtonVars(
|
|
188
|
+
config,
|
|
189
|
+
selectedVariant,
|
|
190
|
+
isIcon,
|
|
191
|
+
resolvedDirection
|
|
192
|
+
)
|
|
188
193
|
const content = (
|
|
189
194
|
<>
|
|
190
195
|
<SocialButtonIcon platform={platform} />
|
|
@@ -192,7 +197,8 @@ const SocialButton = React.forwardRef<HTMLButtonElement, SocialButtonProps>(func
|
|
|
192
197
|
<span
|
|
193
198
|
data-slot="social-button-label"
|
|
194
199
|
dir="auto"
|
|
195
|
-
|
|
200
|
+
title={typeof visibleLabel === "string" ? visibleLabel : undefined}
|
|
201
|
+
className="min-w-0 overflow-hidden text-ellipsis pe-[var(--bh-social-button-label-padding-inline-end)]"
|
|
196
202
|
>
|
|
197
203
|
{visibleLabel}
|
|
198
204
|
</span>
|
|
@@ -238,7 +244,8 @@ function isSimpleLabel(
|
|
|
238
244
|
function getSocialButtonVars(
|
|
239
245
|
config: SocialPlatformConfig,
|
|
240
246
|
variant: SocialButtonVariant,
|
|
241
|
-
isIcon: boolean
|
|
247
|
+
isIcon: boolean,
|
|
248
|
+
direction: "ltr" | "rtl"
|
|
242
249
|
) {
|
|
243
250
|
const isSolid = variant === "solid"
|
|
244
251
|
const border = isSolid
|
|
@@ -262,7 +269,7 @@ function getSocialButtonVars(
|
|
|
262
269
|
"--bh-social-button-icon": isSolid ? config.solidIcon : config.outlineIcon,
|
|
263
270
|
"--bh-social-button-width": isIcon
|
|
264
271
|
? "var(--bh-social-button-height)"
|
|
265
|
-
: config.width
|
|
272
|
+
: config.width[direction],
|
|
266
273
|
"--bh-social-button-width-rtl": isIcon
|
|
267
274
|
? "var(--bh-social-button-height)"
|
|
268
275
|
: config.width.rtl,
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
type TableRootProps = React.ComponentPropsWithoutRef<"table">
|
|
8
|
+
type TableSectionProps = React.ComponentPropsWithoutRef<"thead">
|
|
9
|
+
type TableBodyProps = React.ComponentPropsWithoutRef<"tbody">
|
|
10
|
+
type TableRowElementProps = React.ComponentPropsWithoutRef<"tr">
|
|
11
|
+
type TableHeadProps = React.ComponentPropsWithoutRef<"th">
|
|
12
|
+
type TableCellProps = React.ComponentPropsWithoutRef<"td">
|
|
13
|
+
type DataTableSurfaceProps = React.ComponentPropsWithoutRef<"div">
|
|
14
|
+
|
|
15
|
+
const DataTableSurface = React.forwardRef<
|
|
16
|
+
HTMLDivElement,
|
|
17
|
+
DataTableSurfaceProps
|
|
18
|
+
>(function DataTableSurface({ className, role = "table", ...props }, ref) {
|
|
19
|
+
return (
|
|
20
|
+
<div
|
|
21
|
+
data-slot="data-table-surface"
|
|
22
|
+
ref={ref}
|
|
23
|
+
role={role}
|
|
24
|
+
className={cn("min-w-0", className)}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const TableRoot = React.forwardRef<HTMLTableElement, TableRootProps>(
|
|
31
|
+
function TableRoot({ className, ...props }, ref) {
|
|
32
|
+
return (
|
|
33
|
+
<table
|
|
34
|
+
data-slot="table-root"
|
|
35
|
+
ref={ref}
|
|
36
|
+
className={cn("w-full border-collapse text-start", className)}
|
|
37
|
+
{...props}
|
|
38
|
+
/>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
const TableHeader = React.forwardRef<
|
|
44
|
+
HTMLTableSectionElement,
|
|
45
|
+
TableSectionProps
|
|
46
|
+
>(function TableHeader({ className, ...props }, ref) {
|
|
47
|
+
return (
|
|
48
|
+
<thead
|
|
49
|
+
data-slot="table-header"
|
|
50
|
+
ref={ref}
|
|
51
|
+
className={cn("text-start", className)}
|
|
52
|
+
{...props}
|
|
53
|
+
/>
|
|
54
|
+
)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
const TableBody = React.forwardRef<HTMLTableSectionElement, TableBodyProps>(
|
|
58
|
+
function TableBody({ className, ...props }, ref) {
|
|
59
|
+
return (
|
|
60
|
+
<tbody
|
|
61
|
+
data-slot="table-body"
|
|
62
|
+
ref={ref}
|
|
63
|
+
className={className}
|
|
64
|
+
{...props}
|
|
65
|
+
/>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
const TableRowElement = React.forwardRef<
|
|
71
|
+
HTMLTableRowElement,
|
|
72
|
+
TableRowElementProps
|
|
73
|
+
>(function TableRowElement({ className, ...props }, ref) {
|
|
74
|
+
return (
|
|
75
|
+
<tr
|
|
76
|
+
data-slot="table-row"
|
|
77
|
+
ref={ref}
|
|
78
|
+
className={className}
|
|
79
|
+
{...props}
|
|
80
|
+
/>
|
|
81
|
+
)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
const TableHead = React.forwardRef<HTMLTableCellElement, TableHeadProps>(
|
|
85
|
+
function TableHead({ className, scope = "col", ...props }, ref) {
|
|
86
|
+
return (
|
|
87
|
+
<th
|
|
88
|
+
data-slot="table-head"
|
|
89
|
+
ref={ref}
|
|
90
|
+
className={cn("text-start", className)}
|
|
91
|
+
scope={scope}
|
|
92
|
+
{...props}
|
|
93
|
+
/>
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
const TableCell = React.forwardRef<HTMLTableCellElement, TableCellProps>(
|
|
99
|
+
function TableCell({ className, ...props }, ref) {
|
|
100
|
+
return (
|
|
101
|
+
<td
|
|
102
|
+
data-slot="table-cell"
|
|
103
|
+
ref={ref}
|
|
104
|
+
className={className}
|
|
105
|
+
{...props}
|
|
106
|
+
/>
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
export {
|
|
112
|
+
DataTableSurface,
|
|
113
|
+
TableBody,
|
|
114
|
+
TableCell,
|
|
115
|
+
TableHead,
|
|
116
|
+
TableHeader,
|
|
117
|
+
TableRoot,
|
|
118
|
+
TableRowElement,
|
|
119
|
+
}
|
|
120
|
+
export type {
|
|
121
|
+
DataTableSurfaceProps,
|
|
122
|
+
TableBodyProps,
|
|
123
|
+
TableCellProps,
|
|
124
|
+
TableHeadProps,
|
|
125
|
+
TableRootProps,
|
|
126
|
+
TableRowElementProps,
|
|
127
|
+
TableSectionProps,
|
|
128
|
+
}
|
|
@@ -154,7 +154,6 @@ function Tabs({
|
|
|
154
154
|
defaultValue ??
|
|
155
155
|
normalizedItems[defaultActiveIndex]?.value ??
|
|
156
156
|
normalizedItems[0]?.value
|
|
157
|
-
const hasPanels = normalizedItems.some((item) => item.content !== undefined)
|
|
158
157
|
|
|
159
158
|
function handleValueChange(nextValue: string) {
|
|
160
159
|
onValueChange?.(nextValue)
|
|
@@ -194,15 +193,11 @@ function Tabs({
|
|
|
194
193
|
</TabsTrigger>
|
|
195
194
|
))}
|
|
196
195
|
</TabsList>
|
|
197
|
-
{
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
</TabsContent>
|
|
203
|
-
)
|
|
204
|
-
)
|
|
205
|
-
: null}
|
|
196
|
+
{normalizedItems.map((item) => (
|
|
197
|
+
<TabsContent key={`${item.value}-panel`} value={item.value}>
|
|
198
|
+
{item.content ?? null}
|
|
199
|
+
</TabsContent>
|
|
200
|
+
))}
|
|
206
201
|
{children}
|
|
207
202
|
</TabsRoot>
|
|
208
203
|
)
|
|
@@ -348,7 +348,7 @@ function TagAvatar({
|
|
|
348
348
|
<span
|
|
349
349
|
data-slot="tag-avatar"
|
|
350
350
|
className={cn(
|
|
351
|
-
"flex size-[var(--bh-space-4xl-20)] shrink-0 items-center justify-center overflow-hidden rounded-[var(--bh-radius-full)] border border-[var(--bh-border-subtle)] bg-[var(--bh-bg-neutral-
|
|
351
|
+
"flex size-[var(--bh-space-4xl-20)] shrink-0 items-center justify-center overflow-hidden rounded-[var(--bh-radius-full)] border border-[var(--bh-border-subtle)] bg-[var(--bh-bg-neutral-bold)] text-[length:var(--bh-text-body-3xs-semibold-font-size)] font-[var(--bh-text-body-3xs-semibold-font-weight)] leading-[var(--bh-text-body-3xs-semibold-line-height)] text-[var(--bh-content-on-neutral)]",
|
|
352
352
|
className
|
|
353
353
|
)}
|
|
354
354
|
{...props}
|
|
@@ -367,7 +367,7 @@ function TagIcon({
|
|
|
367
367
|
<span
|
|
368
368
|
data-slot="tag-icon"
|
|
369
369
|
className={cn(
|
|
370
|
-
"flex size-[var(--bh-
|
|
370
|
+
"flex size-[var(--bh-icon-slot-size-default)] shrink-0 items-center justify-center text-current [&_svg]:size-[var(--bh-icon-size-default)] [&_svg]:shrink-0",
|
|
371
371
|
className
|
|
372
372
|
)}
|
|
373
373
|
{...props}
|
|
@@ -78,7 +78,7 @@ const Toggle = React.forwardRef<
|
|
|
78
78
|
>
|
|
79
79
|
<SwitchPrimitive.Thumb
|
|
80
80
|
data-slot="toggle-thumb"
|
|
81
|
-
className="pointer-events-none flex size-[var(--bh-toggle-handle-size)] translate-x-0 items-center justify-center rounded-[var(--bh-radius-full)] bg-[var(--bh-interactive-switch-handle)] text-[var(--bh-content-on-light)] shadow-[var(--shadow-
|
|
81
|
+
className="pointer-events-none flex size-[var(--bh-toggle-handle-size)] translate-x-0 items-center justify-center rounded-[var(--bh-radius-full)] bg-[var(--bh-interactive-switch-handle)] text-[var(--bh-content-on-light)] shadow-[var(--shadow-md)] transition-[background-color,transform] group-data-[disabled]/toggle:bg-[var(--bh-interactive-switch-handle-disabled)] group-data-[state=checked]/toggle:translate-x-[var(--bh-toggle-offset)] rtl:group-data-[state=checked]/toggle:-translate-x-[var(--bh-toggle-offset)]"
|
|
82
82
|
>
|
|
83
83
|
{showIcon ? (
|
|
84
84
|
<span
|
|
@@ -190,7 +190,7 @@ const ToggleField = React.forwardRef<HTMLDivElement, ToggleFieldProps>(
|
|
|
190
190
|
data-slot="toggle-field-description"
|
|
191
191
|
dir="auto"
|
|
192
192
|
id={descriptionId}
|
|
193
|
-
className="w-full min-w-0 break-words text-[length:var(--bh-text-body-xs-regular-font-size)] font-[var(--bh-text-body-xs-regular-font-weight)] leading-[var(--bh-text-body-xs-regular-line-height)] tracking-[var(--bh-text-body-xs-regular-letter-spacing)] text-[var(--bh-content-subtle)]
|
|
193
|
+
className="w-full min-w-0 break-words text-[length:var(--bh-text-body-xs-regular-font-size)] font-[var(--bh-text-body-xs-regular-font-weight)] leading-[var(--bh-text-body-xs-regular-line-height)] tracking-[var(--bh-text-body-xs-regular-letter-spacing)] text-[var(--bh-content-subtle)]"
|
|
194
194
|
>
|
|
195
195
|
{description}
|
|
196
196
|
</span>
|
|
@@ -39,7 +39,7 @@ const toolbarVariants = cva(
|
|
|
39
39
|
)
|
|
40
40
|
|
|
41
41
|
const toolbarSectionVariants = cva(
|
|
42
|
-
"flex min-w-0 items-center gap-[var(--bh-space-xl-12)]",
|
|
42
|
+
"flex max-w-full min-w-0 items-center gap-[var(--bh-space-xl-12)]",
|
|
43
43
|
{
|
|
44
44
|
variants: {
|
|
45
45
|
align: {
|
|
@@ -49,8 +49,8 @@ const toolbarSectionVariants = cva(
|
|
|
49
49
|
between: "justify-between",
|
|
50
50
|
},
|
|
51
51
|
grow: {
|
|
52
|
-
true: "flex-
|
|
53
|
-
false: "
|
|
52
|
+
true: "flex-[1_1_var(--bh-space-19xl-384)]",
|
|
53
|
+
false: "flex-[0_1_auto]",
|
|
54
54
|
},
|
|
55
55
|
stack: {
|
|
56
56
|
true: "flex-col items-stretch",
|
|
@@ -58,20 +58,20 @@ const toolbarSectionVariants = cva(
|
|
|
58
58
|
},
|
|
59
59
|
wrap: {
|
|
60
60
|
true: "flex-wrap",
|
|
61
|
-
false: "",
|
|
61
|
+
false: "overflow-x-auto",
|
|
62
62
|
},
|
|
63
63
|
},
|
|
64
64
|
defaultVariants: {
|
|
65
65
|
align: "start",
|
|
66
66
|
grow: false,
|
|
67
67
|
stack: false,
|
|
68
|
-
wrap:
|
|
68
|
+
wrap: true,
|
|
69
69
|
},
|
|
70
70
|
}
|
|
71
71
|
)
|
|
72
72
|
|
|
73
73
|
const toolbarButtonVariants = cva(
|
|
74
|
-
"group/toolbar-button inline-flex min-w-0 items-center justify-center whitespace-nowrap rounded-[var(--bh-control-default)] text-[length:var(--bh-text-button-font-size)] font-[var(--bh-text-button-font-weight)] leading-[var(--bh-text-button-line-height)] tracking-[var(--bh-text-button-letter-spacing)] outline-none transition-[background-color,border-color,color,box-shadow] focus-visible:shadow-[var(--shadow-button-focus)] disabled:pointer-events-none disabled:shadow-none aria-invalid:border-[length:var(--bh-border-width-default)] aria-invalid:border-[var(--bh-border-danger-strong)] aria-invalid:shadow-[var(--shadow-button-danger-focus)] [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-[var(--bh-button-icon-size)] [&_svg[data-icon='inline-start']]:ms-[var(--bh-button-icon-offset)] [&_svg[data-icon='inline-end']]:me-[var(--bh-button-icon-offset)] rtl:[&_svg[data-rtl-flip='true']]:-scale-x-100",
|
|
74
|
+
"group/toolbar-button inline-flex min-w-0 items-center justify-center whitespace-nowrap rounded-[var(--bh-control-default)] text-[length:var(--bh-text-button-font-size)] font-[var(--bh-text-button-font-weight)] leading-[var(--bh-text-button-line-height)] tracking-[var(--bh-text-button-letter-spacing)] outline-none transition-[background-color,border-color,color,box-shadow] focus-visible:shadow-[var(--shadow-button-focus)] disabled:pointer-events-none disabled:shadow-none aria-invalid:border-[length:var(--bh-border-width-default)] aria-invalid:border-[var(--bh-border-danger-strong)] aria-invalid:shadow-[var(--shadow-button-danger-focus)] [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-[var(--bh-button-icon-size)] [&_svg:not([class*='size-'])]:m-[calc((var(--bh-button-icon-slot-size)-var(--bh-button-icon-size))/2)] [&_svg[data-icon='inline-start']]:ms-[calc(var(--bh-button-icon-offset)+(var(--bh-button-icon-slot-size)-var(--bh-button-icon-size))/2)] [&_svg[data-icon='inline-end']]:me-[calc(var(--bh-button-icon-offset)+(var(--bh-button-icon-slot-size)-var(--bh-button-icon-size))/2)] rtl:[&_svg[data-rtl-flip='true']]:-scale-x-100",
|
|
75
75
|
{
|
|
76
76
|
variants: {
|
|
77
77
|
variant: {
|
|
@@ -38,7 +38,7 @@ const tooltipContentVariants = cva(
|
|
|
38
38
|
dark:
|
|
39
39
|
"[--bh-tooltip-bg:var(--bh-bg-always-dark)] [--bh-tooltip-border:transparent] [--bh-tooltip-close-fg:var(--bh-content-on-color)] [--bh-tooltip-fg:var(--bh-content-on-color)] [--bh-tooltip-shadow:none] [--bh-tooltip-shortcut-bg:var(--bh-bg-neutral-soft)] [--bh-tooltip-shortcut-border:var(--bh-border-inverse-subtle)]",
|
|
40
40
|
default:
|
|
41
|
-
"[--bh-tooltip-bg:var(--bh-bg-raised)] [--bh-tooltip-border:var(--bh-border-subtle)] [--bh-tooltip-close-fg:var(--bh-content-subtle)] [--bh-tooltip-fg:var(--bh-content-default)] [--bh-tooltip-shadow:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-tooltip-border),var(--shadow-
|
|
41
|
+
"[--bh-tooltip-bg:var(--bh-bg-raised)] [--bh-tooltip-border:var(--bh-border-subtle)] [--bh-tooltip-close-fg:var(--bh-content-subtle)] [--bh-tooltip-fg:var(--bh-content-default)] [--bh-tooltip-shadow:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-tooltip-border),var(--shadow-lg)] [--bh-tooltip-shortcut-bg:var(--bh-bg-neutral-subtle)] [--bh-tooltip-shortcut-border:var(--bh-border-subtle)]",
|
|
42
42
|
},
|
|
43
43
|
size: {
|
|
44
44
|
sm:
|
|
@@ -54,7 +54,7 @@ export function AttributeDemo() {
|
|
|
54
54
|
</AttributeItem>
|
|
55
55
|
<AttributeItem label="Invoice">
|
|
56
56
|
<span className="inline-flex min-w-0 items-center gap-[var(--bh-space-md-8)]">
|
|
57
|
-
<PaperclipIcon className="size-[var(--bh-
|
|
57
|
+
<PaperclipIcon className="m-[var(--bh-icon-slot-inset-default)] size-[var(--bh-icon-size-default)] shrink-0 text-[var(--bh-content-muted)]" />
|
|
58
58
|
INV-2024-00123.pdf
|
|
59
59
|
</span>
|
|
60
60
|
</AttributeItem>
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
AvatarImage,
|
|
9
9
|
AvatarStatus,
|
|
10
10
|
} from "@/components/ui/avatar"
|
|
11
|
+
import { Button } from "@/components/ui/button"
|
|
11
12
|
import { PlusIcon } from "lucide-react"
|
|
12
13
|
|
|
13
14
|
const avatarUrl = new URL("../assets/avatars/avatar-01.jpg", import.meta.url).href
|
|
@@ -84,13 +85,15 @@ export function AvatarDemo() {
|
|
|
84
85
|
))}
|
|
85
86
|
<AvatarGroupCount size="md">+10</AvatarGroupCount>
|
|
86
87
|
</AvatarGroup>
|
|
87
|
-
<
|
|
88
|
+
<Button
|
|
88
89
|
aria-label="Add member"
|
|
89
|
-
className="
|
|
90
|
-
size="
|
|
90
|
+
className="size-[var(--bh-avatar-size-md)] rounded-[var(--bh-radius-full)] border-dashed"
|
|
91
|
+
size="icon-sm"
|
|
92
|
+
type="button"
|
|
93
|
+
variant="outline"
|
|
91
94
|
>
|
|
92
95
|
<PlusIcon aria-hidden="true" className="size-[var(--bh-space-3xl-16)]" />
|
|
93
|
-
</
|
|
96
|
+
</Button>
|
|
94
97
|
</div>
|
|
95
98
|
|
|
96
99
|
<div className="flex items-center gap-[var(--bh-space-xl-12)]">
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ActivityHeatmap,
|
|
3
|
+
type ActivityHeatmapItem,
|
|
4
|
+
type ActivityHeatmapLevel,
|
|
5
|
+
} from "@/components/ui/expanded/ActivityHeatmap"
|
|
6
|
+
|
|
7
|
+
const activityItems: ActivityHeatmapItem[] = Array.from(
|
|
8
|
+
{ length: 39 * 7 },
|
|
9
|
+
(_, index) => {
|
|
10
|
+
const column = Math.floor(index / 7)
|
|
11
|
+
const row = index % 7
|
|
12
|
+
const level = ((column * 11 + row * 7 + column * row * 3) % 5) as ActivityHeatmapLevel
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
id: `activity-${index}`,
|
|
16
|
+
label: `Activity level ${level} of 4`,
|
|
17
|
+
level,
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
export function ActivityHeatmapDemo() {
|
|
23
|
+
return (
|
|
24
|
+
<ActivityHeatmap
|
|
25
|
+
ariaLabel="Weekly activity over the last 39 weeks"
|
|
26
|
+
items={activityItems}
|
|
27
|
+
/>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
@@ -30,16 +30,16 @@ const rtlBreadcrumbItems: BreadcrumbItem[] = [
|
|
|
30
30
|
export function BreadcrumbsDemo() {
|
|
31
31
|
return (
|
|
32
32
|
<div className="grid gap-[var(--bh-space-3xl-16)]">
|
|
33
|
-
<Breadcrumbs aria-label="
|
|
33
|
+
<Breadcrumbs aria-label="Default breadcrumb" items={breadcrumbItems} overflowLabel="More" />
|
|
34
34
|
<Breadcrumbs
|
|
35
|
-
aria-label="
|
|
35
|
+
aria-label="Raised breadcrumb"
|
|
36
36
|
items={breadcrumbItems}
|
|
37
37
|
overflowLabel="More"
|
|
38
38
|
separator="chevron"
|
|
39
39
|
style="raised"
|
|
40
40
|
/>
|
|
41
41
|
<Breadcrumbs
|
|
42
|
-
aria-label="
|
|
42
|
+
aria-label="Collapsed breadcrumb"
|
|
43
43
|
items={longBreadcrumbItems}
|
|
44
44
|
maxItems={4}
|
|
45
45
|
overflowLabel="More"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Kbd } from "@/components/ui/kbd"
|
|
1
|
+
import { Kbd, Shortcut } from "@/components/ui/kbd"
|
|
2
2
|
|
|
3
3
|
export function KbdDemo() {
|
|
4
4
|
return (
|
|
@@ -6,6 +6,7 @@ export function KbdDemo() {
|
|
|
6
6
|
<Kbd>⌘</Kbd>
|
|
7
7
|
<Kbd>K</Kbd>
|
|
8
8
|
<Kbd size="md">Shift</Kbd>
|
|
9
|
+
<Shortcut keys={["Mod", "K"]} />
|
|
9
10
|
</div>
|
|
10
11
|
)
|
|
11
12
|
}
|
|
@@ -6,14 +6,14 @@ import {
|
|
|
6
6
|
export function PaginationDemo() {
|
|
7
7
|
return (
|
|
8
8
|
<div className="grid gap-[var(--bh-space-5xl-24)]">
|
|
9
|
-
<Pagination />
|
|
10
|
-
<Pagination variant="ghost" />
|
|
9
|
+
<Pagination aria-label="Soft numeric pagination" />
|
|
10
|
+
<Pagination aria-label="Ghost numeric pagination" variant="ghost" />
|
|
11
11
|
|
|
12
|
-
<Pagination type="simple" />
|
|
13
|
-
<Pagination type="summary" />
|
|
12
|
+
<Pagination aria-label="Simple pagination" type="simple" />
|
|
13
|
+
<Pagination aria-label="Summary pagination" type="summary" />
|
|
14
14
|
|
|
15
15
|
<div dir="rtl">
|
|
16
|
-
<Pagination dir="rtl" messages={arabicPaginationMessages} />
|
|
16
|
+
<Pagination aria-label="ترقيم الصفحات العربية" dir="rtl" messages={arabicPaginationMessages} />
|
|
17
17
|
</div>
|
|
18
18
|
</div>
|
|
19
19
|
)
|
|
@@ -1,41 +1,79 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
CreditCardIcon,
|
|
3
|
+
InboxIcon,
|
|
4
|
+
LayoutGridIcon,
|
|
5
|
+
SettingsIcon,
|
|
6
|
+
UsersIcon,
|
|
7
|
+
} from "lucide-react"
|
|
2
8
|
|
|
9
|
+
import { Avatar, AvatarFallback } from "@/components/ui/avatar"
|
|
3
10
|
import {
|
|
4
11
|
Sidebar,
|
|
5
12
|
SidebarBody,
|
|
6
13
|
SidebarFooter,
|
|
14
|
+
SidebarFooterUser,
|
|
7
15
|
SidebarGroup,
|
|
8
|
-
SidebarGroupLabel,
|
|
9
16
|
SidebarHeader,
|
|
17
|
+
SidebarHeaderWorkspaceSwitch,
|
|
10
18
|
SidebarInset,
|
|
11
19
|
SidebarMenu,
|
|
20
|
+
SidebarMenuBadge,
|
|
12
21
|
SidebarMenuButton,
|
|
13
22
|
SidebarMenuItem,
|
|
23
|
+
SidebarMenuSub,
|
|
24
|
+
SidebarMenuSubButton,
|
|
14
25
|
SidebarProvider,
|
|
15
|
-
SidebarTrigger,
|
|
16
26
|
} from "@/components/ui/sidebar"
|
|
17
27
|
|
|
18
28
|
export function SidebarDemo() {
|
|
19
29
|
return (
|
|
20
|
-
<SidebarProvider className="flex h-[var(--bh-space-
|
|
30
|
+
<SidebarProvider className="flex h-[var(--bh-space-19xl-384)]">
|
|
21
31
|
<Sidebar>
|
|
22
32
|
<SidebarHeader>
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
33
|
+
<SidebarHeaderWorkspaceSwitch
|
|
34
|
+
image={
|
|
35
|
+
<span className="grid size-full place-items-center bg-[var(--bh-bg-brand-default)] text-xs font-semibold text-[var(--bh-content-on-brand)]">
|
|
36
|
+
AC
|
|
37
|
+
</span>
|
|
38
|
+
}
|
|
39
|
+
label="Acme Inc"
|
|
40
|
+
/>
|
|
27
41
|
</SidebarHeader>
|
|
28
42
|
<SidebarBody>
|
|
29
43
|
<SidebarGroup>
|
|
30
|
-
<SidebarGroupLabel>Workspace</SidebarGroupLabel>
|
|
31
44
|
<SidebarMenu>
|
|
32
45
|
<SidebarMenuItem>
|
|
33
|
-
<SidebarMenuButton
|
|
46
|
+
<SidebarMenuButton icon={<LayoutGridIcon />}>
|
|
34
47
|
Overview
|
|
35
48
|
</SidebarMenuButton>
|
|
36
49
|
</SidebarMenuItem>
|
|
37
50
|
<SidebarMenuItem>
|
|
38
|
-
<SidebarMenuButton icon={<
|
|
51
|
+
<SidebarMenuButton icon={<CreditCardIcon />}>
|
|
52
|
+
Transactions
|
|
53
|
+
</SidebarMenuButton>
|
|
54
|
+
<SidebarMenuSub>
|
|
55
|
+
<SidebarMenuItem>
|
|
56
|
+
<SidebarMenuSubButton showLine>
|
|
57
|
+
Payments
|
|
58
|
+
</SidebarMenuSubButton>
|
|
59
|
+
</SidebarMenuItem>
|
|
60
|
+
<SidebarMenuItem>
|
|
61
|
+
<SidebarMenuSubButton active showLine>
|
|
62
|
+
Refunds
|
|
63
|
+
</SidebarMenuSubButton>
|
|
64
|
+
</SidebarMenuItem>
|
|
65
|
+
</SidebarMenuSub>
|
|
66
|
+
</SidebarMenuItem>
|
|
67
|
+
<SidebarMenuItem>
|
|
68
|
+
<SidebarMenuButton
|
|
69
|
+
badge={<SidebarMenuBadge>12</SidebarMenuBadge>}
|
|
70
|
+
icon={<InboxIcon />}
|
|
71
|
+
>
|
|
72
|
+
Inbox
|
|
73
|
+
</SidebarMenuButton>
|
|
74
|
+
</SidebarMenuItem>
|
|
75
|
+
<SidebarMenuItem>
|
|
76
|
+
<SidebarMenuButton icon={<UsersIcon />}>Customers</SidebarMenuButton>
|
|
39
77
|
</SidebarMenuItem>
|
|
40
78
|
<SidebarMenuItem>
|
|
41
79
|
<SidebarMenuButton icon={<SettingsIcon />}>Settings</SidebarMenuButton>
|
|
@@ -44,10 +82,18 @@ export function SidebarDemo() {
|
|
|
44
82
|
</SidebarGroup>
|
|
45
83
|
</SidebarBody>
|
|
46
84
|
<SidebarFooter>
|
|
47
|
-
<
|
|
85
|
+
<SidebarFooterUser
|
|
86
|
+
avatar={
|
|
87
|
+
<Avatar aria-label="Olivia Rhye" className="size-full" size="sm">
|
|
88
|
+
<AvatarFallback size="sm">OR</AvatarFallback>
|
|
89
|
+
</Avatar>
|
|
90
|
+
}
|
|
91
|
+
name="Olivia Rhye"
|
|
92
|
+
supportText="olivia@acme.com"
|
|
93
|
+
/>
|
|
48
94
|
</SidebarFooter>
|
|
49
95
|
</Sidebar>
|
|
50
|
-
<SidebarInset className="grid place-items-center bg-[var(--bh-bg-subtle)] p-[var(--bh-space-4xl-20)] text-sm text-[var(--bh-content-subtle)]">
|
|
96
|
+
<SidebarInset className="grid min-w-0 place-items-center bg-[var(--bh-bg-subtle)] p-[var(--bh-space-4xl-20)] text-sm text-[var(--bh-content-subtle)]">
|
|
51
97
|
App content
|
|
52
98
|
</SidebarInset>
|
|
53
99
|
</SidebarProvider>
|
|
@@ -15,7 +15,7 @@ This project uses the Banhaten design system. Treat the installed component API
|
|
|
15
15
|
- Do not align Banhaten controls with raw height, padding, radius, or font-size utilities. Width and responsive layout utilities are allowed.
|
|
16
16
|
- Use `DatePicker` or `DateRangePicker` for dates. Do not simulate calendar fields with `Input` or native date inputs.
|
|
17
17
|
- Pass explicit `leadingIcon` and `trailingIcon` nodes. Use `showAtSign` or `showInfo` only for those exact semantic affordances. The legacy `hasLeadingIcon` and `hasInformationIcon` flags are deprecated.
|
|
18
|
-
- Use `shortcutKeys={["Mod", "K"]}` or `KbdShortcut`
|
|
18
|
+
- Use `shortcutKeys={["Mod", "K"]}` on search inputs, `Shortcut` for one compact combined hint, or `KbdShortcut` when each physical key needs its own keycap. Never hard-code a single Command glyph for every platform.
|
|
19
19
|
- Use `SegmentedControl` for small mutually exclusive view, density, period, or numeric choices. Do not rebuild joined numeric buttons locally.
|
|
20
20
|
- Use functional `Select` for option menus. `ToolbarSelect` is only a trigger-shaped toolbar primitive.
|
|
21
21
|
- Preserve inherited `dir`; use logical spacing and alignment utilities for RTL-safe composition.
|