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
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import type { ReactNode } from "react";
|
|
1
|
+
import type { ElementType, ReactNode } from "react";
|
|
2
2
|
import { HomeIcon as LucideHomeIcon } from "lucide-react";
|
|
3
|
+
import { Button } from "@/components/ui/button";
|
|
4
|
+
import { Breadcrumbs as OwnedBreadcrumbs } from "./Breadcrumbs";
|
|
3
5
|
import { Tabs, type TabsProps } from "../tabs";
|
|
4
6
|
import "./pageHeader.css";
|
|
5
7
|
|
|
@@ -31,6 +33,7 @@ export type PageHeaderProps = {
|
|
|
31
33
|
description?: string;
|
|
32
34
|
dir?: "ltr" | "rtl";
|
|
33
35
|
icon?: ReactNode | false;
|
|
36
|
+
headingLevel?: 1 | 2 | 3 | 4 | 5 | 6;
|
|
34
37
|
metaInfo?: string;
|
|
35
38
|
tabs?: PageHeaderTabs | false;
|
|
36
39
|
title?: string;
|
|
@@ -44,64 +47,43 @@ function HomeIcon() {
|
|
|
44
47
|
return <LucideHomeIcon aria-hidden="true" className="ds-page-header__home-icon" strokeWidth="var(--bh-icon-stroke-190)" />;
|
|
45
48
|
}
|
|
46
49
|
|
|
47
|
-
function Breadcrumbs({ items, label }: { items: PageHeaderBreadcrumb[]; label?: string }) {
|
|
48
|
-
return (
|
|
49
|
-
<nav aria-label={label} className="ds-page-header__breadcrumbs">
|
|
50
|
-
<ol>
|
|
51
|
-
{items.map((item, index) => {
|
|
52
|
-
const isLast = index === items.length - 1;
|
|
53
|
-
const isCurrent = item.current === true;
|
|
54
|
-
const content = (
|
|
55
|
-
<>
|
|
56
|
-
{item.icon && <span className="ds-page-header__breadcrumb-icon">{item.icon}</span>}
|
|
57
|
-
<span className="ds-page-header__breadcrumb-label">{item.label}</span>
|
|
58
|
-
</>
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
return (
|
|
62
|
-
<li key={`${item.label}-${index}`}>
|
|
63
|
-
{item.href && !isCurrent ? (
|
|
64
|
-
<a href={item.href}>{content}</a>
|
|
65
|
-
) : (
|
|
66
|
-
<span aria-current={isCurrent ? "page" : undefined} className={isCurrent ? "is-current" : undefined}>
|
|
67
|
-
{content}
|
|
68
|
-
</span>
|
|
69
|
-
)}
|
|
70
|
-
{!isLast && (
|
|
71
|
-
<span aria-hidden="true" className="ds-page-header__breadcrumb-separator">
|
|
72
|
-
/
|
|
73
|
-
</span>
|
|
74
|
-
)}
|
|
75
|
-
</li>
|
|
76
|
-
);
|
|
77
|
-
})}
|
|
78
|
-
</ol>
|
|
79
|
-
</nav>
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
50
|
function Action({ action }: { action: PageHeaderAction }) {
|
|
84
51
|
const className = "ds-page-header__action";
|
|
85
52
|
const label = <span>{action.label}</span>;
|
|
86
53
|
|
|
87
54
|
if (action.href) {
|
|
88
55
|
return (
|
|
89
|
-
<
|
|
90
|
-
|
|
91
|
-
|
|
56
|
+
<Button asChild className={className} variant="secondary">
|
|
57
|
+
<a
|
|
58
|
+
aria-disabled={action.disabled || undefined}
|
|
59
|
+
aria-label={action.ariaLabel}
|
|
60
|
+
href={action.disabled ? undefined : action.href}
|
|
61
|
+
onClick={(event) => {
|
|
62
|
+
if (action.disabled) {
|
|
63
|
+
event.preventDefault();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
action.onClick?.();
|
|
67
|
+
}}
|
|
68
|
+
tabIndex={action.disabled ? -1 : undefined}
|
|
69
|
+
>
|
|
70
|
+
{label}
|
|
71
|
+
</a>
|
|
72
|
+
</Button>
|
|
92
73
|
);
|
|
93
74
|
}
|
|
94
75
|
|
|
95
76
|
return (
|
|
96
|
-
<
|
|
77
|
+
<Button
|
|
97
78
|
aria-label={action.ariaLabel}
|
|
98
79
|
className={className}
|
|
99
80
|
disabled={action.disabled}
|
|
100
81
|
onClick={action.onClick}
|
|
101
82
|
type="button"
|
|
83
|
+
variant="secondary"
|
|
102
84
|
>
|
|
103
85
|
{label}
|
|
104
|
-
</
|
|
86
|
+
</Button>
|
|
105
87
|
);
|
|
106
88
|
}
|
|
107
89
|
|
|
@@ -112,6 +94,7 @@ export function PageHeader({
|
|
|
112
94
|
className,
|
|
113
95
|
description,
|
|
114
96
|
dir,
|
|
97
|
+
headingLevel = 1,
|
|
115
98
|
icon,
|
|
116
99
|
metaInfo,
|
|
117
100
|
tabs = {},
|
|
@@ -120,10 +103,18 @@ export function PageHeader({
|
|
|
120
103
|
const breadcrumbItems = breadcrumbs === false ? [] : breadcrumbs;
|
|
121
104
|
const actionItems = actions === false ? [] : actions;
|
|
122
105
|
const showIcon = icon !== false;
|
|
106
|
+
const Heading = `h${headingLevel}` as ElementType;
|
|
123
107
|
|
|
124
108
|
return (
|
|
125
109
|
<header className={classes("ds-page-header", className)} dir={dir}>
|
|
126
|
-
{breadcrumbItems.length > 0 &&
|
|
110
|
+
{breadcrumbItems.length > 0 && (
|
|
111
|
+
<OwnedBreadcrumbs
|
|
112
|
+
aria-label={breadcrumbLabel ?? "Breadcrumb"}
|
|
113
|
+
className="ds-page-header__breadcrumbs"
|
|
114
|
+
dir={dir}
|
|
115
|
+
items={breadcrumbItems}
|
|
116
|
+
/>
|
|
117
|
+
)}
|
|
127
118
|
|
|
128
119
|
<div className="ds-page-header__section">
|
|
129
120
|
<div className="ds-page-header__topline">
|
|
@@ -131,7 +122,7 @@ export function PageHeader({
|
|
|
131
122
|
<div className="ds-page-header__heading-row">
|
|
132
123
|
<div className="ds-page-header__heading-main">
|
|
133
124
|
{showIcon && <span className="ds-page-header__icon">{icon ?? <HomeIcon />}</span>}
|
|
134
|
-
{title && <
|
|
125
|
+
{title && <Heading className="ds-page-header__title">{title}</Heading>}
|
|
135
126
|
</div>
|
|
136
127
|
{metaInfo && (
|
|
137
128
|
<span className="ds-page-header__meta">
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { useId, type ReactNode } from "react";
|
|
2
2
|
import { XIcon } from "lucide-react";
|
|
3
|
+
import { Button } from "@/components/ui/button";
|
|
3
4
|
import "./slideout.css";
|
|
4
5
|
|
|
5
6
|
export type SlideoutSize = "small" | "medium" | "large";
|
|
@@ -48,7 +49,7 @@ export function Slideout({
|
|
|
48
49
|
size = "small",
|
|
49
50
|
title,
|
|
50
51
|
}: SlideoutProps) {
|
|
51
|
-
const
|
|
52
|
+
const titleId = useId();
|
|
52
53
|
const hasFooterActions = Boolean(primaryActionLabel || (secondaryAction && secondaryActionLabel));
|
|
53
54
|
|
|
54
55
|
return (
|
|
@@ -57,39 +58,43 @@ export function Slideout({
|
|
|
57
58
|
"ds-slideout",
|
|
58
59
|
`ds-slideout--${size}`,
|
|
59
60
|
`ds-slideout--${breakpoint}`,
|
|
60
|
-
rtl && "ds-slideout--rtl",
|
|
61
61
|
className,
|
|
62
62
|
)}
|
|
63
63
|
dir={dir}
|
|
64
64
|
>
|
|
65
|
-
<section
|
|
65
|
+
<section
|
|
66
|
+
aria-labelledby={title ? titleId : undefined}
|
|
67
|
+
className="ds-slideout__panel"
|
|
68
|
+
data-slot="slideout-panel"
|
|
69
|
+
role={title ? "region" : undefined}
|
|
70
|
+
>
|
|
66
71
|
<header className="ds-slideout__header">
|
|
67
72
|
<div className="ds-slideout__heading-wrap">
|
|
68
|
-
<h2 className="ds-slideout__title">{title}</h2>
|
|
73
|
+
{title ? <h2 className="ds-slideout__title" id={titleId}>{title}</h2> : null}
|
|
69
74
|
{headerAction && actionLabel && (
|
|
70
|
-
<
|
|
75
|
+
<Button className="ds-slideout__text-button" size="sm" type="button" variant="link" onClick={onAction}>
|
|
71
76
|
{actionLabel}
|
|
72
|
-
</
|
|
77
|
+
</Button>
|
|
73
78
|
)}
|
|
74
79
|
</div>
|
|
75
80
|
{closeLabel && (
|
|
76
|
-
<
|
|
81
|
+
<Button aria-label={closeLabel} className="ds-slideout__close" size="icon-sm" type="button" variant="ghost" onClick={onClose}>
|
|
77
82
|
<CloseIcon />
|
|
78
|
-
</
|
|
83
|
+
</Button>
|
|
79
84
|
)}
|
|
80
85
|
</header>
|
|
81
86
|
<div className="ds-slideout__body">{children}</div>
|
|
82
87
|
{footer && hasFooterActions && (
|
|
83
88
|
<footer className="ds-slideout__footer">
|
|
84
89
|
{primaryActionLabel && (
|
|
85
|
-
<
|
|
90
|
+
<Button className="ds-slideout__button ds-slideout__button--primary" size="sm" type="button" onClick={onPrimaryAction}>
|
|
86
91
|
{primaryActionLabel}
|
|
87
|
-
</
|
|
92
|
+
</Button>
|
|
88
93
|
)}
|
|
89
94
|
{secondaryAction && secondaryActionLabel && (
|
|
90
|
-
<
|
|
95
|
+
<Button className="ds-slideout__button ds-slideout__button--secondary" size="sm" type="button" variant="secondary" onClick={onSecondaryAction}>
|
|
91
96
|
{secondaryActionLabel}
|
|
92
|
-
</
|
|
97
|
+
</Button>
|
|
93
98
|
)}
|
|
94
99
|
</footer>
|
|
95
100
|
)}
|
|
@@ -25,6 +25,34 @@ export type StepsProps = {
|
|
|
25
25
|
items?: StepItem[];
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
+
export type ProgressStepState = "complete" | "active" | "todo";
|
|
29
|
+
|
|
30
|
+
export type ProgressStep = {
|
|
31
|
+
label?: ReactNode;
|
|
32
|
+
marker?: ReactNode;
|
|
33
|
+
state?: ProgressStepState;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type ProgressStepsClassNames = {
|
|
37
|
+
connector?: string;
|
|
38
|
+
item?: string;
|
|
39
|
+
label?: string;
|
|
40
|
+
marker?: string;
|
|
41
|
+
segment?: string;
|
|
42
|
+
track?: string;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export type ProgressStepsProps = {
|
|
46
|
+
activeIndex?: number;
|
|
47
|
+
ariaLabel: string;
|
|
48
|
+
className?: string;
|
|
49
|
+
classNames?: ProgressStepsClassNames;
|
|
50
|
+
dir?: "ltr" | "rtl";
|
|
51
|
+
items: ProgressStep[];
|
|
52
|
+
label?: ReactNode;
|
|
53
|
+
variant?: "bars" | "markers";
|
|
54
|
+
};
|
|
55
|
+
|
|
28
56
|
export type HorizontalStepItemProps = {
|
|
29
57
|
alignment?: StepAlignment;
|
|
30
58
|
caption?: ReactNode;
|
|
@@ -66,13 +94,12 @@ export function Steps({
|
|
|
66
94
|
indicator = "icon",
|
|
67
95
|
items,
|
|
68
96
|
}: StepsProps) {
|
|
69
|
-
const rtl = dir === "rtl";
|
|
70
97
|
const stepItems = items ?? [];
|
|
71
98
|
|
|
72
99
|
if (direction === "vertical") {
|
|
73
100
|
return (
|
|
74
101
|
<div
|
|
75
|
-
className={["ds-steps", "ds-steps--vertical",
|
|
102
|
+
className={["ds-steps", "ds-steps--vertical", className]
|
|
76
103
|
.filter(Boolean)
|
|
77
104
|
.join(" ")}
|
|
78
105
|
dir={dir}
|
|
@@ -102,7 +129,7 @@ export function Steps({
|
|
|
102
129
|
|
|
103
130
|
return (
|
|
104
131
|
<div
|
|
105
|
-
className={["ds-steps", "ds-steps--horizontal",
|
|
132
|
+
className={["ds-steps", "ds-steps--horizontal", className]
|
|
106
133
|
.filter(Boolean)
|
|
107
134
|
.join(" ")}
|
|
108
135
|
dir={dir}
|
|
@@ -122,6 +149,122 @@ export function Steps({
|
|
|
122
149
|
);
|
|
123
150
|
}
|
|
124
151
|
|
|
152
|
+
export function ProgressSteps({
|
|
153
|
+
activeIndex = 0,
|
|
154
|
+
ariaLabel,
|
|
155
|
+
className = "",
|
|
156
|
+
classNames = {},
|
|
157
|
+
dir,
|
|
158
|
+
items,
|
|
159
|
+
label,
|
|
160
|
+
variant = "markers",
|
|
161
|
+
}: ProgressStepsProps) {
|
|
162
|
+
const rootClassName = [
|
|
163
|
+
"ds-progress-steps",
|
|
164
|
+
`ds-progress-steps--${variant}`,
|
|
165
|
+
className,
|
|
166
|
+
]
|
|
167
|
+
.filter(Boolean)
|
|
168
|
+
.join(" ");
|
|
169
|
+
|
|
170
|
+
if (variant === "bars") {
|
|
171
|
+
return (
|
|
172
|
+
<div
|
|
173
|
+
aria-label={ariaLabel}
|
|
174
|
+
aria-valuemax={items.length}
|
|
175
|
+
aria-valuemin={1}
|
|
176
|
+
aria-valuenow={Math.min(activeIndex + 1, items.length)}
|
|
177
|
+
className={rootClassName}
|
|
178
|
+
dir={dir}
|
|
179
|
+
role="progressbar"
|
|
180
|
+
>
|
|
181
|
+
{label ? <span data-slot="progress-steps-label">{label}</span> : null}
|
|
182
|
+
<div
|
|
183
|
+
aria-hidden="true"
|
|
184
|
+
className={["ds-progress-steps__track", classNames.track]
|
|
185
|
+
.filter(Boolean)
|
|
186
|
+
.join(" ")}
|
|
187
|
+
data-slot="progress-steps-track"
|
|
188
|
+
>
|
|
189
|
+
{items.map((item, index) => (
|
|
190
|
+
<span
|
|
191
|
+
className={["ds-progress-steps__segment", classNames.segment]
|
|
192
|
+
.filter(Boolean)
|
|
193
|
+
.join(" ")}
|
|
194
|
+
data-active={index <= activeIndex ? "true" : "false"}
|
|
195
|
+
data-slot="progress-steps-segment"
|
|
196
|
+
data-state={item.state ?? (index < activeIndex ? "complete" : index === activeIndex ? "active" : "todo")}
|
|
197
|
+
key={index}
|
|
198
|
+
/>
|
|
199
|
+
))}
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return (
|
|
206
|
+
<ol aria-label={ariaLabel} className={rootClassName} dir={dir}>
|
|
207
|
+
{items.map((item, index) => {
|
|
208
|
+
const state =
|
|
209
|
+
item.state ??
|
|
210
|
+
(index < activeIndex
|
|
211
|
+
? "complete"
|
|
212
|
+
: index === activeIndex
|
|
213
|
+
? "active"
|
|
214
|
+
: "todo");
|
|
215
|
+
|
|
216
|
+
return (
|
|
217
|
+
<li
|
|
218
|
+
className={[
|
|
219
|
+
"ds-progress-steps__item",
|
|
220
|
+
`is-${state}`,
|
|
221
|
+
classNames.item,
|
|
222
|
+
]
|
|
223
|
+
.filter(Boolean)
|
|
224
|
+
.join(" ")}
|
|
225
|
+
data-slot="progress-steps-item"
|
|
226
|
+
data-state={state}
|
|
227
|
+
key={index}
|
|
228
|
+
>
|
|
229
|
+
<span
|
|
230
|
+
aria-hidden="true"
|
|
231
|
+
className={["ds-progress-steps__marker", classNames.marker]
|
|
232
|
+
.filter(Boolean)
|
|
233
|
+
.join(" ")}
|
|
234
|
+
data-slot="progress-steps-marker"
|
|
235
|
+
>
|
|
236
|
+
{item.marker ?? index + 1}
|
|
237
|
+
</span>
|
|
238
|
+
{item.label ? (
|
|
239
|
+
<span
|
|
240
|
+
className={["ds-progress-steps__label", classNames.label]
|
|
241
|
+
.filter(Boolean)
|
|
242
|
+
.join(" ")}
|
|
243
|
+
data-slot="progress-steps-item-label"
|
|
244
|
+
>
|
|
245
|
+
{item.label}
|
|
246
|
+
</span>
|
|
247
|
+
) : null}
|
|
248
|
+
{index < items.length - 1 ? (
|
|
249
|
+
<span
|
|
250
|
+
aria-hidden="true"
|
|
251
|
+
className={[
|
|
252
|
+
"ds-progress-steps__connector",
|
|
253
|
+
state === "complete" && "is-complete",
|
|
254
|
+
classNames.connector,
|
|
255
|
+
]
|
|
256
|
+
.filter(Boolean)
|
|
257
|
+
.join(" ")}
|
|
258
|
+
data-slot="progress-steps-connector"
|
|
259
|
+
/>
|
|
260
|
+
) : null}
|
|
261
|
+
</li>
|
|
262
|
+
);
|
|
263
|
+
})}
|
|
264
|
+
</ol>
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
125
268
|
export function HorizontalStepItem({
|
|
126
269
|
alignment = "leading",
|
|
127
270
|
caption,
|
|
@@ -135,7 +278,6 @@ export function HorizontalStepItem({
|
|
|
135
278
|
showLeadingLine = true,
|
|
136
279
|
showTrailingLine = true,
|
|
137
280
|
}: HorizontalStepItemProps) {
|
|
138
|
-
const rtl = dir === "rtl";
|
|
139
281
|
const linePlacement = getHorizontalLinePlacement(alignment, showLeadingLine, showTrailingLine);
|
|
140
282
|
const text = (
|
|
141
283
|
<div className="ds-horizontal-step-item__text">
|
|
@@ -157,7 +299,6 @@ export function HorizontalStepItem({
|
|
|
157
299
|
"ds-horizontal-step-item",
|
|
158
300
|
`ds-horizontal-step-item--${alignment}`,
|
|
159
301
|
`ds-horizontal-step-item--${position}`,
|
|
160
|
-
rtl ? "ds-horizontal-step-item--rtl" : "ds-horizontal-step-item--ltr",
|
|
161
302
|
className,
|
|
162
303
|
]
|
|
163
304
|
.filter(Boolean)
|
|
@@ -189,7 +330,6 @@ export function VerticalStepItem({
|
|
|
189
330
|
slotContent,
|
|
190
331
|
supportText,
|
|
191
332
|
}: VerticalStepItemProps & { children?: ReactNode }) {
|
|
192
|
-
const rtl = dir === "rtl";
|
|
193
333
|
const marker = (
|
|
194
334
|
<div className="ds-vertical-step-item__rail" aria-hidden="true">
|
|
195
335
|
<StepMarker indicator={indicator} number={number} />
|
|
@@ -205,7 +345,6 @@ export function VerticalStepItem({
|
|
|
205
345
|
className={[
|
|
206
346
|
"ds-vertical-step-item",
|
|
207
347
|
`ds-vertical-step-item--${alignment}`,
|
|
208
|
-
rtl ? "ds-vertical-step-item--rtl" : "ds-vertical-step-item--ltr",
|
|
209
348
|
className,
|
|
210
349
|
]
|
|
211
350
|
.filter(Boolean)
|
|
@@ -216,9 +355,8 @@ export function VerticalStepItem({
|
|
|
216
355
|
<div className="ds-vertical-step-item__content">
|
|
217
356
|
<div className="ds-vertical-step-item__copy">
|
|
218
357
|
<div className="ds-vertical-step-item__header">
|
|
219
|
-
{rtl && hasSupportText && supportText && <div className="ds-vertical-step-item__support">{supportText}</div>}
|
|
220
358
|
{label && <div className="ds-vertical-step-item__label">{label}</div>}
|
|
221
|
-
{
|
|
359
|
+
{hasSupportText && supportText && <div className="ds-vertical-step-item__support">{supportText}</div>}
|
|
222
360
|
</div>
|
|
223
361
|
{hasCaption && caption && <div className="ds-vertical-step-item__caption">{caption}</div>}
|
|
224
362
|
</div>
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
EllipsisIcon,
|
|
6
6
|
EyeIcon,
|
|
7
7
|
FileIcon as LucideFileIcon,
|
|
8
|
+
StarIcon,
|
|
8
9
|
Trash2Icon,
|
|
9
10
|
} from "lucide-react";
|
|
10
11
|
import { Pagination, type PaginationPage, type PaginationProps } from "@/components/ui/pagination";
|
|
@@ -135,6 +136,7 @@ export type DataTableSearch<Row extends TableRowBase> =
|
|
|
135
136
|
};
|
|
136
137
|
|
|
137
138
|
export type DataTablePagination = {
|
|
139
|
+
ariaLabel?: string;
|
|
138
140
|
defaultPage?: number;
|
|
139
141
|
manual?: boolean;
|
|
140
142
|
onPageChange?: (page: number) => void;
|
|
@@ -216,7 +218,12 @@ export function Table<Row extends TableRowBase>({
|
|
|
216
218
|
}
|
|
217
219
|
|
|
218
220
|
return (
|
|
219
|
-
<div
|
|
221
|
+
<div
|
|
222
|
+
aria-label="Scrollable table"
|
|
223
|
+
className={`ds-table-wrap ds-table-wrap--${size}`}
|
|
224
|
+
dir={dir}
|
|
225
|
+
tabIndex={0}
|
|
226
|
+
>
|
|
220
227
|
<table className="ds-table">
|
|
221
228
|
<thead>
|
|
222
229
|
<tr>
|
|
@@ -238,18 +245,20 @@ export function Table<Row extends TableRowBase>({
|
|
|
238
245
|
) : column.header ? (
|
|
239
246
|
column.sortable ? (
|
|
240
247
|
onSortChange ? (
|
|
241
|
-
<
|
|
248
|
+
<Button
|
|
242
249
|
aria-label={`Sort by ${column.header}`}
|
|
243
250
|
className={cx(
|
|
244
251
|
"ds-table__sort",
|
|
245
252
|
sort?.columnId === column.id && `ds-table__sort--${sort.direction}`,
|
|
246
253
|
)}
|
|
247
254
|
onClick={() => onSortChange(getNextSort(sort, column.id))}
|
|
255
|
+
size="xs"
|
|
248
256
|
type="button"
|
|
257
|
+
variant="ghost"
|
|
249
258
|
>
|
|
250
259
|
<span>{column.header}</span>
|
|
251
260
|
<SortIcon direction={sort?.columnId === column.id ? sort.direction : undefined} />
|
|
252
|
-
</
|
|
261
|
+
</Button>
|
|
253
262
|
) : (
|
|
254
263
|
<span className="ds-table__sort ds-table__sort--static">
|
|
255
264
|
<span>{column.header}</span>
|
|
@@ -259,7 +268,11 @@ export function Table<Row extends TableRowBase>({
|
|
|
259
268
|
) : (
|
|
260
269
|
column.header
|
|
261
270
|
)
|
|
262
|
-
) :
|
|
271
|
+
) : (
|
|
272
|
+
<span className="sr-only">
|
|
273
|
+
{column.kind === "actions" ? "Actions" : column.id}
|
|
274
|
+
</span>
|
|
275
|
+
)}
|
|
263
276
|
</th>
|
|
264
277
|
))}
|
|
265
278
|
</tr>
|
|
@@ -283,7 +296,7 @@ export function Table<Row extends TableRowBase>({
|
|
|
283
296
|
) : column.renderCell ? (
|
|
284
297
|
column.renderCell(row)
|
|
285
298
|
) : column.item ? (
|
|
286
|
-
renderTableItem(column.item(row))
|
|
299
|
+
renderTableItem(column.item(row), size)
|
|
287
300
|
) : null}
|
|
288
301
|
</td>
|
|
289
302
|
))}
|
|
@@ -492,6 +505,10 @@ export function DataTable<Row extends TableRowBase>({
|
|
|
492
505
|
<footer className="ds-data-table__footer">
|
|
493
506
|
<ToolbarText>{labels.resultsSummary({ from: visibleFrom, to: visibleTo, total: totalRows })}</ToolbarText>
|
|
494
507
|
<Pagination
|
|
508
|
+
aria-label={
|
|
509
|
+
paginationConfig.ariaLabel ??
|
|
510
|
+
(typeof title === "string" ? `${title} pagination` : "Table pagination")
|
|
511
|
+
}
|
|
495
512
|
dir={dir}
|
|
496
513
|
nextDisabled={currentPage >= pageCount}
|
|
497
514
|
onPageChange={setDataTablePage}
|
|
@@ -755,7 +772,7 @@ function clampNumber(value: number, min: number, max: number) {
|
|
|
755
772
|
return Math.min(max, Math.max(min, Math.floor(value)));
|
|
756
773
|
}
|
|
757
774
|
|
|
758
|
-
function renderTableItem(item: TableItem) {
|
|
775
|
+
function renderTableItem(item: TableItem, tableSize: TableSize) {
|
|
759
776
|
switch (item.type) {
|
|
760
777
|
case "checkbox":
|
|
761
778
|
return (
|
|
@@ -772,7 +789,7 @@ function renderTableItem(item: TableItem) {
|
|
|
772
789
|
case "avatarText":
|
|
773
790
|
return (
|
|
774
791
|
<span className="ds-table-avatar-text">
|
|
775
|
-
<Avatar name={item.name} src={item.src} />
|
|
792
|
+
<Avatar name={item.name} size={tableSize === "lg" ? "md" : "sm"} src={item.src} />
|
|
776
793
|
<span>
|
|
777
794
|
<strong>{item.name}</strong>
|
|
778
795
|
{item.caption && <em>{item.caption}</em>}
|
|
@@ -796,7 +813,7 @@ function renderTableItem(item: TableItem) {
|
|
|
796
813
|
case "badges":
|
|
797
814
|
return <BadgeList items={item.items} maxVisible={item.maxVisible} />;
|
|
798
815
|
case "onlyAvatar":
|
|
799
|
-
return <Avatar name={item.alt} src={item.src} />;
|
|
816
|
+
return <Avatar name={item.alt} size={tableSize === "lg" ? "md" : "xs"} src={item.src} />;
|
|
800
817
|
case "avatarStack":
|
|
801
818
|
return (
|
|
802
819
|
<AvatarStack
|
|
@@ -805,6 +822,7 @@ function renderTableItem(item: TableItem) {
|
|
|
805
822
|
canAdd={item.canAdd}
|
|
806
823
|
count={item.count}
|
|
807
824
|
onAdd={item.onAdd}
|
|
825
|
+
size={tableSize}
|
|
808
826
|
/>
|
|
809
827
|
);
|
|
810
828
|
case "tags":
|
|
@@ -855,7 +873,9 @@ function SelectionCheckbox({
|
|
|
855
873
|
);
|
|
856
874
|
}
|
|
857
875
|
|
|
858
|
-
|
|
876
|
+
type TableAvatarSize = "xs" | "sm" | "md";
|
|
877
|
+
|
|
878
|
+
function Avatar({ name, size, src }: { name: string; size: TableAvatarSize; src?: string }) {
|
|
859
879
|
const initials = name
|
|
860
880
|
.split(" ")
|
|
861
881
|
.map((part) => part[0])
|
|
@@ -864,9 +884,9 @@ function Avatar({ name, src }: { name: string; src?: string }) {
|
|
|
864
884
|
.toUpperCase();
|
|
865
885
|
|
|
866
886
|
return (
|
|
867
|
-
<AvatarPrimitive className="ds-table-avatar" size=
|
|
887
|
+
<AvatarPrimitive className="ds-table-avatar" size={size}>
|
|
868
888
|
{src ? <AvatarImage alt="" src={src} /> : null}
|
|
869
|
-
<AvatarFallback>{initials}</AvatarFallback>
|
|
889
|
+
<AvatarFallback size={size}>{initials}</AvatarFallback>
|
|
870
890
|
</AvatarPrimitive>
|
|
871
891
|
);
|
|
872
892
|
}
|
|
@@ -877,23 +897,25 @@ function AvatarStack({
|
|
|
877
897
|
canAdd,
|
|
878
898
|
count,
|
|
879
899
|
onAdd,
|
|
900
|
+
size,
|
|
880
901
|
}: {
|
|
881
902
|
addLabel?: string;
|
|
882
903
|
avatars: AvatarItem[];
|
|
883
904
|
canAdd?: boolean;
|
|
884
905
|
count?: number;
|
|
885
906
|
onAdd?: () => void;
|
|
907
|
+
size: TableSize;
|
|
886
908
|
}) {
|
|
887
909
|
return (
|
|
888
|
-
<span className=
|
|
910
|
+
<span className={`ds-table-avatar-stack ds-table-avatar-stack--${size}`}>
|
|
889
911
|
{avatars.slice(0, 3).map((avatar) => (
|
|
890
|
-
<Avatar key={avatar.alt} name={avatar.alt} src={avatar.src} />
|
|
912
|
+
<Avatar key={avatar.alt} name={avatar.alt} size={size === "lg" ? "md" : "xs"} src={avatar.src} />
|
|
891
913
|
))}
|
|
892
914
|
{count ? <span className="ds-table-avatar ds-table-avatar--count">+{count}</span> : null}
|
|
893
915
|
{canAdd ? (
|
|
894
|
-
<
|
|
916
|
+
<Button aria-label={addLabel} disabled={!onAdd} onClick={onAdd} size="icon-xs" type="button" variant="ghost">
|
|
895
917
|
+
|
|
896
|
-
</
|
|
918
|
+
</Button>
|
|
897
919
|
) : null}
|
|
898
920
|
</span>
|
|
899
921
|
);
|
|
@@ -938,10 +960,11 @@ function TagList({ items, maxVisible = items.length }: { items: string[]; maxVis
|
|
|
938
960
|
function Progress({ label, tone, value }: { label?: string; tone: "success" | "brand"; value: number }) {
|
|
939
961
|
return (
|
|
940
962
|
<ProgressPrimitive
|
|
963
|
+
aria-label={label ?? "Progress"}
|
|
941
964
|
className={`ds-table-progress ds-table-progress--${tone}`}
|
|
942
965
|
indicator={label}
|
|
943
966
|
labelPosition="inline"
|
|
944
|
-
size="
|
|
967
|
+
size="lg"
|
|
945
968
|
value={value}
|
|
946
969
|
/>
|
|
947
970
|
);
|
|
@@ -949,9 +972,18 @@ function Progress({ label, tone, value }: { label?: string; tone: "success" | "b
|
|
|
949
972
|
|
|
950
973
|
function Rating({ max, value }: { max: number; value: number }) {
|
|
951
974
|
return (
|
|
952
|
-
<span
|
|
975
|
+
<span
|
|
976
|
+
aria-label={`${value} out of ${max}`}
|
|
977
|
+
className="ds-table-rating"
|
|
978
|
+
role="img"
|
|
979
|
+
>
|
|
953
980
|
{Array.from({ length: max }, (_, index) => (
|
|
954
|
-
<
|
|
981
|
+
<StarIcon
|
|
982
|
+
aria-hidden="true"
|
|
983
|
+
className={index < value ? "is-filled" : undefined}
|
|
984
|
+
key={index}
|
|
985
|
+
strokeWidth="var(--bh-icon-stroke-190)"
|
|
986
|
+
/>
|
|
955
987
|
))}
|
|
956
988
|
</span>
|
|
957
989
|
);
|