banhaten 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -8
- package/package.json +8 -2
- package/registry/components/autocomplete.tsx +637 -0
- package/registry/components/avatar.tsx +258 -22
- package/registry/components/badge.tsx +97 -35
- package/registry/components/date-picker-state.ts +253 -0
- package/registry/components/date-picker.tsx +115 -158
- package/registry/components/expanded/EmptyState.tsx +155 -0
- package/registry/components/expanded/emptyState.css +111 -0
- package/registry/components/expanded/slideout.css +1 -0
- package/registry/components/expanded/table.css +1 -0
- package/registry/components/input-otp.tsx +574 -0
- package/registry/components/input.tsx +21 -11
- package/registry/components/menu.tsx +371 -8
- package/registry/components/popover.tsx +840 -0
- package/registry/components/select.tsx +4 -0
- package/registry/components/skeleton.css +57 -0
- package/registry/components/skeleton.tsx +482 -0
- package/registry/components/spinner.tsx +79 -11
- package/registry/components/textarea.tsx +1 -1
- package/registry/components/tooltip.tsx +4 -0
- package/registry/examples/autocomplete-demo.tsx +109 -0
- package/registry/examples/avatar-demo.tsx +102 -47
- package/registry/examples/badge-demo.tsx +16 -0
- package/registry/examples/expanded/command-bar-demo.tsx +236 -0
- package/registry/examples/expanded/empty-state-demo.tsx +39 -0
- package/registry/examples/input-demo.tsx +1 -1
- package/registry/examples/input-otp-demo.tsx +72 -0
- package/registry/examples/menu-demo.tsx +101 -88
- package/registry/examples/popover-demo.tsx +546 -0
- package/registry/examples/select-demo.tsx +1 -1
- package/registry/examples/skeleton-demo.tsx +56 -0
- package/registry/examples/spinner-demo.tsx +23 -1
- package/registry/examples/textarea-demo.tsx +1 -1
- package/registry/index.json +240 -8
- package/registry/styles/globals.css +88 -0
- package/src/cli/index.js +997 -62
|
@@ -24,6 +24,8 @@ const defaultTooltipSupportText =
|
|
|
24
24
|
// Radix sideOffset requires numbers; these mirror the tooltip spacing aliases.
|
|
25
25
|
const TOOLTIP_POINTER_SIDE_OFFSET_PX = 7
|
|
26
26
|
const TOOLTIP_POINTERLESS_SIDE_OFFSET_PX = 6
|
|
27
|
+
// Radix collisionPadding requires a number; this mirrors --bh-space-md-8.
|
|
28
|
+
const TOOLTIP_COLLISION_PADDING_PX = 8
|
|
27
29
|
|
|
28
30
|
const tooltipContentVariants = cva(
|
|
29
31
|
[
|
|
@@ -87,6 +89,7 @@ const TooltipContent = React.forwardRef<
|
|
|
87
89
|
children,
|
|
88
90
|
className,
|
|
89
91
|
closeLabel,
|
|
92
|
+
collisionPadding = TOOLTIP_COLLISION_PADDING_PX,
|
|
90
93
|
onCloseClick,
|
|
91
94
|
pointerPosition = "top-left",
|
|
92
95
|
shortcut,
|
|
@@ -127,6 +130,7 @@ const TooltipContent = React.forwardRef<
|
|
|
127
130
|
ref={ref}
|
|
128
131
|
side={side ?? placement.side}
|
|
129
132
|
align={align ?? placement.align}
|
|
133
|
+
collisionPadding={collisionPadding}
|
|
130
134
|
sideOffset={
|
|
131
135
|
sideOffset ??
|
|
132
136
|
(shouldShowPointer
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { Autocomplete } from "@/components/ui/autocomplete"
|
|
2
|
+
|
|
3
|
+
const peopleOptions = [
|
|
4
|
+
{
|
|
5
|
+
value: "ahmed",
|
|
6
|
+
label: "Ahmed Galal",
|
|
7
|
+
addonText: "Design system",
|
|
8
|
+
itemType: "avatar" as const,
|
|
9
|
+
keywords: ["foundations", "tokens"],
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
value: "nora",
|
|
13
|
+
label: "Nora Ali",
|
|
14
|
+
addonText: "Product",
|
|
15
|
+
itemType: "avatar" as const,
|
|
16
|
+
keywords: ["roadmap", "research"],
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
value: "layla",
|
|
20
|
+
label: "Layla Hassan",
|
|
21
|
+
addonText: "Engineering",
|
|
22
|
+
itemType: "avatar" as const,
|
|
23
|
+
keywords: ["frontend", "platform"],
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
value: "omar",
|
|
27
|
+
label: "Omar Saleh",
|
|
28
|
+
addonText: "Unavailable",
|
|
29
|
+
itemType: "avatar" as const,
|
|
30
|
+
disabled: true,
|
|
31
|
+
},
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
const projectOptions = [
|
|
35
|
+
{ value: "tokens", label: "Token audit", addonText: "In review", itemType: "dot" as const },
|
|
36
|
+
{ value: "inputs", label: "Input system", addonText: "Ready", itemType: "dot" as const },
|
|
37
|
+
{ value: "tables", label: "Table promotion", addonText: "Planned", itemType: "dot" as const },
|
|
38
|
+
{ value: "forms", label: "Form patterns", addonText: "Discovery", itemType: "dot" as const },
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
const rtlOptions = [
|
|
42
|
+
{
|
|
43
|
+
value: "ahmed",
|
|
44
|
+
label: "أحمد جلال",
|
|
45
|
+
addonText: "النظام",
|
|
46
|
+
itemType: "avatar" as const,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
value: "nora",
|
|
50
|
+
label: "نورا علي",
|
|
51
|
+
addonText: "المنتج",
|
|
52
|
+
itemType: "avatar" as const,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
value: "layla",
|
|
56
|
+
label: "ليلى حسن",
|
|
57
|
+
addonText: "الهندسة",
|
|
58
|
+
itemType: "avatar" as const,
|
|
59
|
+
},
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
export function AutocompleteDemo() {
|
|
63
|
+
return (
|
|
64
|
+
<div className="grid w-full gap-5 lg:grid-cols-2">
|
|
65
|
+
<div className="grid min-w-0 content-start gap-5">
|
|
66
|
+
<Autocomplete
|
|
67
|
+
className="mb-[var(--bh-space-13xl-160)]"
|
|
68
|
+
defaultOpen
|
|
69
|
+
defaultValue="ahmed"
|
|
70
|
+
helperText="Search by name, team, or keyword."
|
|
71
|
+
label="Owner"
|
|
72
|
+
options={peopleOptions}
|
|
73
|
+
placeholder="Search people"
|
|
74
|
+
/>
|
|
75
|
+
|
|
76
|
+
<Autocomplete
|
|
77
|
+
defaultValue={["tokens", "inputs"]}
|
|
78
|
+
helperText="Selected values render with the Tag component."
|
|
79
|
+
label="Related work"
|
|
80
|
+
options={projectOptions}
|
|
81
|
+
placeholder="Add projects"
|
|
82
|
+
selectionMode="multiple"
|
|
83
|
+
/>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<div className="grid min-w-0 content-start gap-5">
|
|
87
|
+
<Autocomplete
|
|
88
|
+
label="Project"
|
|
89
|
+
options={projectOptions}
|
|
90
|
+
placeholder="Search projects"
|
|
91
|
+
variant="soft"
|
|
92
|
+
/>
|
|
93
|
+
|
|
94
|
+
<div dir="rtl" className="min-w-0">
|
|
95
|
+
<Autocomplete
|
|
96
|
+
className="pb-[var(--bh-space-13xl-160)] lg:pb-0"
|
|
97
|
+
defaultOpen
|
|
98
|
+
defaultValue="nora"
|
|
99
|
+
dir="rtl"
|
|
100
|
+
helperText="ابحث بالاسم أو الفريق."
|
|
101
|
+
label="المالك"
|
|
102
|
+
options={rtlOptions}
|
|
103
|
+
placeholder="ابحث عن شخص"
|
|
104
|
+
/>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
)
|
|
109
|
+
}
|
|
@@ -1,73 +1,128 @@
|
|
|
1
|
-
import { PlusIcon } from "lucide-react"
|
|
2
|
-
|
|
3
1
|
import {
|
|
4
2
|
Avatar,
|
|
5
|
-
|
|
3
|
+
AvatarAction,
|
|
6
4
|
AvatarFallback,
|
|
7
5
|
AvatarGroup,
|
|
8
6
|
AvatarGroupCount,
|
|
7
|
+
AvatarIcon,
|
|
9
8
|
AvatarImage,
|
|
9
|
+
AvatarStatus,
|
|
10
10
|
} from "@/components/ui/avatar"
|
|
11
|
+
import { PlusIcon } from "lucide-react"
|
|
11
12
|
|
|
12
13
|
const avatarUrl = new URL("../assets/avatars/avatar-01.jpg", import.meta.url).href
|
|
13
14
|
|
|
15
|
+
const statusColumns = [
|
|
16
|
+
"available",
|
|
17
|
+
"available",
|
|
18
|
+
"busy",
|
|
19
|
+
"away",
|
|
20
|
+
"blocked",
|
|
21
|
+
] as const
|
|
22
|
+
|
|
23
|
+
const secondaryStatusColumns = [
|
|
24
|
+
"offline",
|
|
25
|
+
"available",
|
|
26
|
+
"busy",
|
|
27
|
+
"away",
|
|
28
|
+
"blocked",
|
|
29
|
+
] as const
|
|
30
|
+
|
|
14
31
|
export function AvatarDemo() {
|
|
15
32
|
return (
|
|
16
|
-
<div className="flex flex-col gap-
|
|
17
|
-
<div className="
|
|
18
|
-
|
|
33
|
+
<div className="flex flex-col gap-7">
|
|
34
|
+
<div className="grid w-fit grid-cols-5 gap-x-4 gap-y-4">
|
|
35
|
+
{statusColumns.map((status, index) => (
|
|
36
|
+
<Avatar key={`icon-${status}-${index}`} size="2xl">
|
|
37
|
+
<AvatarIcon />
|
|
38
|
+
{index === 0 ? <AvatarAction type="edit" size="medium" /> : null}
|
|
39
|
+
<AvatarStatus status={status} size="small" />
|
|
40
|
+
</Avatar>
|
|
41
|
+
))}
|
|
42
|
+
|
|
43
|
+
{secondaryStatusColumns.map((status) => (
|
|
44
|
+
<Avatar key={`initials-${status}`} size="2xl">
|
|
45
|
+
<AvatarFallback>AG</AvatarFallback>
|
|
46
|
+
<AvatarStatus status={status} size="small" />
|
|
47
|
+
</Avatar>
|
|
48
|
+
))}
|
|
49
|
+
|
|
50
|
+
{secondaryStatusColumns.map((status) => (
|
|
51
|
+
<Avatar key={`image-${status}`} size="2xl">
|
|
52
|
+
<AvatarImage src={avatarUrl} alt="Profile portrait" />
|
|
53
|
+
<AvatarFallback>AG</AvatarFallback>
|
|
54
|
+
<AvatarStatus status={status} size="small" />
|
|
55
|
+
</Avatar>
|
|
56
|
+
))}
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<div className="flex flex-wrap items-center gap-5 ps-7">
|
|
60
|
+
<Avatar size="2xl">
|
|
19
61
|
<AvatarImage src={avatarUrl} alt="Profile portrait" />
|
|
20
|
-
<AvatarFallback>
|
|
62
|
+
<AvatarFallback>AG</AvatarFallback>
|
|
63
|
+
<AvatarAction type="edit" size="medium" />
|
|
21
64
|
</Avatar>
|
|
22
|
-
<Avatar>
|
|
23
|
-
<
|
|
65
|
+
<Avatar size="2xl">
|
|
66
|
+
<AvatarImage src={avatarUrl} alt="Profile portrait" />
|
|
67
|
+
<AvatarFallback>AG</AvatarFallback>
|
|
68
|
+
<AvatarAction type="remove" size="medium" />
|
|
24
69
|
</Avatar>
|
|
25
|
-
<Avatar>
|
|
26
|
-
<AvatarImage src=
|
|
27
|
-
<AvatarFallback>
|
|
70
|
+
<Avatar size="2xl">
|
|
71
|
+
<AvatarImage src={avatarUrl} alt="Profile portrait" />
|
|
72
|
+
<AvatarFallback>AG</AvatarFallback>
|
|
73
|
+
<AvatarAction type="verified" size="medium" />
|
|
28
74
|
</Avatar>
|
|
29
75
|
</div>
|
|
30
76
|
|
|
31
|
-
<div className="flex
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
|
|
77
|
+
<div className="flex items-center gap-3">
|
|
78
|
+
<AvatarGroup>
|
|
79
|
+
{Array.from({ length: 7 }).map((_, index) => (
|
|
80
|
+
<Avatar key={index} size="md">
|
|
81
|
+
<AvatarImage src={avatarUrl} alt="" />
|
|
82
|
+
<AvatarFallback>AG</AvatarFallback>
|
|
83
|
+
</Avatar>
|
|
84
|
+
))}
|
|
85
|
+
<AvatarGroupCount size="md">+10</AvatarGroupCount>
|
|
86
|
+
</AvatarGroup>
|
|
87
|
+
<Avatar
|
|
88
|
+
aria-label="Add member"
|
|
89
|
+
className="border border-dashed border-[var(--bh-border-subtle)] bg-[var(--bh-bg-default)] text-[var(--bh-content-subtle)]"
|
|
90
|
+
size="md"
|
|
91
|
+
>
|
|
92
|
+
<PlusIcon aria-hidden="true" className="size-4" />
|
|
42
93
|
</Avatar>
|
|
43
94
|
</div>
|
|
44
95
|
|
|
45
|
-
<
|
|
46
|
-
<Avatar>
|
|
47
|
-
<
|
|
96
|
+
<div className="flex items-center gap-3">
|
|
97
|
+
<Avatar size="xl">
|
|
98
|
+
<AvatarImage src={avatarUrl} alt="John Doe" />
|
|
99
|
+
<AvatarFallback>JD</AvatarFallback>
|
|
100
|
+
<AvatarStatus status="available" size="small" />
|
|
48
101
|
</Avatar>
|
|
49
|
-
<
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
102
|
+
<div className="min-w-0">
|
|
103
|
+
<p className="text-sm font-semibold text-[var(--bh-content-default)]">
|
|
104
|
+
John Doe
|
|
105
|
+
</p>
|
|
106
|
+
<p className="text-sm text-[var(--bh-content-subtle)]">
|
|
107
|
+
johndoe@example.com
|
|
108
|
+
</p>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
57
111
|
|
|
58
|
-
<div
|
|
59
|
-
<
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
112
|
+
<div className="flex items-center gap-3">
|
|
113
|
+
<Avatar size="md">
|
|
114
|
+
<AvatarImage src={avatarUrl} alt="John Doe" />
|
|
115
|
+
<AvatarFallback>JD</AvatarFallback>
|
|
116
|
+
<AvatarStatus status="available" size="tiny" />
|
|
117
|
+
</Avatar>
|
|
118
|
+
<div className="min-w-0">
|
|
119
|
+
<p className="text-sm font-semibold text-[var(--bh-content-default)]">
|
|
120
|
+
John Doe
|
|
121
|
+
</p>
|
|
122
|
+
<p className="text-xs text-[var(--bh-content-subtle)]">
|
|
123
|
+
johndoe@example.com
|
|
124
|
+
</p>
|
|
125
|
+
</div>
|
|
71
126
|
</div>
|
|
72
127
|
</div>
|
|
73
128
|
)
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
ArrowUpRightIcon,
|
|
3
3
|
BadgeCheckIcon,
|
|
4
4
|
BookmarkIcon,
|
|
5
|
+
ChevronRightIcon,
|
|
5
6
|
} from "lucide-react"
|
|
6
7
|
|
|
7
8
|
import { Badge } from "@/components/ui/badge"
|
|
@@ -48,6 +49,21 @@ export function BadgeDemo() {
|
|
|
48
49
|
<ArrowUpRightIcon data-icon="inline-end" data-rtl-flip="true" />
|
|
49
50
|
</a>
|
|
50
51
|
</Badge>
|
|
52
|
+
<Badge
|
|
53
|
+
asChild
|
|
54
|
+
color="blue"
|
|
55
|
+
splitAction
|
|
56
|
+
type="trailing-icon"
|
|
57
|
+
>
|
|
58
|
+
<a href="#badge">
|
|
59
|
+
In progress
|
|
60
|
+
<ChevronRightIcon
|
|
61
|
+
aria-hidden="true"
|
|
62
|
+
data-icon="inline-end"
|
|
63
|
+
data-rtl-flip="true"
|
|
64
|
+
/>
|
|
65
|
+
</a>
|
|
66
|
+
</Badge>
|
|
51
67
|
</div>
|
|
52
68
|
)
|
|
53
69
|
}
|
|
@@ -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
|
+
}
|
|
@@ -26,7 +26,7 @@ const demoRtlInputProps = {
|
|
|
26
26
|
...demoInputProps,
|
|
27
27
|
errorMessage: "\u0631\u0633\u0627\u0644\u0629 \u062e\u0637\u0623",
|
|
28
28
|
label: "\u0645\u0644\u0635\u0642",
|
|
29
|
-
message: "\
|
|
29
|
+
message: "\u0646\u0635 \u062a\u0648\u0636\u064a\u062d\u064a",
|
|
30
30
|
optionalText: "(\u062e\u064a\u0627\u0631\u064a)",
|
|
31
31
|
placeholder: "\u0627\u0644\u0646\u0635",
|
|
32
32
|
valueText: "\u0646\u0635 \u0647\u0646\u0627",
|