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
@@ -2,16 +2,39 @@ import * as React from "react"
2
2
 
3
3
  import { cn } from "@/lib/utils"
4
4
 
5
- function Spinner({ className, ...props }: React.ComponentProps<"svg">) {
5
+ type SpinnerMotion = "dynamic" | "steady"
6
+
7
+ type SpinnerProps = React.ComponentProps<"svg">
8
+
9
+ type DynamicSpinnerProps = SpinnerProps
10
+
11
+ type SpinnerBaseProps = SpinnerProps & {
12
+ motion: SpinnerMotion
13
+ }
14
+
15
+ function Spinner(props: SpinnerProps) {
16
+ return <SpinnerBase {...props} motion="steady" />
17
+ }
18
+
19
+ function DynamicSpinner(props: DynamicSpinnerProps) {
20
+ return <SpinnerBase {...props} motion="dynamic" />
21
+ }
22
+
23
+ function SpinnerBase({ className, motion, ...props }: SpinnerBaseProps) {
6
24
  return (
7
25
  <svg
8
26
  aria-hidden="true"
27
+ data-motion={motion}
9
28
  data-slot="spinner"
10
29
  viewBox="0 0 24 24"
11
- className={cn("size-[var(--bh-spinner-size)] animate-spin text-current", className)}
30
+ className={cn(
31
+ "size-[var(--bh-spinner-size)] shrink-0 text-current",
32
+ className
33
+ )}
12
34
  {...props}
13
35
  >
14
36
  <circle
37
+ data-slot="spinner-track"
15
38
  cx="12"
16
39
  cy="12"
17
40
  r="10"
@@ -20,12 +43,73 @@ function Spinner({ className, ...props }: React.ComponentProps<"svg">) {
20
43
  strokeWidth="4"
21
44
  opacity="var(--bh-opacity-25)"
22
45
  />
23
- <path
24
- fill="currentColor"
25
- d="M22 12a10 10 0 0 1-10 10v-4a6 6 0 0 0 6-6z"
26
- />
46
+ <circle
47
+ data-slot="spinner-arc"
48
+ cx="12"
49
+ cy="12"
50
+ r="10"
51
+ fill="none"
52
+ stroke="currentColor"
53
+ strokeWidth="4"
54
+ strokeLinecap="round"
55
+ pathLength="100"
56
+ strokeDasharray="30 70"
57
+ >
58
+ {motion === "dynamic" ? (
59
+ <DynamicSpinnerMotion />
60
+ ) : (
61
+ <SteadySpinnerMotion />
62
+ )}
63
+ </circle>
27
64
  </svg>
28
65
  )
29
66
  }
30
67
 
31
- export { Spinner }
68
+ function DynamicSpinnerMotion() {
69
+ return (
70
+ <>
71
+ <animate
72
+ attributeName="stroke-dasharray"
73
+ calcMode="spline"
74
+ dur="6s"
75
+ keySplines="0.45 0 0.55 1; 0.45 0 0.55 1; 0.45 0 0.55 1; 0.45 0 0.55 1"
76
+ keyTimes="0; 0.25; 0.5; 0.75; 1"
77
+ repeatCount="indefinite"
78
+ values="22 78; 38 62; 30 70; 42 58; 22 78"
79
+ />
80
+ <animate
81
+ attributeName="stroke-dashoffset"
82
+ calcMode="spline"
83
+ dur="6s"
84
+ keySplines="0.45 0 0.55 1; 0.45 0 0.55 1; 0.45 0 0.55 1; 0.45 0 0.55 1"
85
+ keyTimes="0; 0.25; 0.5; 0.75; 1"
86
+ repeatCount="indefinite"
87
+ values="0; -16; -44; -72; -100"
88
+ />
89
+ <animateTransform
90
+ attributeName="transform"
91
+ dur="6s"
92
+ from="0 12 12"
93
+ repeatCount="indefinite"
94
+ to="1080 12 12"
95
+ type="rotate"
96
+ />
97
+ </>
98
+ )
99
+ }
100
+
101
+ function SteadySpinnerMotion() {
102
+ return (
103
+ <animateTransform
104
+ attributeName="transform"
105
+ dur="1.5s"
106
+ from="0 12 12"
107
+ repeatCount="indefinite"
108
+ to="360 12 12"
109
+ type="rotate"
110
+ />
111
+ )
112
+ }
113
+
114
+ export { DynamicSpinner, Spinner }
115
+ export type { DynamicSpinnerProps, SpinnerProps }
@@ -1,5 +1,6 @@
1
1
  import * as React from "react"
2
2
  import { cva } from "class-variance-authority"
3
+ import { CircleAlertIcon, InfoIcon, XIcon } from "lucide-react"
3
4
 
4
5
  import { cn } from "@/lib/utils"
5
6
 
@@ -99,7 +100,7 @@ const textareaControl = cva(
99
100
  variants: {
100
101
  type: {
101
102
  default:
102
- "min-h-[calc(var(--bh-textarea-min-height)_-_var(--bh-textarea-padding)_-_var(--bh-textarea-padding))]",
103
+ "min-h-[calc(var(--bh-textarea-min-height)_-_var(--bh-textarea-padding)_-_var(--bh-textarea-padding))] overflow-y-auto [scrollbar-gutter:stable]",
103
104
  tags:
104
105
  "h-[var(--bh-text-body-md-regular-line-height)] min-h-[var(--bh-text-body-md-regular-line-height)] overflow-hidden",
105
106
  },
@@ -533,53 +534,37 @@ function TextareaIcon({
533
534
  className
534
535
  )}
535
536
  >
536
- <svg
537
- className="size-[var(--bh-textarea-helper-icon-size)]"
538
- fill="none"
539
- focusable="false"
540
- viewBox="0 0 13.3333 13.3333"
541
- >
542
- {type === "error" ? <ErrorIconPath /> : <InfoIconPath />}
543
- </svg>
537
+ {type === "error" ? (
538
+ <CircleAlertIcon
539
+ aria-hidden="true"
540
+ className="size-[var(--bh-textarea-helper-icon-size)]"
541
+ focusable="false"
542
+ strokeWidth={2.25}
543
+ />
544
+ ) : (
545
+ <InfoIcon
546
+ aria-hidden="true"
547
+ className="size-[var(--bh-textarea-helper-icon-size)]"
548
+ focusable="false"
549
+ strokeWidth={2.25}
550
+ />
551
+ )}
544
552
  </span>
545
553
  )
546
554
  }
547
555
 
548
- function ErrorIconPath() {
549
- return (
550
- <path
551
- d="M6.66667 13.3333C2.98477 13.3333 0 10.3485 0 6.66667C0 2.98477 2.98477 0 6.66667 0C10.3485 0 13.3333 2.98477 13.3333 6.66667C13.3333 10.3485 10.3485 13.3333 6.66667 13.3333ZM6 8.66667V10H7.33333V8.66667H6ZM6 3.33333V7.33333H7.33333V3.33333H6Z"
552
- fill="currentColor"
553
- />
554
- )
555
- }
556
-
557
- function InfoIconPath() {
558
- return (
559
- <path
560
- d="M6.66667 13.3333C2.98477 13.3333 0 10.3485 0 6.66667C0 2.98477 2.98477 0 6.66667 0C10.3485 0 13.3333 2.98477 13.3333 6.66667C13.3333 10.3485 10.3485 13.3333 6.66667 13.3333ZM6.66667 5C7.21893 5 7.66667 4.55229 7.66667 4C7.66667 3.44771 7.21893 3 6.66667 3C6.1144 3 5.66667 3.44771 5.66667 4C5.66667 4.55229 6.1144 5 6.66667 5ZM8 8.66667H7.33333V5.66667H5.33333V7H6V8.66667H5.33333V10H8V8.66667Z"
561
- fill="currentColor"
562
- />
563
- )
564
- }
565
-
566
556
  function CloseIcon() {
567
557
  return (
568
558
  <span
569
559
  aria-hidden="true"
570
560
  className="flex size-[var(--bh-textarea-close-button-size)] items-center justify-center"
571
561
  >
572
- <svg
562
+ <XIcon
563
+ aria-hidden="true"
573
564
  className="size-[var(--bh-textarea-close-icon-size)]"
574
- fill="none"
575
565
  focusable="false"
576
- viewBox="0 0 6.36398 6.36394"
577
- >
578
- <path
579
- d="M3.18198 2.47489L5.65688 0L6.36398 0.707105L3.88908 3.18199L6.36398 5.65684L5.65688 6.36394L3.18198 3.88909L0.70711 6.36394L0 5.65684L2.47488 3.18199L0 0.707105L0.70711 0L3.18198 2.47489Z"
580
- fill="currentColor"
581
- />
582
- </svg>
566
+ strokeWidth={2.5}
567
+ />
583
568
  </span>
584
569
  )
585
570
  }
@@ -3,6 +3,7 @@
3
3
  import * as React from "react"
4
4
  import * as SwitchPrimitive from "@radix-ui/react-switch"
5
5
  import { cva, type VariantProps } from "class-variance-authority"
6
+ import { CheckIcon, XIcon } from "lucide-react"
6
7
 
7
8
  import { cn } from "@/lib/utils"
8
9
 
@@ -226,40 +227,23 @@ const ToggleField = React.forwardRef<HTMLDivElement, ToggleFieldProps>(
226
227
 
227
228
  function ToggleCheckIcon({ className }: { className?: string }) {
228
229
  return (
229
- <svg
230
+ <CheckIcon
230
231
  aria-hidden="true"
231
232
  className={className}
232
- fill="none"
233
233
  focusable="false"
234
- viewBox="0 0 16 16"
235
- >
236
- <path
237
- d="M4 8.2 6.55 10.75 12 5.25"
238
- stroke="currentColor"
239
- strokeLinecap="round"
240
- strokeLinejoin="round"
241
- strokeWidth="1.75"
242
- />
243
- </svg>
234
+ strokeWidth={2.5}
235
+ />
244
236
  )
245
237
  }
246
238
 
247
239
  function ToggleCloseIcon({ className }: { className?: string }) {
248
240
  return (
249
- <svg
241
+ <XIcon
250
242
  aria-hidden="true"
251
243
  className={className}
252
- fill="none"
253
244
  focusable="false"
254
- viewBox="0 0 16 16"
255
- >
256
- <path
257
- d="m5 5 6 6m0-6-6 6"
258
- stroke="currentColor"
259
- strokeLinecap="round"
260
- strokeWidth="1.75"
261
- />
262
- </svg>
245
+ strokeWidth={2.5}
246
+ />
263
247
  )
264
248
  }
265
249
 
@@ -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
@@ -386,13 +390,13 @@ const pointerPlacement: Record<Exclude<TooltipPointerPosition, "none">, string>
386
390
 
387
391
  const pointerFillShape = {
388
392
  top:
389
- "left-0 top-0 border-x-[length:var(--bh-tooltip-pointer-half-width)] border-b-[length:var(--bh-tooltip-pointer-depth)] border-x-[color:transparent] border-b-[color:var(--bh-tooltip-bg)]",
393
+ "left-0 top-[var(--bh-space-xxxs-1)] border-x-[length:var(--bh-tooltip-pointer-half-width)] border-b-[length:var(--bh-tooltip-pointer-depth)] border-x-[color:transparent] border-b-[color:var(--bh-tooltip-bg)]",
390
394
  bottom:
391
- "bottom-0 left-0 border-x-[length:var(--bh-tooltip-pointer-half-width)] border-t-[length:var(--bh-tooltip-pointer-depth)] border-x-[color:transparent] border-t-[color:var(--bh-tooltip-bg)]",
395
+ "bottom-[var(--bh-space-xxxs-1)] left-0 border-x-[length:var(--bh-tooltip-pointer-half-width)] border-t-[length:var(--bh-tooltip-pointer-depth)] border-x-[color:transparent] border-t-[color:var(--bh-tooltip-bg)]",
392
396
  right:
393
- "left-0 top-0 border-y-[length:var(--bh-tooltip-pointer-half-width)] border-l-[length:var(--bh-tooltip-pointer-depth)] border-y-[color:transparent] border-l-[color:var(--bh-tooltip-bg)]",
397
+ "left-[calc(var(--bh-space-xxxs-1)*-1)] top-0 border-y-[length:var(--bh-tooltip-pointer-half-width)] border-l-[length:var(--bh-tooltip-pointer-depth)] border-y-[color:transparent] border-l-[color:var(--bh-tooltip-bg)]",
394
398
  left:
395
- "right-0 top-0 border-y-[length:var(--bh-tooltip-pointer-half-width)] border-r-[length:var(--bh-tooltip-pointer-depth)] border-y-[color:transparent] border-r-[color:var(--bh-tooltip-bg)]",
399
+ "right-[calc(var(--bh-space-xxxs-1)*-1)] top-0 border-y-[length:var(--bh-tooltip-pointer-half-width)] border-r-[length:var(--bh-tooltip-pointer-depth)] border-y-[color:transparent] border-r-[color:var(--bh-tooltip-bg)]",
396
400
  } as const
397
401
 
398
402
  const pointerBorderShape = {
@@ -38,9 +38,9 @@ export function AttributeDemo() {
38
38
  <AttributeCard>
39
39
  <AttributeHeader
40
40
  title="Order details"
41
- description="End-aligned values, copy actions, files, and status chips."
41
+ description="Rich values with copy actions, files, and status chips."
42
42
  />
43
- <AttributeList layout="end" dividers>
43
+ <AttributeList dividers>
44
44
  <AttributeItem label="Subscription">
45
45
  <Badge color="green" type="dot">
46
46
  Active
@@ -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
- AvatarBadge,
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-6">
17
- <div className="flex flex-wrap items-center gap-4">
18
- <Avatar>
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>CN</AvatarFallback>
62
+ <AvatarFallback>AG</AvatarFallback>
63
+ <AvatarAction type="edit" size="medium" />
21
64
  </Avatar>
22
- <Avatar>
23
- <AvatarFallback>ER</AvatarFallback>
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="/missing-avatar.png" alt="Missing image" />
27
- <AvatarFallback>LR</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 flex-wrap items-center gap-4">
32
- <Avatar>
33
- <AvatarImage src={avatarUrl} alt="Profile portrait" />
34
- <AvatarFallback>CN</AvatarFallback>
35
- <AvatarBadge />
36
- </Avatar>
37
- <Avatar>
38
- <AvatarFallback>PP</AvatarFallback>
39
- <AvatarBadge className="size-5 bg-success">
40
- <PlusIcon className="size-3" />
41
- </AvatarBadge>
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
- <AvatarGroup>
46
- <Avatar>
47
- <AvatarFallback>CN</AvatarFallback>
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
- <Avatar>
50
- <AvatarFallback>LR</AvatarFallback>
51
- </Avatar>
52
- <Avatar>
53
- <AvatarFallback>ER</AvatarFallback>
54
- </Avatar>
55
- <AvatarGroupCount>+3</AvatarGroupCount>
56
- </AvatarGroup>
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 dir="rtl">
59
- <AvatarGroup>
60
- <Avatar>
61
- <AvatarFallback>ح</AvatarFallback>
62
- </Avatar>
63
- <Avatar>
64
- <AvatarFallback>ن</AvatarFallback>
65
- </Avatar>
66
- <Avatar>
67
- <AvatarFallback>م</AvatarFallback>
68
- </Avatar>
69
- <AvatarGroupCount>+٣</AvatarGroupCount>
70
- </AvatarGroup>
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,7 @@
1
1
  import * as React from "react"
2
2
 
3
+ import { UserIcon } from "lucide-react"
4
+
3
5
  import { Checkbox, CheckboxCard } from "@/components/ui/checkbox"
4
6
 
5
7
  export function CheckboxDemo() {
@@ -56,12 +58,5 @@ export function CheckboxDemo() {
56
58
  }
57
59
 
58
60
  function UserLineIcon() {
59
- return (
60
- <svg aria-hidden="true" focusable="false" viewBox="0 0 24 24">
61
- <path
62
- fill="currentColor"
63
- d="M4 22A8 8 0 0 1 20 22H18A6 6 0 0 0 6 22H4ZM12 13C8.685 13 6 10.315 6 7C6 3.685 8.685 1 12 1C15.315 1 18 3.685 18 7C18 10.315 15.315 13 12 13ZM12 11C14.21 11 16 9.21 16 7C16 4.79 14.21 3 12 3C9.79 3 8 4.79 8 7C8 9.21 9.79 11 12 11Z"
64
- />
65
- </svg>
66
- )
61
+ return <UserIcon aria-hidden="true" focusable="false" strokeWidth={2.1} />
67
62
  }