banhaten 0.1.0 → 0.1.1
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 +1 -1
- package/package.json +1 -1
- package/registry/components/accordion.tsx +37 -1
- package/registry/components/alert.tsx +14 -28
- package/registry/components/attribute.tsx +6 -10
- package/registry/components/avatar.tsx +1 -2
- package/registry/components/button-group.tsx +1 -1
- package/registry/components/card.tsx +1 -1
- package/registry/components/checkbox.tsx +19 -16
- package/registry/components/expanded/ActivityFeed.tsx +37 -23
- package/registry/components/expanded/Banner.tsx +54 -19
- package/registry/components/expanded/Breadcrumbs.tsx +10 -38
- package/registry/components/expanded/CatalogComponentsShowcase.tsx +11 -16
- package/registry/components/expanded/CatalogTag.tsx +4 -11
- package/registry/components/expanded/CommandBar.tsx +33 -53
- package/registry/components/expanded/FileUpload.tsx +362 -59
- package/registry/components/expanded/OnboardingStepListItem.tsx +6 -10
- package/registry/components/expanded/PageHeader.tsx +2 -11
- package/registry/components/expanded/Slideout.tsx +12 -23
- package/registry/components/expanded/Steps.tsx +6 -8
- package/registry/components/expanded/Table.tsx +18 -40
- package/registry/components/expanded/Timeline.tsx +5 -24
- package/registry/components/expanded/activityFeed.css +10 -54
- package/registry/components/expanded/banner.css +8 -75
- package/registry/components/expanded/breadcrumbs.css +1 -1
- package/registry/components/expanded/commandBar.css +23 -26
- package/registry/components/expanded/divider.css +1 -1
- package/registry/components/expanded/fileUpload.css +304 -75
- package/registry/components/expanded/pageHeader.css +1 -1
- package/registry/components/expanded/steps.css +15 -51
- package/registry/components/expanded/table.css +5 -1
- package/registry/components/expanded/timeline.css +18 -15
- package/registry/components/input.tsx +125 -54
- package/registry/components/menu.tsx +99 -72
- package/registry/components/pagination.tsx +6 -18
- package/registry/components/radio-card.tsx +25 -31
- package/registry/components/select-content.tsx +28 -123
- package/registry/components/select.tsx +9 -9
- package/registry/components/social-button.tsx +24 -90
- package/registry/components/spinner.tsx +21 -5
- package/registry/components/textarea.tsx +20 -35
- package/registry/components/toggle.tsx +7 -23
- package/registry/components/tooltip.tsx +4 -4
- package/registry/examples/attribute-demo.tsx +2 -2
- package/registry/examples/checkbox-demo.tsx +3 -8
- package/registry/examples/date-picker-demo.tsx +75 -22
- package/registry/examples/expanded/banner-demo.tsx +31 -6
- package/registry/examples/expanded/breadcrumbs-demo.tsx +59 -0
- package/registry/examples/expanded/file-upload-demo.tsx +60 -0
- package/registry/examples/expanded/steps-demo.tsx +11 -0
- package/registry/examples/expanded/table-demo.tsx +142 -0
- package/registry/examples/progress-demo.tsx +2 -2
- package/registry/examples/select-demo.tsx +32 -18
- package/registry/examples/social-button-demo.tsx +33 -33
- package/registry/examples/spinner-demo.tsx +37 -0
- package/registry/examples/tag-demo.tsx +1 -1
- package/registry/index.json +26 -12
- package/registry/styles/globals.css +5 -3
package/README.md
CHANGED
|
@@ -53,7 +53,7 @@ The current scaffold includes:
|
|
|
53
53
|
- A Figma-matched `tag` registry item with simple, dot, flag, avatar, and icon types, default/active/disabled states, native hover/focus-visible styling, xs/md/lg sizes, optional close button, and RTL-safe composition.
|
|
54
54
|
- A Radix Switch-backed `toggle` registry item with sm/md geometry, active/disabled states, optional handle icons, labeled fields, supporting text, and RTL-safe label placement.
|
|
55
55
|
- A Figma-matched `textarea` registry item with label, helper text, error, default and tags modes, placeholder, filled, disabled, and RTL states.
|
|
56
|
-
- A Radix Select-backed, Figma-matched `select` registry item with default and soft triggers, error/filled/disabled states, menu surface, checkbox and check selection, and item media for text,
|
|
56
|
+
- A Radix Select-backed, Figma-matched `select` registry item with default and soft triggers, error/filled/disabled states, menu surface, checkbox and check selection, and item media for text, icons, users, companies, payment methods, and status dots.
|
|
57
57
|
- A Figma-matched `date-picker` registry item with Calendar, Range Calendar, Date Picker, Date Range Picker, presets, time rows, open states, month/year view, and RTL.
|
|
58
58
|
- A Radix Slider-backed `slider` registry item with single, range, multi-thumb, text/tooltip indicators, ticks, vertical layout, pill treatment, tones, and RTL behavior.
|
|
59
59
|
- A read-only `progress` registry item with value meters, label placements, helper text, indicators, spinner, and RTL fill direction.
|
package/package.json
CHANGED
|
@@ -67,11 +67,15 @@ function AccordionItem({
|
|
|
67
67
|
function AccordionTrigger({
|
|
68
68
|
className,
|
|
69
69
|
children,
|
|
70
|
+
dir,
|
|
70
71
|
...props
|
|
71
72
|
}: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
|
|
73
|
+
const triggerDirection = dir ?? getTextDirection(children)
|
|
74
|
+
|
|
72
75
|
return (
|
|
73
76
|
<AccordionPrimitive.Header className="flex">
|
|
74
77
|
<AccordionPrimitive.Trigger
|
|
78
|
+
dir={triggerDirection}
|
|
75
79
|
data-slot="accordion-trigger"
|
|
76
80
|
className={cn(
|
|
77
81
|
"flex flex-1 items-start justify-between gap-[var(--bh-space-3xl-16)] rounded-[var(--bh-radius-md-6)] py-[var(--bh-space-3xl-16)] text-start text-[length:var(--bh-text-body-sm-medium-font-size)] font-[var(--bh-text-body-sm-medium-font-weight)] leading-[var(--bh-text-body-sm-medium-line-height)] tracking-[var(--bh-text-body-sm-medium-letter-spacing)] text-[var(--bh-content-default)] outline-none transition-[background-color,color,box-shadow] hover:text-[var(--bh-content-default)] focus-visible:shadow-[var(--shadow-button-focus)] disabled:pointer-events-none disabled:opacity-[var(--bh-opacity-50)] data-[disabled]:pointer-events-none data-[disabled]:opacity-[var(--bh-opacity-50)] [&[data-state=open]>svg]:rotate-180 [&_svg]:size-[var(--bh-space-3xl-16)]",
|
|
@@ -79,7 +83,7 @@ function AccordionTrigger({
|
|
|
79
83
|
)}
|
|
80
84
|
{...props}
|
|
81
85
|
>
|
|
82
|
-
<span dir="auto" className="min-w-0">
|
|
86
|
+
<span dir="auto" className="min-w-0 flex-1 text-start">
|
|
83
87
|
{children}
|
|
84
88
|
</span>
|
|
85
89
|
<ChevronDownIcon
|
|
@@ -110,6 +114,38 @@ function AccordionContent({
|
|
|
110
114
|
)
|
|
111
115
|
}
|
|
112
116
|
|
|
117
|
+
function getTextDirection(children: React.ReactNode) {
|
|
118
|
+
const text = getNodeText(children)
|
|
119
|
+
|
|
120
|
+
for (const character of text) {
|
|
121
|
+
if (/[\u0590-\u08ff]/.test(character)) {
|
|
122
|
+
return "rtl"
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (/[A-Za-z]/.test(character)) {
|
|
126
|
+
return "ltr"
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return undefined
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function getNodeText(node: React.ReactNode): string {
|
|
134
|
+
if (typeof node === "string" || typeof node === "number") {
|
|
135
|
+
return String(node)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (Array.isArray(node)) {
|
|
139
|
+
return node.map(getNodeText).join("")
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (React.isValidElement<{ children?: React.ReactNode }>(node)) {
|
|
143
|
+
return getNodeText(node.props.children)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return ""
|
|
147
|
+
}
|
|
148
|
+
|
|
113
149
|
export {
|
|
114
150
|
Accordion,
|
|
115
151
|
AccordionItem,
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import * as React from "react"
|
|
2
2
|
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import {
|
|
4
|
+
CircleAlertIcon,
|
|
5
|
+
CircleCheckIcon,
|
|
6
|
+
InfoIcon,
|
|
7
|
+
StarIcon,
|
|
8
|
+
TriangleAlertIcon,
|
|
9
|
+
XIcon,
|
|
10
|
+
} from "lucide-react"
|
|
3
11
|
|
|
4
12
|
import { cn } from "@/lib/utils"
|
|
5
13
|
|
|
@@ -226,49 +234,27 @@ function AlertActions({ actions }: { actions: AlertAction[] }) {
|
|
|
226
234
|
|
|
227
235
|
function AlertStatusIcon({ status }: { status: AlertStatus }) {
|
|
228
236
|
if (status === "success") {
|
|
229
|
-
return (
|
|
230
|
-
<svg aria-hidden="true" className="size-[var(--bh-alert-icon-size)] fill-current" viewBox="0 0 16.6667 16.6667">
|
|
231
|
-
<path d="M8.33333 16.6667C12.9357 16.6667 16.6667 12.9357 16.6667 8.33333C16.6667 3.73096 12.9357 0 8.33333 0C3.73096 0 0 3.73096 0 8.33333C0 12.9357 3.73096 16.6667 8.33333 16.6667ZM12.8809 6.21426L7.5 11.5952L3.99407 8.08925L5.17259 6.91075L7.5 9.23817L11.7024 5.03574L12.8809 6.21426Z" />
|
|
232
|
-
</svg>
|
|
233
|
-
)
|
|
237
|
+
return <CircleCheckIcon aria-hidden="true" className="size-[var(--bh-alert-icon-size)]" strokeWidth={2.25} />
|
|
234
238
|
}
|
|
235
239
|
|
|
236
240
|
if (status === "warning") {
|
|
237
|
-
return (
|
|
238
|
-
<svg aria-hidden="true" className="size-[var(--bh-alert-icon-size)] fill-current" viewBox="0 0 17.544 15.4168">
|
|
239
|
-
<path d="M9.49356 0.416809L17.4321 14.1668C17.6623 14.5654 17.5257 15.0751 17.1271 15.3052C17.0005 15.3783 16.8567 15.4168 16.7105 15.4168H0.833333C0.3731 15.4168 0 15.0438 0 14.5835C0 14.4372 0.0385084 14.2935 0.11165 14.1668L8.05022 0.416809C8.28031 0.0182258 8.78997 -0.118333 9.18856 0.111784C9.31522 0.184926 9.42047 0.290126 9.49356 0.416809ZM7.93856 11.2502V12.9168H9.60522V11.2502H7.93856ZM7.93856 5.41681V9.5835H9.60522V5.41681H7.93856Z" />
|
|
240
|
-
</svg>
|
|
241
|
-
)
|
|
241
|
+
return <TriangleAlertIcon aria-hidden="true" className="size-[var(--bh-alert-icon-size)]" strokeWidth={2.25} />
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
if (status === "info") {
|
|
245
|
-
return (
|
|
246
|
-
<svg aria-hidden="true" className="size-[var(--bh-alert-icon-size)] fill-current" viewBox="0 0 16.6667 16.6667">
|
|
247
|
-
<path d="M8.33333 16.6667C3.73096 16.6667 0 12.9357 0 8.33333C0 3.73096 3.73096 0 8.33333 0C12.9357 0 16.6667 3.73096 16.6667 8.33333C16.6667 12.9357 12.9357 16.6667 8.33333 16.6667ZM8.33333 6.25C9.02367 6.25 9.58333 5.69036 9.58333 5C9.58333 4.30964 9.02367 3.75 8.33333 3.75C7.643 3.75 7.08333 4.30964 7.08333 5C7.08333 5.69036 7.643 6.25 8.33333 6.25ZM10 10.8333H9.16667V7.08333H6.66667V8.75H7.5V10.8333H6.66667V12.5H10V10.8333Z" />
|
|
248
|
-
</svg>
|
|
249
|
-
)
|
|
245
|
+
return <InfoIcon aria-hidden="true" className="size-[var(--bh-alert-icon-size)]" strokeWidth={2.25} />
|
|
250
246
|
}
|
|
251
247
|
|
|
252
248
|
if (status === "neutral") {
|
|
253
|
-
return (
|
|
254
|
-
<svg aria-hidden="true" className="size-[var(--bh-alert-icon-size)] fill-current" viewBox="0 0 15.851 15.0752">
|
|
255
|
-
<path d="M7.92545 12.0833L3.02726 15.0752L4.35901 9.49217L0 5.75819L5.72128 5.29952L7.92545 0L10.1297 5.29952L15.851 5.75819L11.492 9.49217L12.8237 15.0752L7.92545 12.0833Z" />
|
|
256
|
-
</svg>
|
|
257
|
-
)
|
|
249
|
+
return <StarIcon aria-hidden="true" className="size-[var(--bh-alert-icon-size)]" strokeWidth={2.25} />
|
|
258
250
|
}
|
|
259
251
|
|
|
260
|
-
return (
|
|
261
|
-
<svg aria-hidden="true" className="size-[var(--bh-alert-icon-size)] fill-current" viewBox="0 0 16.6667 16.6667">
|
|
262
|
-
<path d="M8.33333 16.6667C3.73096 16.6667 0 12.9357 0 8.33333C0 3.73096 3.73096 0 8.33333 0C12.9357 0 16.6667 3.73096 16.6667 8.33333C16.6667 12.9357 12.9357 16.6667 8.33333 16.6667ZM7.5 10.8333V12.5H9.16667V10.8333H7.5ZM7.5 4.16667V9.16667H9.16667V4.16667H7.5Z" />
|
|
263
|
-
</svg>
|
|
264
|
-
)
|
|
252
|
+
return <CircleAlertIcon aria-hidden="true" className="size-[var(--bh-alert-icon-size)]" strokeWidth={2.25} />
|
|
265
253
|
}
|
|
266
254
|
|
|
267
255
|
function CloseIcon() {
|
|
268
256
|
return (
|
|
269
|
-
<
|
|
270
|
-
<path d="M8.00004 7.05723L11.2999 3.75739L12.2427 4.7002L8.94284 8.00003L12.2427 11.2998L11.2999 12.2427L8.00004 8.94283L4.70021 12.2427L3.75739 11.2998L7.05723 8.00003L3.75739 4.7002L4.70021 3.75739L8.00004 7.05723Z" />
|
|
271
|
-
</svg>
|
|
257
|
+
<XIcon aria-hidden="true" className="size-[var(--bh-alert-icon-size)]" strokeWidth={2.25} />
|
|
272
258
|
)
|
|
273
259
|
}
|
|
274
260
|
|
|
@@ -3,9 +3,9 @@ import { cva, type VariantProps } from "class-variance-authority"
|
|
|
3
3
|
|
|
4
4
|
import { cn } from "@/lib/utils"
|
|
5
5
|
|
|
6
|
-
type AttributeLayout = "inline" | "
|
|
6
|
+
type AttributeLayout = "inline" | "stacked" | "two-column"
|
|
7
7
|
type AttributeDensity = "compact" | "default" | "comfortable"
|
|
8
|
-
type AttributeAlign = "start" | "
|
|
8
|
+
type AttributeAlign = "start" | "stretch"
|
|
9
9
|
type AttributeValueDir = "auto" | "ltr" | "rtl"
|
|
10
10
|
|
|
11
11
|
type AttributeContextValue = {
|
|
@@ -70,13 +70,13 @@ const attributeListVariants = cva("grid min-w-0", {
|
|
|
70
70
|
variants: {
|
|
71
71
|
layout: {
|
|
72
72
|
inline: "grid-cols-1",
|
|
73
|
-
end: "grid-cols-1",
|
|
74
73
|
stacked: "grid-cols-1",
|
|
75
74
|
"two-column":
|
|
76
75
|
"grid-cols-1 gap-x-[var(--bh-space-6xl-32)] sm:grid-cols-2",
|
|
77
76
|
},
|
|
78
77
|
bordered: {
|
|
79
|
-
true:
|
|
78
|
+
true:
|
|
79
|
+
"overflow-hidden rounded-[var(--bh-radius-lg-8)] border border-[var(--bh-border-subtle)] [&>[data-slot='attribute-item']]:px-[var(--bh-space-3xl-16)] sm:[&>[data-slot='attribute-item']]:px-[var(--bh-space-4xl-20)]",
|
|
80
80
|
false: "",
|
|
81
81
|
},
|
|
82
82
|
},
|
|
@@ -91,8 +91,6 @@ const attributeItemVariants = cva("min-w-0", {
|
|
|
91
91
|
layout: {
|
|
92
92
|
inline:
|
|
93
93
|
"grid grid-cols-1 items-center gap-x-[var(--bh-space-xs-4)] sm:grid-cols-[var(--bh-attribute-label-width)_minmax(0,1fr)]",
|
|
94
|
-
end:
|
|
95
|
-
"grid grid-cols-1 items-center gap-x-[var(--bh-space-xs-4)] sm:grid-cols-[var(--bh-attribute-label-width)_minmax(0,1fr)]",
|
|
96
94
|
stacked:
|
|
97
95
|
"flex flex-col items-start justify-center gap-[var(--bh-space-xs-4)]",
|
|
98
96
|
"two-column":
|
|
@@ -100,7 +98,7 @@ const attributeItemVariants = cva("min-w-0", {
|
|
|
100
98
|
},
|
|
101
99
|
density: {
|
|
102
100
|
compact: "py-[var(--bh-space-md-8)]",
|
|
103
|
-
default: "py-[var(--bh-space-
|
|
101
|
+
default: "py-[var(--bh-space-3xl-16)]",
|
|
104
102
|
comfortable: "py-[var(--bh-space-3xl-16)]",
|
|
105
103
|
},
|
|
106
104
|
divided: {
|
|
@@ -154,7 +152,6 @@ const attributeValueVariants = cva(
|
|
|
154
152
|
variants: {
|
|
155
153
|
align: {
|
|
156
154
|
start: "justify-self-start text-start",
|
|
157
|
-
end: "justify-self-end justify-end text-end rtl:justify-self-start rtl:justify-start rtl:text-right",
|
|
158
155
|
stretch: "w-full justify-self-stretch text-start",
|
|
159
156
|
},
|
|
160
157
|
truncate: {
|
|
@@ -323,8 +320,7 @@ const AttributeItem = React.forwardRef<HTMLDivElement, AttributeItemProps>(
|
|
|
323
320
|
const selectedLayout = layout || context.layout
|
|
324
321
|
const selectedDensity = density || context.density
|
|
325
322
|
const selectedDivided = divided ?? context.dividers
|
|
326
|
-
const selectedAlign =
|
|
327
|
-
valueAlign || (selectedLayout === "end" ? "end" : "start")
|
|
323
|
+
const selectedAlign = valueAlign || "start"
|
|
328
324
|
|
|
329
325
|
return (
|
|
330
326
|
<div
|
|
@@ -119,14 +119,13 @@ function AvatarGroupCount({
|
|
|
119
119
|
return (
|
|
120
120
|
<span
|
|
121
121
|
data-slot="avatar-group-count"
|
|
122
|
-
dir={dir}
|
|
123
122
|
className={cn(
|
|
124
123
|
"-ms-[var(--bh-space-md-8)] flex size-[var(--bh-space-7xl-40)] items-center justify-center rounded-[var(--bh-radius-full)] bg-[var(--bh-bg-neutral-subtle)] text-[length:var(--bh-text-body-sm-medium-font-size)] font-[var(--bh-text-body-sm-medium-font-weight)] leading-[var(--bh-text-body-sm-medium-line-height)] tracking-[var(--bh-text-body-sm-medium-letter-spacing)] text-[var(--bh-content-default)] ring-[var(--bh-space-xxs-2)] ring-[var(--bh-bg-default)]",
|
|
125
124
|
className
|
|
126
125
|
)}
|
|
127
126
|
{...props}
|
|
128
127
|
>
|
|
129
|
-
{children}
|
|
128
|
+
<span dir={dir}>{children}</span>
|
|
130
129
|
</span>
|
|
131
130
|
)
|
|
132
131
|
}
|
|
@@ -18,7 +18,7 @@ const buttonGroupContext = React.createContext<ButtonGroupContextValue>({
|
|
|
18
18
|
})
|
|
19
19
|
|
|
20
20
|
const buttonGroupVariants = cva(
|
|
21
|
-
"inline-flex items-center overflow-hidden rounded-[var(--bh-button-group-radius)] bg-[var(--bh-button-group-bg)] text-[var(--bh-button-group-content)] shadow-[var(--shadow-button-group)] [--shadow-button-group:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-button-group-border),var(--shadow-component-default)]",
|
|
21
|
+
"inline-flex w-fit items-center overflow-hidden rounded-[var(--bh-button-group-radius)] bg-[var(--bh-button-group-bg)] text-[var(--bh-button-group-content)] shadow-[var(--shadow-button-group)] [--shadow-button-group:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-button-group-border),var(--shadow-component-default)]",
|
|
22
22
|
{
|
|
23
23
|
variants: {
|
|
24
24
|
size: {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import * as React from "react"
|
|
4
4
|
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
|
|
5
5
|
import { cva, type VariantProps } from "class-variance-authority"
|
|
6
|
+
import { CheckIcon, MinusIcon } from "lucide-react"
|
|
6
7
|
|
|
7
8
|
import { cn } from "@/lib/utils"
|
|
8
9
|
|
|
@@ -58,8 +59,18 @@ const Checkbox = React.forwardRef<
|
|
|
58
59
|
data-slot="checkbox-indicator"
|
|
59
60
|
className="pointer-events-none absolute top-1/2 left-1/2 flex size-[var(--bh-checkbox-icon-size)] -translate-x-1/2 -translate-y-1/2 items-center justify-center text-[var(--bh-content-on-color)] group-data-[disabled]/checkbox:text-[var(--bh-content-disabled)]"
|
|
60
61
|
>
|
|
61
|
-
<
|
|
62
|
-
|
|
62
|
+
<CheckIcon
|
|
63
|
+
aria-hidden="true"
|
|
64
|
+
className="size-[var(--bh-checkbox-icon-size)] group-data-[state=indeterminate]/checkbox:hidden"
|
|
65
|
+
focusable="false"
|
|
66
|
+
strokeWidth={3}
|
|
67
|
+
/>
|
|
68
|
+
<MinusIcon
|
|
69
|
+
aria-hidden="true"
|
|
70
|
+
className="hidden size-[var(--bh-checkbox-icon-size)] group-data-[state=indeterminate]/checkbox:block"
|
|
71
|
+
focusable="false"
|
|
72
|
+
strokeWidth={3}
|
|
73
|
+
/>
|
|
63
74
|
</CheckboxPrimitive.Indicator>
|
|
64
75
|
</CheckboxPrimitive.Root>
|
|
65
76
|
)
|
|
@@ -216,25 +227,17 @@ function CheckboxCardIndicator({
|
|
|
216
227
|
className="pointer-events-none absolute top-1/2 left-1/2 size-[var(--bh-checkbox-control-size)] -translate-x-1/2 -translate-y-1/2 rounded-[var(--bh-checkbox-control-radius)] bg-[var(--bh-bg-default)] shadow-[var(--shadow-checkbox-control)] [--bh-checkbox-control-border:var(--bh-border-input)] [--shadow-checkbox-control:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-checkbox-control-border,var(--bh-border-input)),var(--shadow-component-default)] data-[state=checked]:[--bh-checkbox-control-border:transparent] data-[state=checked]:bg-[var(--bh-interactive-checkbox-default)] data-[disabled=true]:bg-[var(--bh-bg-disabled)] data-[disabled=true]:shadow-[inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-border-disabled)]"
|
|
217
228
|
/>
|
|
218
229
|
{checked ? (
|
|
219
|
-
<
|
|
230
|
+
<CheckIcon
|
|
231
|
+
aria-hidden="true"
|
|
232
|
+
className="absolute top-1/2 left-1/2 size-[var(--bh-checkbox-icon-size)] -translate-x-1/2 -translate-y-1/2 text-[var(--bh-content-on-color)]"
|
|
233
|
+
focusable="false"
|
|
234
|
+
strokeWidth={3}
|
|
235
|
+
/>
|
|
220
236
|
) : null}
|
|
221
237
|
</span>
|
|
222
238
|
)
|
|
223
239
|
}
|
|
224
240
|
|
|
225
|
-
function CheckFillIcon({ className }: { className?: string }) {
|
|
226
|
-
return (
|
|
227
|
-
<svg
|
|
228
|
-
aria-hidden="true"
|
|
229
|
-
className={className}
|
|
230
|
-
focusable="false"
|
|
231
|
-
viewBox="0 0 24 24"
|
|
232
|
-
>
|
|
233
|
-
<path d="M9.9997 15.1709L19.1921 5.97852L20.6063 7.39273L9.9997 17.9993L3.63574 11.6354L5.04996 10.2212L9.9997 15.1709Z" />
|
|
234
|
-
</svg>
|
|
235
|
-
)
|
|
236
|
-
}
|
|
237
|
-
|
|
238
241
|
export { Checkbox, CheckboxCard, checkboxCardVariants, checkboxVariants }
|
|
239
242
|
export type {
|
|
240
243
|
CheckboxCardControlPosition,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
+
import { Button } from "@/components/ui/button";
|
|
2
3
|
import "./activityFeed.css";
|
|
3
4
|
|
|
4
5
|
const defaultAvatarSrc = new URL("../assets/activity-feed-avatar.png", import.meta.url).href;
|
|
@@ -85,26 +86,48 @@ function Indicator({
|
|
|
85
86
|
}
|
|
86
87
|
|
|
87
88
|
function ActionButton({ action }: { action: ActivityFeedAction }) {
|
|
88
|
-
const
|
|
89
|
+
const variant = action.variant === "primary" ? "default" : "outline";
|
|
89
90
|
|
|
90
91
|
if (action.href) {
|
|
91
92
|
return (
|
|
92
|
-
<
|
|
93
|
-
{action.
|
|
94
|
-
|
|
93
|
+
<Button
|
|
94
|
+
aria-label={action.ariaLabel}
|
|
95
|
+
asChild
|
|
96
|
+
className="ds-activity-feed-item__button"
|
|
97
|
+
size="sm"
|
|
98
|
+
variant={variant}
|
|
99
|
+
>
|
|
100
|
+
<a
|
|
101
|
+
aria-disabled={action.disabled || undefined}
|
|
102
|
+
href={action.disabled ? undefined : action.href}
|
|
103
|
+
onClick={(event) => {
|
|
104
|
+
if (action.disabled) {
|
|
105
|
+
event.preventDefault();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
action.onClick?.();
|
|
110
|
+
}}
|
|
111
|
+
tabIndex={action.disabled ? -1 : undefined}
|
|
112
|
+
>
|
|
113
|
+
{action.label}
|
|
114
|
+
</a>
|
|
115
|
+
</Button>
|
|
95
116
|
);
|
|
96
117
|
}
|
|
97
118
|
|
|
98
119
|
return (
|
|
99
|
-
<
|
|
120
|
+
<Button
|
|
100
121
|
aria-label={action.ariaLabel}
|
|
101
|
-
className=
|
|
122
|
+
className="ds-activity-feed-item__button"
|
|
102
123
|
disabled={action.disabled}
|
|
103
124
|
onClick={action.onClick}
|
|
125
|
+
size="sm"
|
|
104
126
|
type="button"
|
|
127
|
+
variant={variant}
|
|
105
128
|
>
|
|
106
129
|
{action.label}
|
|
107
|
-
</
|
|
130
|
+
</Button>
|
|
108
131
|
);
|
|
109
132
|
}
|
|
110
133
|
|
|
@@ -135,17 +158,11 @@ export function ActivityFeedItem({
|
|
|
135
158
|
const rtl = dir === "rtl";
|
|
136
159
|
const nodeId = rtl ? (type === "caption" ? "587:18720" : "587:18701") : type === "caption" ? "584:18591" : "584:10411";
|
|
137
160
|
|
|
138
|
-
const textParts =
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
]
|
|
144
|
-
: [
|
|
145
|
-
label && <strong key="label">{label}</strong>,
|
|
146
|
-
hasSupportText && supportText && <span className="ds-activity-feed-item__support" key="support">{supportText}</span>,
|
|
147
|
-
hasLink && link && <span className="ds-activity-feed-item__link" key="link">{link}</span>,
|
|
148
|
-
];
|
|
161
|
+
const textParts = [
|
|
162
|
+
label && <strong key="label">{label}</strong>,
|
|
163
|
+
hasSupportText && supportText && <span className="ds-activity-feed-item__support" key="support">{supportText}</span>,
|
|
164
|
+
hasLink && link && <span className="ds-activity-feed-item__link" key="link">{link}</span>,
|
|
165
|
+
];
|
|
149
166
|
|
|
150
167
|
return (
|
|
151
168
|
<article
|
|
@@ -158,14 +175,13 @@ export function ActivityFeedItem({
|
|
|
158
175
|
data-node-id={nodeId}
|
|
159
176
|
dir={rtl ? "rtl" : "ltr"}
|
|
160
177
|
>
|
|
161
|
-
|
|
178
|
+
<Indicator avatarAlt={avatarAlt} avatarSrc={avatarSrc} rtl={rtl} showLine={showLine} type={type} />
|
|
162
179
|
|
|
163
180
|
<div className="ds-activity-feed-item__content">
|
|
164
181
|
<div className="ds-activity-feed-item__container">
|
|
165
182
|
<div className="ds-activity-feed-item__topline">
|
|
166
|
-
{rtl && hasStatus && <StatusDot />}
|
|
167
183
|
<div className="ds-activity-feed-item__text">{textParts}</div>
|
|
168
|
-
{
|
|
184
|
+
{hasStatus && <StatusDot />}
|
|
169
185
|
</div>
|
|
170
186
|
|
|
171
187
|
{type === "slot" ? (
|
|
@@ -191,8 +207,6 @@ export function ActivityFeedItem({
|
|
|
191
207
|
|
|
192
208
|
{showPaddingBottom && <div className="ds-activity-feed-item__bottom-space" aria-hidden="true" />}
|
|
193
209
|
</div>
|
|
194
|
-
|
|
195
|
-
{rtl && <Indicator avatarAlt={avatarAlt} avatarSrc={avatarSrc} rtl={rtl} showLine={showLine} type={type} />}
|
|
196
210
|
</article>
|
|
197
211
|
);
|
|
198
212
|
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
+
import { MegaphoneIcon as LucideMegaphoneIcon, XIcon } from "lucide-react";
|
|
3
|
+
|
|
4
|
+
import { Button, type ButtonProps } from "@/components/ui/button";
|
|
5
|
+
import { Input } from "@/components/ui/input";
|
|
6
|
+
|
|
2
7
|
import "./banner.css";
|
|
3
8
|
|
|
4
9
|
export type BannerType = "slim" | "single-action-inline" | "single-action" | "input";
|
|
@@ -30,19 +35,17 @@ export type BannerProps = {
|
|
|
30
35
|
};
|
|
31
36
|
|
|
32
37
|
function MegaphoneIcon() {
|
|
33
|
-
return
|
|
34
|
-
<svg aria-hidden="true" className="ds-banner__icon" viewBox="0 0 24 24">
|
|
35
|
-
<path d="M3 10.5v3a2.5 2.5 0 0 0 2.5 2.5h.63l1.15 3.44A1.5 1.5 0 0 0 8.7 20.5h1.1a1 1 0 0 0 .95-1.32L9.7 16H11l7.24 3.1A1.25 1.25 0 0 0 20 17.95V6.05a1.25 1.25 0 0 0-1.76-1.14L11 8H5.5A2.5 2.5 0 0 0 3 10.5Zm15 6.69-6-2.58V9.39l6-2.58v10.38ZM5 10.5A.5.5 0 0 1 5.5 10H10v4H5.5a.5.5 0 0 1-.5-.5v-3Z" />
|
|
36
|
-
</svg>
|
|
37
|
-
);
|
|
38
|
+
return <LucideMegaphoneIcon aria-hidden="true" className="ds-banner__icon" strokeWidth={2.1} />;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
function CloseIcon() {
|
|
41
|
-
return
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
);
|
|
42
|
+
return <XIcon aria-hidden="true" className="ds-banner__close-icon" strokeWidth={2.25} />;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getBannerActionVariant(color: BannerColor): ButtonProps["variant"] {
|
|
46
|
+
if (color === "brand-light") return "default";
|
|
47
|
+
if (color === "grey") return "secondary";
|
|
48
|
+
return "white";
|
|
46
49
|
}
|
|
47
50
|
|
|
48
51
|
function cx(...classes: Array<string | false | undefined>) {
|
|
@@ -75,10 +78,18 @@ export function Banner({
|
|
|
75
78
|
const isInlineAction = type === "slim" || type === "single-action-inline";
|
|
76
79
|
const inlineActionLabel = type === "single-action-inline" ? actionLabel : linkLabel;
|
|
77
80
|
const hasInlineAction = isInlineAction && (showInlineAction ?? true) && Boolean(inlineActionLabel);
|
|
81
|
+
const actionVariant = getBannerActionVariant(color);
|
|
78
82
|
const closeButton = (
|
|
79
|
-
<
|
|
83
|
+
<Button
|
|
84
|
+
aria-label={closeLabel ?? "Dismiss banner"}
|
|
85
|
+
className="ds-banner__close"
|
|
86
|
+
onClick={onClose}
|
|
87
|
+
size="icon-xs"
|
|
88
|
+
type="button"
|
|
89
|
+
variant="ghost"
|
|
90
|
+
>
|
|
80
91
|
<CloseIcon />
|
|
81
|
-
</
|
|
92
|
+
</Button>
|
|
82
93
|
);
|
|
83
94
|
|
|
84
95
|
return (
|
|
@@ -110,18 +121,30 @@ export function Banner({
|
|
|
110
121
|
</div>
|
|
111
122
|
|
|
112
123
|
{hasInlineAction && (
|
|
113
|
-
<
|
|
124
|
+
<Button
|
|
125
|
+
className="ds-banner__link"
|
|
126
|
+
onClick={onAction}
|
|
127
|
+
size="sm"
|
|
128
|
+
type="button"
|
|
129
|
+
variant={type === "single-action-inline" ? actionVariant : "link"}
|
|
130
|
+
>
|
|
114
131
|
{inlineActionLabel}
|
|
115
|
-
</
|
|
132
|
+
</Button>
|
|
116
133
|
)}
|
|
117
134
|
</div>
|
|
118
135
|
|
|
119
136
|
{type === "single-action" && (
|
|
120
137
|
<div className="ds-banner__tail">
|
|
121
138
|
{actionLabel && (
|
|
122
|
-
<
|
|
139
|
+
<Button
|
|
140
|
+
className="ds-banner__action"
|
|
141
|
+
onClick={onAction}
|
|
142
|
+
size="sm"
|
|
143
|
+
type="button"
|
|
144
|
+
variant={actionVariant}
|
|
145
|
+
>
|
|
123
146
|
{actionLabel}
|
|
124
|
-
</
|
|
147
|
+
</Button>
|
|
125
148
|
)}
|
|
126
149
|
{closeButton}
|
|
127
150
|
</div>
|
|
@@ -129,11 +152,23 @@ export function Banner({
|
|
|
129
152
|
|
|
130
153
|
{type === "input" && (
|
|
131
154
|
<div className="ds-banner__tail ds-banner__tail--input">
|
|
132
|
-
<
|
|
155
|
+
<Input
|
|
156
|
+
aria-label={inputAriaLabel}
|
|
157
|
+
className="ds-banner__input"
|
|
158
|
+
hasInformationIcon={false}
|
|
159
|
+
placeholder={inputPlaceholder}
|
|
160
|
+
size="md"
|
|
161
|
+
/>
|
|
133
162
|
{actionLabel && (
|
|
134
|
-
<
|
|
163
|
+
<Button
|
|
164
|
+
className="ds-banner__action"
|
|
165
|
+
onClick={onAction}
|
|
166
|
+
size="sm"
|
|
167
|
+
type="button"
|
|
168
|
+
variant={actionVariant}
|
|
169
|
+
>
|
|
135
170
|
{actionLabel}
|
|
136
|
-
</
|
|
171
|
+
</Button>
|
|
137
172
|
)}
|
|
138
173
|
{closeButton}
|
|
139
174
|
</div>
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
+
import {
|
|
3
|
+
ChevronRightIcon,
|
|
4
|
+
EllipsisIcon,
|
|
5
|
+
FolderIcon,
|
|
6
|
+
HomeIcon,
|
|
7
|
+
} from "lucide-react";
|
|
2
8
|
import "./breadcrumbs.css";
|
|
3
9
|
|
|
4
10
|
export type BreadcrumbSeparator = "slash" | "chevron";
|
|
@@ -106,51 +112,17 @@ function Separator({ separator }: { separator: BreadcrumbSeparator }) {
|
|
|
106
112
|
}
|
|
107
113
|
|
|
108
114
|
export function BreadcrumbHomeIcon() {
|
|
109
|
-
return
|
|
110
|
-
<svg aria-hidden="true" viewBox="0 0 20 20">
|
|
111
|
-
<path
|
|
112
|
-
d="M3.5 8.7 10 3.4l6.5 5.3v7.1a.9.9 0 0 1-.9.9h-3.2v-4.9H7.6v4.9H4.4a.9.9 0 0 1-.9-.9V8.7Z"
|
|
113
|
-
fill="none"
|
|
114
|
-
stroke="currentColor"
|
|
115
|
-
strokeLinejoin="round"
|
|
116
|
-
strokeWidth="1.7"
|
|
117
|
-
/>
|
|
118
|
-
</svg>
|
|
119
|
-
);
|
|
115
|
+
return <HomeIcon aria-hidden="true" strokeWidth={1.9} />;
|
|
120
116
|
}
|
|
121
117
|
|
|
122
118
|
export function BreadcrumbFolderIcon() {
|
|
123
|
-
return
|
|
124
|
-
<svg aria-hidden="true" viewBox="0 0 20 20">
|
|
125
|
-
<path
|
|
126
|
-
d="M2.8 5.7c0-.9.7-1.6 1.6-1.6h3.8l1.5 1.8h5.9c.9 0 1.6.7 1.6 1.6v6.8c0 .9-.7 1.6-1.6 1.6H4.4c-.9 0-1.6-.7-1.6-1.6V5.7Z"
|
|
127
|
-
fill="none"
|
|
128
|
-
stroke="currentColor"
|
|
129
|
-
strokeLinejoin="round"
|
|
130
|
-
strokeWidth="1.7"
|
|
131
|
-
/>
|
|
132
|
-
</svg>
|
|
133
|
-
);
|
|
119
|
+
return <FolderIcon aria-hidden="true" strokeWidth={1.9} />;
|
|
134
120
|
}
|
|
135
121
|
|
|
136
122
|
function ChevronIcon() {
|
|
137
|
-
return
|
|
138
|
-
<svg aria-hidden="true" viewBox="0 0 16 16">
|
|
139
|
-
<path d="m6 3.5 4 4.5-4 4.5" fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.6" />
|
|
140
|
-
</svg>
|
|
141
|
-
);
|
|
123
|
+
return <ChevronRightIcon aria-hidden="true" strokeWidth={2} />;
|
|
142
124
|
}
|
|
143
125
|
|
|
144
126
|
function OverflowIcon() {
|
|
145
|
-
return
|
|
146
|
-
<svg aria-hidden="true" viewBox="0 0 20 20">
|
|
147
|
-
<path
|
|
148
|
-
d="M5.2 10h.1M10 10h.1M14.8 10h.1"
|
|
149
|
-
fill="none"
|
|
150
|
-
stroke="currentColor"
|
|
151
|
-
strokeLinecap="round"
|
|
152
|
-
strokeWidth="2.4"
|
|
153
|
-
/>
|
|
154
|
-
</svg>
|
|
155
|
-
);
|
|
127
|
+
return <EllipsisIcon aria-hidden="true" strokeWidth={2.25} />;
|
|
156
128
|
}
|