banhaten 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.md +21 -9
  2. package/package.json +8 -2
  3. package/registry/components/accordion.tsx +37 -1
  4. package/registry/components/alert.tsx +14 -28
  5. package/registry/components/attribute.tsx +6 -10
  6. package/registry/components/autocomplete.tsx +637 -0
  7. package/registry/components/avatar.tsx +259 -24
  8. package/registry/components/badge.tsx +97 -35
  9. package/registry/components/button-group.tsx +1 -1
  10. package/registry/components/card.tsx +1 -1
  11. package/registry/components/checkbox.tsx +19 -16
  12. package/registry/components/date-picker-state.ts +253 -0
  13. package/registry/components/date-picker.tsx +115 -158
  14. package/registry/components/expanded/ActivityFeed.tsx +37 -23
  15. package/registry/components/expanded/Banner.tsx +54 -19
  16. package/registry/components/expanded/Breadcrumbs.tsx +10 -38
  17. package/registry/components/expanded/CatalogComponentsShowcase.tsx +11 -16
  18. package/registry/components/expanded/CatalogTag.tsx +4 -11
  19. package/registry/components/expanded/CommandBar.tsx +33 -53
  20. package/registry/components/expanded/EmptyState.tsx +155 -0
  21. package/registry/components/expanded/FileUpload.tsx +362 -59
  22. package/registry/components/expanded/OnboardingStepListItem.tsx +6 -10
  23. package/registry/components/expanded/PageHeader.tsx +2 -11
  24. package/registry/components/expanded/Slideout.tsx +12 -23
  25. package/registry/components/expanded/Steps.tsx +6 -8
  26. package/registry/components/expanded/Table.tsx +18 -40
  27. package/registry/components/expanded/Timeline.tsx +5 -24
  28. package/registry/components/expanded/activityFeed.css +10 -54
  29. package/registry/components/expanded/banner.css +8 -75
  30. package/registry/components/expanded/breadcrumbs.css +1 -1
  31. package/registry/components/expanded/commandBar.css +23 -26
  32. package/registry/components/expanded/divider.css +1 -1
  33. package/registry/components/expanded/emptyState.css +111 -0
  34. package/registry/components/expanded/fileUpload.css +304 -75
  35. package/registry/components/expanded/pageHeader.css +1 -1
  36. package/registry/components/expanded/slideout.css +1 -0
  37. package/registry/components/expanded/steps.css +15 -51
  38. package/registry/components/expanded/table.css +6 -1
  39. package/registry/components/expanded/timeline.css +18 -15
  40. package/registry/components/input-otp.tsx +574 -0
  41. package/registry/components/input.tsx +140 -59
  42. package/registry/components/menu.tsx +470 -80
  43. package/registry/components/pagination.tsx +6 -18
  44. package/registry/components/popover.tsx +840 -0
  45. package/registry/components/radio-card.tsx +25 -31
  46. package/registry/components/select-content.tsx +28 -123
  47. package/registry/components/select.tsx +13 -9
  48. package/registry/components/skeleton.css +57 -0
  49. package/registry/components/skeleton.tsx +482 -0
  50. package/registry/components/social-button.tsx +24 -90
  51. package/registry/components/spinner.tsx +91 -7
  52. package/registry/components/textarea.tsx +21 -36
  53. package/registry/components/toggle.tsx +7 -23
  54. package/registry/components/tooltip.tsx +8 -4
  55. package/registry/examples/attribute-demo.tsx +2 -2
  56. package/registry/examples/autocomplete-demo.tsx +109 -0
  57. package/registry/examples/avatar-demo.tsx +102 -47
  58. package/registry/examples/badge-demo.tsx +16 -0
  59. package/registry/examples/checkbox-demo.tsx +3 -8
  60. package/registry/examples/date-picker-demo.tsx +75 -22
  61. package/registry/examples/expanded/banner-demo.tsx +31 -6
  62. package/registry/examples/expanded/breadcrumbs-demo.tsx +59 -0
  63. package/registry/examples/expanded/command-bar-demo.tsx +236 -0
  64. package/registry/examples/expanded/empty-state-demo.tsx +39 -0
  65. package/registry/examples/expanded/file-upload-demo.tsx +60 -0
  66. package/registry/examples/expanded/steps-demo.tsx +11 -0
  67. package/registry/examples/expanded/table-demo.tsx +142 -0
  68. package/registry/examples/input-demo.tsx +1 -1
  69. package/registry/examples/input-otp-demo.tsx +72 -0
  70. package/registry/examples/menu-demo.tsx +101 -88
  71. package/registry/examples/popover-demo.tsx +546 -0
  72. package/registry/examples/progress-demo.tsx +2 -2
  73. package/registry/examples/select-demo.tsx +32 -18
  74. package/registry/examples/skeleton-demo.tsx +56 -0
  75. package/registry/examples/social-button-demo.tsx +33 -33
  76. package/registry/examples/spinner-demo.tsx +59 -0
  77. package/registry/examples/tag-demo.tsx +1 -1
  78. package/registry/examples/textarea-demo.tsx +1 -1
  79. package/registry/index.json +266 -20
  80. package/registry/styles/globals.css +93 -3
  81. package/src/cli/index.js +997 -62
@@ -12,58 +12,111 @@ const selectedRange: CalendarRange = {
12
12
  to: new Date(2050, 7, 23),
13
13
  }
14
14
 
15
+ const staticPopoverClassName =
16
+ "static left-auto top-auto mt-2 translate-x-0"
17
+ const datePickerClassName =
18
+ "w-[var(--bh-date-surface-width-picker)] max-w-full"
19
+ const dateRangePickerClassName = "w-max max-w-none"
20
+
15
21
  export function DatePickerDemo() {
16
22
  return (
17
23
  <div className="grid gap-8">
18
- <div className="flex flex-wrap items-start gap-6">
19
- <Calendar value={selectedDate} />
20
- <Calendar view="month-year" value={selectedDate} />
24
+ <div className="grid gap-6 lg:grid-cols-[repeat(auto-fit,minmax(260px,max-content))]">
25
+ <div data-date-demo-item>
26
+ <Calendar value={selectedDate} />
27
+ </div>
28
+ <div data-date-demo-item>
29
+ <Calendar view="month-year" value={selectedDate} />
30
+ </div>
21
31
  </div>
22
32
 
23
33
  <div className="grid gap-6 xl:grid-cols-2">
24
- <DatePicker defaultValue={selectedDate} />
25
- <DatePicker defaultOpen defaultValue={selectedDate} />
26
- <div dir="rtl">
34
+ <div data-date-demo-item>
35
+ <DatePicker
36
+ calendarProps={{ className: staticPopoverClassName }}
37
+ className={datePickerClassName}
38
+ defaultValue={selectedDate}
39
+ />
40
+ </div>
41
+ <div data-date-demo-item dir="rtl">
27
42
  <DatePicker
43
+ calendarProps={{ className: staticPopoverClassName }}
44
+ className={datePickerClassName}
28
45
  dir="rtl"
29
- placeholder="\u0627\u062e\u062a\u0631 \u062a\u0627\u0631\u064a\u062e"
46
+ placeholder={"\u0627\u062e\u062a\u0631 \u062a\u0627\u0631\u064a\u062e"}
47
+ />
48
+ </div>
49
+ </div>
50
+
51
+ <div className="grid gap-6 xl:grid-cols-2">
52
+ <div data-date-demo-item>
53
+ <DatePicker
54
+ calendarProps={{ className: staticPopoverClassName }}
55
+ className={datePickerClassName}
56
+ defaultOpen
57
+ defaultValue={selectedDate}
30
58
  />
31
59
  </div>
32
- <div dir="rtl">
60
+ <div data-date-demo-item dir="rtl">
33
61
  <DatePicker
62
+ calendarProps={{ className: staticPopoverClassName }}
63
+ className={datePickerClassName}
34
64
  defaultOpen
35
65
  defaultValue={selectedDate}
36
66
  dir="rtl"
37
- placeholder="\u0627\u062e\u062a\u0631 \u062a\u0627\u0631\u064a\u062e"
67
+ placeholder={"\u0627\u062e\u062a\u0631 \u062a\u0627\u0631\u064a\u062e"}
38
68
  />
39
69
  </div>
40
70
  </div>
41
71
 
42
72
  <div className="grid gap-6 overflow-x-auto">
43
- <RangeCalendar value={selectedRange} />
44
- <RangeCalendar type="double-with-presets" value={selectedRange} />
45
- <RangeCalendar type="single" value={selectedRange} />
73
+ <div data-date-demo-item>
74
+ <RangeCalendar value={selectedRange} />
75
+ </div>
76
+ <div data-date-demo-item>
77
+ <RangeCalendar type="double-with-presets" value={selectedRange} />
78
+ </div>
79
+ <div data-date-demo-item>
80
+ <RangeCalendar type="single" value={selectedRange} />
81
+ </div>
46
82
  </div>
47
83
 
48
84
  <div className="grid gap-6 xl:grid-cols-2">
49
- <DateRangePicker value={selectedRange} />
50
- <DateRangePicker
51
- defaultOpen
52
- type="double-with-presets"
53
- value={selectedRange}
54
- />
55
- <div dir="rtl">
85
+ <div data-date-demo-item className="overflow-x-auto pb-2">
86
+ <DateRangePicker
87
+ calendarProps={{ className: staticPopoverClassName }}
88
+ className={dateRangePickerClassName}
89
+ value={selectedRange}
90
+ />
91
+ </div>
92
+ <div data-date-demo-item className="overflow-x-auto pb-2" dir="rtl">
56
93
  <DateRangePicker
94
+ calendarProps={{ className: staticPopoverClassName }}
95
+ className={dateRangePickerClassName}
57
96
  dir="rtl"
58
- placeholder="\u0627\u062e\u062a\u0631 \u0646\u0637\u0627\u0642 \u0627\u0644\u062a\u0627\u0631\u064a\u062e"
97
+ placeholder={"\u0627\u062e\u062a\u0631 \u0646\u0637\u0627\u0642 \u0627\u0644\u062a\u0627\u0631\u064a\u062e"}
98
+ value={selectedRange}
99
+ />
100
+ </div>
101
+ </div>
102
+
103
+ <div className="grid gap-6">
104
+ <div data-date-demo-item className="overflow-x-auto pb-2">
105
+ <DateRangePicker
106
+ calendarProps={{ className: staticPopoverClassName }}
107
+ className={dateRangePickerClassName}
108
+ defaultOpen
109
+ type="double-with-presets"
59
110
  value={selectedRange}
60
111
  />
61
112
  </div>
62
- <div dir="rtl">
113
+ <div data-date-demo-item className="overflow-x-auto pb-2" dir="rtl">
63
114
  <DateRangePicker
115
+ calendarProps={{ className: staticPopoverClassName }}
116
+ className={dateRangePickerClassName}
64
117
  defaultOpen
65
118
  dir="rtl"
66
- placeholder="\u064a\u0648\u0645 / \u0634\u0647\u0631 / \u0633\u0646\u0629"
119
+ placeholder={"\u064a\u0648\u0645 / \u0634\u0647\u0631 / \u0633\u0646\u0629"}
67
120
  type="double-with-presets"
68
121
  value={selectedRange}
69
122
  />
@@ -1,23 +1,48 @@
1
1
  import { Banner } from "@/components/ui/expanded/Banner"
2
- import { BannerBoard as ExpandedBannerBoard } from "@/components/ui/expanded/BannerBoard"
3
2
 
4
3
  const bannerDemoCopy = {
5
4
  actionLabel: "Button",
6
5
  ariaLabel: "Announcement banner",
7
6
  closeLabel: "Dismiss banner",
8
7
  description: "Discover the Latest Updates in Our New Release",
9
- inputAriaLabel: "Banner input",
10
- inputPlaceholder: "Placeholder",
11
8
  linkLabel: "Link button",
12
9
  title: "Big news!",
13
10
  }
14
11
 
12
+ const rtlBannerDemoCopy = {
13
+ actionLabel: "تطبيق",
14
+ ariaLabel: "شريط إعلان",
15
+ closeLabel: "إغلاق",
16
+ description: "اكتشف آخر التحديثات في إصدارنا الجديد",
17
+ linkLabel: "الرابط",
18
+ title: "أخبار مهمة!",
19
+ }
20
+
15
21
  export function BannerDemo() {
16
22
  return (
17
- <div className="grid gap-6">
23
+ <div className="grid gap-4">
18
24
  <Banner {...bannerDemoCopy} />
19
- <Banner {...bannerDemoCopy} type="input" />
20
- <ExpandedBannerBoard />
25
+ <Banner {...bannerDemoCopy} type="single-action-inline" />
26
+ <Banner {...bannerDemoCopy} size="full-width" type="single-action" />
27
+ <Banner
28
+ {...bannerDemoCopy}
29
+ color="grey"
30
+ content="title-description"
31
+ size="full-width"
32
+ type="single-action"
33
+ />
34
+ <Banner
35
+ {...bannerDemoCopy}
36
+ color="brand-light"
37
+ content="title-description"
38
+ type="single-action-inline"
39
+ />
40
+ <Banner
41
+ {...rtlBannerDemoCopy}
42
+ content="title-description"
43
+ dir="rtl"
44
+ type="single-action-inline"
45
+ />
21
46
  </div>
22
47
  )
23
48
  }
@@ -0,0 +1,59 @@
1
+ import {
2
+ BreadcrumbFolderIcon,
3
+ BreadcrumbHomeIcon,
4
+ Breadcrumbs,
5
+ type BreadcrumbItem,
6
+ } from "@/components/ui/expanded/Breadcrumbs"
7
+
8
+ const breadcrumbItems: BreadcrumbItem[] = [
9
+ { label: "Home", href: "#", icon: <BreadcrumbHomeIcon /> },
10
+ { label: "Design system", href: "#", icon: <BreadcrumbFolderIcon /> },
11
+ { label: "Components", href: "#" },
12
+ { label: "Breadcrumbs", current: true },
13
+ ]
14
+
15
+ const longBreadcrumbItems: BreadcrumbItem[] = [
16
+ { label: "Home", href: "#", icon: <BreadcrumbHomeIcon /> },
17
+ { label: "Workspace", href: "#", icon: <BreadcrumbFolderIcon /> },
18
+ { label: "Design System", href: "#" },
19
+ { label: "Components", href: "#" },
20
+ { label: "Navigation", href: "#" },
21
+ { label: "Breadcrumbs", current: true },
22
+ ]
23
+
24
+ const rtlBreadcrumbItems: BreadcrumbItem[] = [
25
+ { label: "\u0627\u0644\u0631\u0626\u064a\u0633\u064a\u0629", href: "#", icon: <BreadcrumbHomeIcon /> },
26
+ { label: "\u0627\u0644\u0646\u0638\u0627\u0645", href: "#", icon: <BreadcrumbFolderIcon /> },
27
+ { label: "\u0627\u0644\u0645\u0643\u0648\u0646\u0627\u062a", current: true },
28
+ ]
29
+
30
+ export function BreadcrumbsDemo() {
31
+ return (
32
+ <div className="grid gap-4">
33
+ <Breadcrumbs aria-label="Breadcrumb" items={breadcrumbItems} overflowLabel="More" />
34
+ <Breadcrumbs
35
+ aria-label="Breadcrumb"
36
+ items={breadcrumbItems}
37
+ overflowLabel="More"
38
+ separator="chevron"
39
+ style="raised"
40
+ />
41
+ <Breadcrumbs
42
+ aria-label="Breadcrumb"
43
+ items={longBreadcrumbItems}
44
+ maxItems={4}
45
+ overflowLabel="More"
46
+ separator="chevron"
47
+ style="raised"
48
+ />
49
+ <Breadcrumbs
50
+ aria-label={"\u0627\u0644\u0645\u0633\u0627\u0631"}
51
+ dir="rtl"
52
+ items={rtlBreadcrumbItems}
53
+ overflowLabel={"\u0627\u0644\u0645\u0632\u064a\u062f"}
54
+ separator="chevron"
55
+ style="raised"
56
+ />
57
+ </div>
58
+ )
59
+ }
@@ -1,5 +1,101 @@
1
+ import * as React from "react"
2
+ import {
3
+ BoxIcon,
4
+ CheckIcon,
5
+ CloudIcon,
6
+ EyeIcon,
7
+ FileTextIcon,
8
+ GitPullRequestIcon,
9
+ LayoutGridIcon,
10
+ MessageSquareIcon,
11
+ PencilIcon,
12
+ RefreshCcwIcon,
13
+ SearchIcon,
14
+ Settings2Icon,
15
+ ShieldCheckIcon,
16
+ WorkflowIcon,
17
+ XIcon,
18
+ } from "lucide-react"
19
+
20
+ import { Avatar, AvatarIcon, AvatarStatus } from "@/components/ui/avatar"
21
+ import { Badge } from "@/components/ui/badge"
22
+ import { Button } from "@/components/ui/button"
23
+ import {
24
+ Menu,
25
+ MenuItem,
26
+ MenuItemDescription,
27
+ MenuItemText,
28
+ MenuItemTitle,
29
+ } from "@/components/ui/menu"
30
+ import { ModalBody, ModalHeader, ModalSurface } from "@/components/ui/modal"
31
+ import {
32
+ Toolbar,
33
+ ToolbarButton,
34
+ ToolbarSearch,
35
+ ToolbarSection,
36
+ } from "@/components/ui/toolbar"
1
37
  import { CommandBar } from "@/components/ui/expanded/CommandBar"
2
38
 
39
+ type ToolTone = "neutral" | "blue" | "green" | "amber" | "purple" | "sky"
40
+ type ToolIcon = React.ComponentType<React.SVGProps<SVGSVGElement>>
41
+
42
+ type ToolItem = {
43
+ added?: boolean
44
+ description?: string
45
+ icon: ToolIcon
46
+ title: string
47
+ tone?: ToolTone
48
+ }
49
+
50
+ const popularTools: ToolItem[] = [
51
+ {
52
+ added: true,
53
+ icon: EyeIcon,
54
+ title: "View repositories & issues",
55
+ tone: "green",
56
+ },
57
+ {
58
+ description: "Create, comment, update issues",
59
+ icon: PencilIcon,
60
+ title: "Create & update issues",
61
+ },
62
+ {
63
+ description: "Use commit and repo activity",
64
+ icon: RefreshCcwIcon,
65
+ title: "Sync commits and activity",
66
+ },
67
+ {
68
+ description: "Review and update pull requests",
69
+ icon: GitPullRequestIcon,
70
+ title: "Manage pull requests",
71
+ },
72
+ {
73
+ description: "Run GitHub workflows",
74
+ icon: WorkflowIcon,
75
+ title: "Trigger workflows & automations",
76
+ },
77
+ ]
78
+
79
+ const serverTools: ToolItem[] = [
80
+ { icon: BoxIcon, title: "Box", tone: "blue" },
81
+ { icon: CloudIcon, title: "Google Drive", tone: "green" },
82
+ { icon: Settings2Icon, title: "Hubspot", tone: "amber" },
83
+ { icon: MessageSquareIcon, title: "Intercom", tone: "sky" },
84
+ { icon: WorkflowIcon, title: "Jira", tone: "blue" },
85
+ { icon: FileTextIcon, title: "Notion" },
86
+ { icon: ShieldCheckIcon, title: "Okta" },
87
+ { icon: CloudIcon, title: "Salesforce", tone: "sky" },
88
+ ]
89
+
90
+ const toolToneClassNames: Record<ToolTone, string> = {
91
+ amber: "text-[var(--bh-content-accent-amber-default)]",
92
+ blue: "text-[var(--bh-content-accent-blue-default)]",
93
+ green: "text-[var(--bh-content-accent-green-default)]",
94
+ neutral: "text-[var(--bh-content-subtle)]",
95
+ purple: "text-[var(--bh-content-accent-purple-default)]",
96
+ sky: "text-[var(--bh-content-accent-sky-default)]",
97
+ }
98
+
3
99
  export function CommandBarDemo() {
4
100
  return (
5
101
  <div className="grid gap-6">
@@ -8,3 +104,143 @@ export function CommandBarDemo() {
8
104
  </div>
9
105
  )
10
106
  }
107
+
108
+ export function AddToolPickerExample() {
109
+ return (
110
+ <ModalSurface
111
+ aria-label="Add tool command example"
112
+ className={[
113
+ "[--bh-modal-width:calc(var(--bh-space-19xl-384)+var(--bh-space-18xl-320)+var(--bh-space-8xl-48))]",
114
+ "rounded-[var(--bh-radius-6xl-28)]",
115
+ ].join(" ")}
116
+ size="lg"
117
+ >
118
+ <ModalHeader className="items-start justify-between px-[var(--bh-space-5xl-24)] pb-[var(--bh-space-lg-10)] pe-[var(--bh-space-5xl-24)] pt-[var(--bh-space-5xl-24)]">
119
+ <h2 className="min-w-0 flex-1 text-start text-[length:var(--bh-text-heading-sm-semibold-font-size)] font-[var(--bh-text-heading-sm-semibold-font-weight)] leading-[var(--bh-text-heading-sm-semibold-line-height)] tracking-[var(--bh-text-heading-sm-semibold-letter-spacing)] text-[var(--bh-content-default)]">
120
+ Add tool
121
+ </h2>
122
+ <Button aria-label="Close add tool" size="icon" type="button" variant="ghost">
123
+ <XIcon aria-hidden="true" />
124
+ </Button>
125
+ </ModalHeader>
126
+
127
+ <ModalBody className="grid w-full gap-[var(--bh-space-4xl-20)] px-[var(--bh-space-5xl-24)] pb-[var(--bh-space-6xl-32)] pt-0">
128
+ <ToolbarSearch
129
+ aria-label="Search tools"
130
+ icon={<SearchIcon aria-hidden="true" />}
131
+ placeholder="Search..."
132
+ width="full"
133
+ />
134
+
135
+ <Toolbar aria-label="Tool source filters" className="overflow-x-auto" wrap>
136
+ <ToolbarSection wrap>
137
+ <ToolCategory icon={Settings2Icon}>All</ToolCategory>
138
+ <ToolCategory icon={LayoutGridIcon}>Apps</ToolCategory>
139
+ <ToolCategory active icon={LayoutGridIcon}>
140
+ MCP
141
+ </ToolCategory>
142
+ </ToolbarSection>
143
+ </Toolbar>
144
+
145
+ <ToolSection items={popularTools} title="Popular" />
146
+ <ToolSection items={serverTools} title="Servers" />
147
+ </ModalBody>
148
+ </ModalSurface>
149
+ )
150
+ }
151
+
152
+ function ToolCategory({
153
+ active = false,
154
+ children,
155
+ icon: Icon,
156
+ }: {
157
+ active?: boolean
158
+ children: React.ReactNode
159
+ icon: ToolIcon
160
+ }) {
161
+ return (
162
+ <ToolbarButton
163
+ aria-pressed={active}
164
+ className={[
165
+ "rounded-[var(--bh-radius-none)] border-b-[length:var(--bh-border-width-strong)] px-[var(--bh-space-xs-4)]",
166
+ active
167
+ ? "border-b-[var(--bh-border-accent-purple-strong)] text-[var(--bh-content-accent-purple-default)]"
168
+ : "border-b-transparent text-[var(--bh-content-subtle)]",
169
+ ].join(" ")}
170
+ size="sm"
171
+ type="button"
172
+ variant="ghost"
173
+ >
174
+ <Icon aria-hidden="true" data-icon="inline-start" />
175
+ {children}
176
+ </ToolbarButton>
177
+ )
178
+ }
179
+
180
+ function ToolSection({ items, title }: { items: ToolItem[]; title: string }) {
181
+ return (
182
+ <section className="grid gap-[var(--bh-space-xl-12)]" aria-label={title}>
183
+ <h3 className="text-start text-[length:var(--bh-text-body-md-medium-font-size)] font-[var(--bh-text-body-md-medium-font-weight)] leading-[var(--bh-text-body-md-medium-line-height)] tracking-[var(--bh-text-body-md-medium-letter-spacing)] text-[var(--bh-content-subtle)]">
184
+ {title}
185
+ </h3>
186
+ <div className="grid gap-[var(--bh-space-md-8)] sm:grid-cols-2">
187
+ {items.map((item) => (
188
+ <ToolRow item={item} key={item.title} />
189
+ ))}
190
+ </div>
191
+ </section>
192
+ )
193
+ }
194
+
195
+ function ToolRow({ item }: { item: ToolItem }) {
196
+ const Icon = item.icon
197
+ const tone = item.tone ?? "neutral"
198
+
199
+ return (
200
+ <Menu
201
+ aria-label={`${item.title} tool action`}
202
+ className="w-full bg-transparent p-0 shadow-none"
203
+ width="auto"
204
+ >
205
+ <MenuItem
206
+ aria-label={item.title}
207
+ className="min-h-[var(--bh-space-8xl-48)] px-[var(--bh-space-xs-4)] py-[var(--bh-space-xxs-2)]"
208
+ kind="multiline"
209
+ role="menuitem"
210
+ >
211
+ <Avatar shape="rounded" size="lg">
212
+ <AvatarIcon
213
+ className={[
214
+ "border border-[var(--bh-border-subtle)]",
215
+ "[&_svg]:size-[var(--bh-space-4xl-20)]",
216
+ toolToneClassNames[tone],
217
+ ].join(" ")}
218
+ size="lg"
219
+ >
220
+ <Icon aria-hidden="true" strokeWidth={2} />
221
+ </AvatarIcon>
222
+ {item.added ? <AvatarStatus size="small" status="available" /> : null}
223
+ </Avatar>
224
+ <MenuItemText>
225
+ <MenuItemTitle className="font-[var(--bh-text-body-md-medium-font-weight)]">
226
+ {item.title}
227
+ </MenuItemTitle>
228
+ {item.added ? (
229
+ <Badge
230
+ badgeStyle="light"
231
+ className="self-start"
232
+ color="green"
233
+ size="sm"
234
+ type="leading-icon"
235
+ >
236
+ <CheckIcon aria-hidden="true" />
237
+ Added
238
+ </Badge>
239
+ ) : item.description ? (
240
+ <MenuItemDescription>{item.description}</MenuItemDescription>
241
+ ) : null}
242
+ </MenuItemText>
243
+ </MenuItem>
244
+ </Menu>
245
+ )
246
+ }
@@ -0,0 +1,39 @@
1
+ import { InboxIcon, PlusIcon, SearchIcon } from "lucide-react"
2
+
3
+ import {
4
+ EmptyState,
5
+ type EmptyStateAction,
6
+ } from "@/components/ui/expanded/EmptyState"
7
+
8
+ const searchActions: EmptyStateAction[] = [
9
+ { label: "Reset filters", variant: "secondary" },
10
+ { label: "Create item" },
11
+ ]
12
+
13
+ const inboxActions: EmptyStateAction[] = [
14
+ {
15
+ icon: <PlusIcon data-icon="inline-start" />,
16
+ label: "Add record",
17
+ },
18
+ ]
19
+
20
+ export function EmptyStateDemo() {
21
+ return (
22
+ <div className="grid gap-6">
23
+ <EmptyState
24
+ actions={searchActions}
25
+ description="Try adjusting your search or filters"
26
+ icon={<SearchIcon />}
27
+ title="No results found"
28
+ />
29
+ <EmptyState
30
+ actions={inboxActions}
31
+ align="start"
32
+ description="Create a record to start tracking this workflow."
33
+ icon={<InboxIcon />}
34
+ size="compact"
35
+ title="Nothing here yet"
36
+ />
37
+ </div>
38
+ )
39
+ }
@@ -0,0 +1,60 @@
1
+ import {
2
+ FileUpload,
3
+ type FileUploadFile,
4
+ } from "@/components/ui/expanded/FileUpload"
5
+
6
+ const uploadFiles: FileUploadFile[] = [
7
+ {
8
+ id: "uploaded",
9
+ name: "file-name.pdf",
10
+ sizeLabel: "32mb",
11
+ status: "uploaded",
12
+ versionLabel: "v1.2.2",
13
+ },
14
+ {
15
+ id: "uploading",
16
+ name: "file-name.pdf",
17
+ sizeLabel: "32mb",
18
+ status: "uploading",
19
+ versionLabel: "v1.2.2",
20
+ },
21
+ {
22
+ id: "progress",
23
+ name: "file-name.pdf",
24
+ progress: 40,
25
+ sizeLabel: "32 KB",
26
+ status: "uploading-progress",
27
+ },
28
+ {
29
+ errorLabel: "Upload failed",
30
+ id: "error",
31
+ name: "file-name.pdf",
32
+ sizeLabel: "32mb",
33
+ status: "error",
34
+ versionLabel: "v1.2.2",
35
+ },
36
+ ]
37
+
38
+ export function FileUploadDemo() {
39
+ return (
40
+ <div className="grid items-start gap-5 xl:grid-cols-2">
41
+ <FileUpload
42
+ files={uploadFiles}
43
+ multiple
44
+ />
45
+ <FileUpload
46
+ files={uploadFiles}
47
+ size="sm"
48
+ />
49
+ <FileUpload
50
+ dropzoneState="hover"
51
+ files={uploadFiles.slice(0, 1)}
52
+ variant="button"
53
+ />
54
+ <FileUpload
55
+ disabled
56
+ files={[]}
57
+ />
58
+ </div>
59
+ )
60
+ }
@@ -1,6 +1,17 @@
1
+ import { Button } from "@/components/ui/button"
1
2
  import { Steps, type StepItem } from "@/components/ui/expanded/Steps"
2
3
 
3
4
  const stepItems: StepItem[] = Array.from({ length: 6 }, (_, index) => ({
5
+ actions: (
6
+ <>
7
+ <Button size="sm" type="button" variant="default">
8
+ Button
9
+ </Button>
10
+ <Button size="sm" type="button" variant="secondary">
11
+ Button
12
+ </Button>
13
+ </>
14
+ ),
4
15
  caption: "Caption",
5
16
  label: "Label",
6
17
  number: index + 1,