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.
Files changed (58) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  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/avatar.tsx +1 -2
  7. package/registry/components/button-group.tsx +1 -1
  8. package/registry/components/card.tsx +1 -1
  9. package/registry/components/checkbox.tsx +19 -16
  10. package/registry/components/expanded/ActivityFeed.tsx +37 -23
  11. package/registry/components/expanded/Banner.tsx +54 -19
  12. package/registry/components/expanded/Breadcrumbs.tsx +10 -38
  13. package/registry/components/expanded/CatalogComponentsShowcase.tsx +11 -16
  14. package/registry/components/expanded/CatalogTag.tsx +4 -11
  15. package/registry/components/expanded/CommandBar.tsx +33 -53
  16. package/registry/components/expanded/FileUpload.tsx +362 -59
  17. package/registry/components/expanded/OnboardingStepListItem.tsx +6 -10
  18. package/registry/components/expanded/PageHeader.tsx +2 -11
  19. package/registry/components/expanded/Slideout.tsx +12 -23
  20. package/registry/components/expanded/Steps.tsx +6 -8
  21. package/registry/components/expanded/Table.tsx +18 -40
  22. package/registry/components/expanded/Timeline.tsx +5 -24
  23. package/registry/components/expanded/activityFeed.css +10 -54
  24. package/registry/components/expanded/banner.css +8 -75
  25. package/registry/components/expanded/breadcrumbs.css +1 -1
  26. package/registry/components/expanded/commandBar.css +23 -26
  27. package/registry/components/expanded/divider.css +1 -1
  28. package/registry/components/expanded/fileUpload.css +304 -75
  29. package/registry/components/expanded/pageHeader.css +1 -1
  30. package/registry/components/expanded/steps.css +15 -51
  31. package/registry/components/expanded/table.css +5 -1
  32. package/registry/components/expanded/timeline.css +18 -15
  33. package/registry/components/input.tsx +125 -54
  34. package/registry/components/menu.tsx +99 -72
  35. package/registry/components/pagination.tsx +6 -18
  36. package/registry/components/radio-card.tsx +25 -31
  37. package/registry/components/select-content.tsx +28 -123
  38. package/registry/components/select.tsx +9 -9
  39. package/registry/components/social-button.tsx +24 -90
  40. package/registry/components/spinner.tsx +21 -5
  41. package/registry/components/textarea.tsx +20 -35
  42. package/registry/components/toggle.tsx +7 -23
  43. package/registry/components/tooltip.tsx +4 -4
  44. package/registry/examples/attribute-demo.tsx +2 -2
  45. package/registry/examples/checkbox-demo.tsx +3 -8
  46. package/registry/examples/date-picker-demo.tsx +75 -22
  47. package/registry/examples/expanded/banner-demo.tsx +31 -6
  48. package/registry/examples/expanded/breadcrumbs-demo.tsx +59 -0
  49. package/registry/examples/expanded/file-upload-demo.tsx +60 -0
  50. package/registry/examples/expanded/steps-demo.tsx +11 -0
  51. package/registry/examples/expanded/table-demo.tsx +142 -0
  52. package/registry/examples/progress-demo.tsx +2 -2
  53. package/registry/examples/select-demo.tsx +32 -18
  54. package/registry/examples/social-button-demo.tsx +33 -33
  55. package/registry/examples/spinner-demo.tsx +37 -0
  56. package/registry/examples/tag-demo.tsx +1 -1
  57. package/registry/index.json +26 -12
  58. package/registry/styles/globals.css +5 -3
@@ -1,5 +1,11 @@
1
1
  import * as React from "react"
2
2
  import { cva } from "class-variance-authority"
3
+ import {
4
+ ChevronDownIcon,
5
+ InfoIcon,
6
+ MinusIcon as LucideMinusIcon,
7
+ PlusIcon as LucidePlusIcon,
8
+ } from "lucide-react"
3
9
 
4
10
  import { cn } from "@/lib/utils"
5
11
  import {
@@ -85,7 +91,11 @@ const inputSurface = cva(
85
91
  "group/input-surface relative flex w-full overflow-hidden rounded-[var(--bh-input-radius)]",
86
92
  "transition-[background-color,box-shadow]",
87
93
  "[--bh-input-border:var(--bh-border-input)] [--shadow-input:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-input-border,var(--bh-border-input)),var(--shadow-component-default)]",
88
- "focus-within:shadow-[var(--shadow-input-focus-ring)]",
94
+ "[--shadow-input-overlay:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-input-border,var(--bh-border-input)),inset_0px_-1px_0px_0px_rgb(0_0_0_/_0.08)]",
95
+ "[--shadow-input-focus-overlay:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-border-focus)]",
96
+ "[--shadow-input-disabled-overlay:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-border-disabled)]",
97
+ "after:pointer-events-none after:absolute after:inset-0 after:z-[var(--bh-z-raised)] after:rounded-[inherit] after:[box-shadow:var(--shadow-input-overlay)] after:content-['']",
98
+ "focus-within:shadow-[var(--shadow-input-focus-ring)] focus-within:after:[box-shadow:var(--shadow-input-focus-overlay)]",
89
99
  ],
90
100
  {
91
101
  variants: {
@@ -103,9 +113,9 @@ const inputSurface = cva(
103
113
  filled:
104
114
  "shadow-[var(--shadow-input)]",
105
115
  error:
106
- "[--bh-input-border:var(--bh-border-danger-strong)] shadow-[var(--shadow-input)] focus-within:shadow-[var(--shadow-input)]",
116
+ "[--bh-input-border:var(--bh-border-danger-strong)] shadow-[var(--shadow-input)] focus-within:shadow-[var(--shadow-input)] focus-within:after:[box-shadow:var(--shadow-input-overlay)]",
107
117
  disabled:
108
- "bg-[var(--bh-interactive-input-disabled)] shadow-[inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-border-disabled)]",
118
+ "[--bh-input-border:var(--bh-border-disabled)] bg-[var(--bh-interactive-input-disabled)] shadow-[inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-border-disabled)] after:[box-shadow:var(--shadow-input-disabled-overlay)]",
109
119
  },
110
120
  },
111
121
  compoundVariants: [
@@ -211,6 +221,27 @@ const inputSegment = cva(
211
221
  }
212
222
  )
213
223
 
224
+ const inputStepperButton = cva(
225
+ [
226
+ "inline-flex h-full w-[var(--bh-input-stepper-width,var(--bh-input-lg-height))] shrink-0 items-center justify-center",
227
+ "bg-[var(--bh-interactive-secondary-default)] text-[var(--bh-content-subtle)]",
228
+ "outline-none transition-[background-color,color]",
229
+ "hover:bg-[var(--bh-interactive-secondary-hover)] focus-visible:bg-[var(--bh-interactive-secondary-hover)]",
230
+ "disabled:bg-[var(--bh-interactive-secondary-disabled)] disabled:text-[var(--bh-content-disabled)]",
231
+ ],
232
+ {
233
+ variants: {
234
+ side: {
235
+ leading: "border-e border-[var(--bh-border-input)]",
236
+ trailing: "border-s border-[var(--bh-border-input)]",
237
+ },
238
+ },
239
+ defaultVariants: {
240
+ side: "trailing",
241
+ },
242
+ }
243
+ )
244
+
214
245
  const Input = React.forwardRef<HTMLInputElement, InputProps>(
215
246
  (
216
247
  {
@@ -352,6 +383,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
352
383
  kind,
353
384
  leadingAddon,
354
385
  quantityLabel,
386
+ size,
355
387
  trailingAddon,
356
388
  })}
357
389
 
@@ -414,6 +446,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
414
446
  kind,
415
447
  leadingAddon,
416
448
  quantityLabel,
449
+ size,
417
450
  trailingAddon,
418
451
  })}
419
452
  </div>
@@ -577,17 +610,12 @@ function InputInfoIcon({
577
610
  )}
578
611
  >
579
612
  {icon || (
580
- <svg
613
+ <InfoIcon
614
+ aria-hidden="true"
581
615
  className="size-[var(--bh-input-info-icon-size)]"
582
- fill="none"
583
616
  focusable="false"
584
- viewBox="0 0 16 16"
585
- >
586
- <path
587
- d="M8 14.6667C4.3181 14.6667 1.33333 11.6819 1.33333 8C1.33333 4.3181 4.3181 1.33333 8 1.33333C11.6819 1.33333 14.6667 4.3181 14.6667 8C14.6667 11.6819 11.6819 14.6667 8 14.6667ZM8 6.33333C8.55227 6.33333 9 5.88562 9 5.33333C9 4.78105 8.55227 4.33333 8 4.33333C7.44773 4.33333 7 4.78105 7 5.33333C7 5.88562 7.44773 6.33333 8 6.33333ZM9.33333 10H8.66667V7H6.66667V8.33333H7.33333V10H6.66667V11.3333H9.33333V10Z"
588
- fill="currentColor"
589
- />
590
- </svg>
617
+ strokeWidth={2.25}
618
+ />
591
619
  )}
592
620
  </span>
593
621
  )
@@ -699,12 +727,46 @@ function InputSegment({
699
727
  )
700
728
  }
701
729
 
730
+ function InputStepperButton({
731
+ children,
732
+ disabled,
733
+ label,
734
+ side,
735
+ size,
736
+ }: {
737
+ children: React.ReactNode
738
+ disabled: boolean
739
+ label: string
740
+ side: "leading" | "trailing"
741
+ size: InputSize
742
+ }) {
743
+ return (
744
+ <button
745
+ aria-label={label}
746
+ data-side={side}
747
+ data-slot="input-stepper-button"
748
+ disabled={disabled}
749
+ type="button"
750
+ className={cn(
751
+ inputStepperButton({ side }),
752
+ size === "md" && "w-[var(--bh-input-md-height)]"
753
+ )}
754
+ onClick={(event) => {
755
+ event.stopPropagation()
756
+ }}
757
+ >
758
+ {children}
759
+ </button>
760
+ )
761
+ }
762
+
702
763
  type InputSegmentRenderContext = {
703
764
  buttonLabel?: React.ReactNode
704
765
  disabled: boolean
705
766
  kind: InputKind
706
767
  leadingAddon?: React.ReactNode
707
768
  quantityLabel?: React.ReactNode
769
+ size: InputSize
708
770
  trailingAddon?: React.ReactNode
709
771
  }
710
772
 
@@ -731,13 +793,25 @@ const leadingSegmentRenderers: Partial<Record<InputKind, InputSegmentRenderer>>
731
793
  <ChevronIcon />
732
794
  </InputSegment>
733
795
  ),
734
- "quantity-2": ({ disabled, quantityLabel }) => (
735
- <InputSegment disabled={disabled} interactive side="leading">
796
+ quantity: ({ disabled, quantityLabel, size }) => (
797
+ <InputStepperButton
798
+ disabled={disabled}
799
+ label={formatStepperLabel("Decrease", quantityLabel)}
800
+ side="leading"
801
+ size={size}
802
+ >
736
803
  <MinusIcon />
737
- {hasRenderableContent(quantityLabel) ? (
738
- <span className="sr-only">{quantityLabel}</span>
739
- ) : null}
740
- </InputSegment>
804
+ </InputStepperButton>
805
+ ),
806
+ "quantity-2": ({ disabled, quantityLabel, size }) => (
807
+ <InputStepperButton
808
+ disabled={disabled}
809
+ label={formatStepperLabel("Decrease", quantityLabel)}
810
+ side="leading"
811
+ size={size}
812
+ >
813
+ <MinusIcon />
814
+ </InputStepperButton>
741
815
  ),
742
816
  }
743
817
 
@@ -760,17 +834,25 @@ const trailingSegmentRenderers: Partial<Record<InputKind, InputSegmentRenderer>>
760
834
  <ChevronIcon />
761
835
  </InputSegment>
762
836
  ),
763
- quantity: ({ disabled }) => (
764
- <InputSegment disabled={disabled} interactive side="trailing">
765
- <MinusIcon />
837
+ quantity: ({ disabled, quantityLabel, size }) => (
838
+ <InputStepperButton
839
+ disabled={disabled}
840
+ label={formatStepperLabel("Increase", quantityLabel)}
841
+ side="trailing"
842
+ size={size}
843
+ >
766
844
  <PlusIcon />
767
- </InputSegment>
845
+ </InputStepperButton>
768
846
  ),
769
- "quantity-2": ({ disabled }) => (
770
- <InputSegment disabled={disabled} interactive side="trailing">
771
- <MinusIcon />
847
+ "quantity-2": ({ disabled, quantityLabel, size }) => (
848
+ <InputStepperButton
849
+ disabled={disabled}
850
+ label={formatStepperLabel("Increase", quantityLabel)}
851
+ side="trailing"
852
+ size={size}
853
+ >
772
854
  <PlusIcon />
773
- </InputSegment>
855
+ </InputStepperButton>
774
856
  ),
775
857
  }
776
858
 
@@ -782,51 +864,42 @@ function renderTrailingSegment(context: InputSegmentRenderContext) {
782
864
  return trailingSegmentRenderers[context.kind]?.(context) ?? null
783
865
  }
784
866
 
867
+ function formatStepperLabel(action: "Decrease" | "Increase", quantityLabel?: React.ReactNode) {
868
+ return typeof quantityLabel === "string" && quantityLabel.trim()
869
+ ? `${action} ${quantityLabel}`
870
+ : action
871
+ }
872
+
785
873
  function ChevronIcon() {
786
874
  return (
787
- <svg
875
+ <ChevronDownIcon
788
876
  aria-hidden="true"
789
877
  className="size-[var(--bh-input-chevron-size)]"
790
- fill="none"
791
878
  focusable="false"
792
- viewBox="0 0 16 16"
793
- >
794
- <path
795
- d="M8 10.6667L4 6.66667L4.93333 5.73333L8 8.8L11.0667 5.73333L12 6.66667L8 10.6667Z"
796
- fill="currentColor"
797
- />
798
- </svg>
879
+ strokeWidth={2.25}
880
+ />
799
881
  )
800
882
  }
801
883
 
802
884
  function MinusIcon() {
803
885
  return (
804
- <svg
886
+ <LucideMinusIcon
805
887
  aria-hidden="true"
806
888
  className="size-[var(--bh-input-stepper-icon-size)]"
807
- fill="none"
808
889
  focusable="false"
809
- viewBox="0 0 16 16"
810
- >
811
- <path d="M3.33333 7.33333H12.6667V8.66667H3.33333V7.33333Z" fill="currentColor" />
812
- </svg>
890
+ strokeWidth={2.25}
891
+ />
813
892
  )
814
893
  }
815
894
 
816
895
  function PlusIcon() {
817
896
  return (
818
- <svg
897
+ <LucidePlusIcon
819
898
  aria-hidden="true"
820
899
  className="size-[var(--bh-input-stepper-icon-size)]"
821
- fill="none"
822
900
  focusable="false"
823
- viewBox="0 0 16 16"
824
- >
825
- <path
826
- d="M7.33333 7.33333V3.33333H8.66667V7.33333H12.6667V8.66667H8.66667V12.6667H7.33333V8.66667H3.33333V7.33333H7.33333Z"
827
- fill="currentColor"
828
- />
829
- </svg>
901
+ strokeWidth={2.25}
902
+ />
830
903
  )
831
904
  }
832
905
 
@@ -836,10 +909,6 @@ function shouldRenderLeadingIcon(kind: InputKind) {
836
909
  "shortcut",
837
910
  "tags",
838
911
  "inline-tags",
839
- "trailing-dropdown",
840
- "leading-dropdown",
841
- "trailing-button",
842
- "leading-button",
843
912
  ].includes(kind)
844
913
  }
845
914
 
@@ -848,6 +917,8 @@ function shouldRenderInfoIcon(kind: InputKind, hasInformationIcon: boolean) {
848
917
 
849
918
  return ![
850
919
  "shortcut",
920
+ "leading-dropdown",
921
+ "leading-button",
851
922
  "trailing-dropdown",
852
923
  "trailing-button",
853
924
  "quantity",
@@ -108,6 +108,7 @@ type MenuContentProps = React.ComponentProps<typeof DropdownMenuPrimitive.Conten
108
108
 
109
109
  const MenuRoot = DropdownMenuPrimitive.Root
110
110
  const MenuTrigger = DropdownMenuPrimitive.Trigger
111
+ const MenuStaticContext = React.createContext(false)
111
112
 
112
113
  const MenuContent = React.forwardRef<HTMLDivElement, MenuContentProps>(function MenuContent({
113
114
  className,
@@ -133,23 +134,36 @@ const MenuContent = React.forwardRef<HTMLDivElement, MenuContentProps>(function
133
134
 
134
135
  function Menu({
135
136
  children,
137
+ className,
136
138
  defaultOpen = true,
137
139
  modal = false,
138
140
  onOpenChange,
139
141
  open,
142
+ role = "menu",
143
+ width,
144
+ asChild = false,
140
145
  ...props
141
146
  }: MenuProps) {
142
- const rootProps =
143
- open === undefined
144
- ? { defaultOpen, onOpenChange }
145
- : { open, onOpenChange }
147
+ const Comp = asChild ? Slot : "div"
148
+ const isOpen = open ?? defaultOpen
149
+
150
+ void modal
151
+ void onOpenChange
152
+
153
+ if (!isOpen) return null
146
154
 
147
155
  return (
148
- <MenuRoot modal={modal} {...rootProps}>
149
- <MenuContent forceMount {...props}>
156
+ <MenuStaticContext.Provider value>
157
+ <Comp
158
+ data-slot="menu"
159
+ data-width={width ?? "menu"}
160
+ role={role}
161
+ className={cn(menuVariants({ width, className }))}
162
+ {...props}
163
+ >
150
164
  {children}
151
- </MenuContent>
152
- </MenuRoot>
165
+ </Comp>
166
+ </MenuStaticContext.Provider>
153
167
  )
154
168
  }
155
169
 
@@ -188,9 +202,30 @@ const MenuItem = React.forwardRef<HTMLDivElement, MenuItemProps>(function MenuIt
188
202
  ...props
189
203
  }, ref) {
190
204
  const Comp = asChild ? Slot : "div"
205
+ const isStaticMenu = React.useContext(MenuStaticContext)
191
206
  const selectedKind: MenuItemKind = kind ?? "default"
192
207
  const isDisabled = disabled
193
208
 
209
+ const item = (
210
+ <Comp
211
+ aria-disabled={isDisabled ? true : undefined}
212
+ data-disabled={isDisabled ? "true" : "false"}
213
+ data-kind={selectedKind}
214
+ data-slot="menu-item"
215
+ ref={ref}
216
+ role={role}
217
+ tabIndex={isDisabled ? -1 : (tabIndex ?? 0)}
218
+ className={cn(menuItemVariants({ kind: selectedKind, className }))}
219
+ {...props}
220
+ >
221
+ <MenuItemContent kind={selectedKind}>
222
+ <MenuItemChildren kind={selectedKind}>{children}</MenuItemChildren>
223
+ </MenuItemContent>
224
+ </Comp>
225
+ )
226
+
227
+ if (isStaticMenu) return item
228
+
194
229
  return (
195
230
  <DropdownMenuPrimitive.Item
196
231
  asChild
@@ -198,21 +233,7 @@ const MenuItem = React.forwardRef<HTMLDivElement, MenuItemProps>(function MenuIt
198
233
  onSelect={onSelect}
199
234
  textValue={textValue ?? getMenuItemTextValue(children)}
200
235
  >
201
- <Comp
202
- aria-disabled={isDisabled ? true : undefined}
203
- data-disabled={isDisabled ? "true" : "false"}
204
- data-kind={selectedKind}
205
- data-slot="menu-item"
206
- ref={ref}
207
- role={role}
208
- tabIndex={isDisabled ? -1 : (tabIndex ?? 0)}
209
- className={cn(menuItemVariants({ kind: selectedKind, className }))}
210
- {...props}
211
- >
212
- <MenuItemContent kind={selectedKind}>
213
- <MenuItemChildren kind={selectedKind}>{children}</MenuItemChildren>
214
- </MenuItemContent>
215
- </Comp>
236
+ {item}
216
237
  </DropdownMenuPrimitive.Item>
217
238
  )
218
239
  })
@@ -620,42 +641,45 @@ function MenuLabel({
620
641
  role = "presentation",
621
642
  ...props
622
643
  }: MenuLabelProps) {
623
- return (
624
- <DropdownMenuPrimitive.Label asChild>
625
- <div
626
- data-slot="menu-label"
627
- role={role}
628
- className={cn(
629
- "flex h-[var(--bh-menu-item-label-height)] w-full items-center gap-0 bg-[var(--bh-menu-item-bg)] px-[var(--bh-menu-label-padding-x)] py-[var(--bh-space-xs-4)] text-[length:var(--bh-text-body-xs-regular-font-size)] leading-[var(--bh-text-body-xs-regular-line-height)] tracking-[var(--bh-text-body-xs-regular-letter-spacing)]",
630
- className
631
- )}
632
- {...props}
633
- >
634
- {children ?? (
635
- <>
636
- {hasRenderableContent(label) ? (
637
- <span
638
- data-slot="menu-label-primary"
639
- dir="auto"
640
- className="min-w-0 flex-1 truncate text-start font-[var(--bh-text-body-xs-medium-font-weight)] text-[var(--bh-content-default)]"
641
- >
642
- {label}
643
- </span>
644
- ) : null}
645
- {hasRenderableContent(caption) ? (
646
- <span
647
- data-slot="menu-label-caption"
648
- dir="auto"
649
- className="shrink-0 whitespace-nowrap text-end font-[var(--bh-text-body-xs-regular-font-weight)] text-[var(--bh-content-subtle)]"
650
- >
651
- {caption}
652
- </span>
653
- ) : null}
654
- </>
655
- )}
656
- </div>
657
- </DropdownMenuPrimitive.Label>
644
+ const isStaticMenu = React.useContext(MenuStaticContext)
645
+ const labelContent = (
646
+ <div
647
+ data-slot="menu-label"
648
+ role={role}
649
+ className={cn(
650
+ "flex h-[var(--bh-menu-item-label-height)] w-full items-center gap-0 bg-[var(--bh-menu-item-bg)] px-[var(--bh-menu-label-padding-x)] py-[var(--bh-space-xs-4)] text-[length:var(--bh-text-body-xs-regular-font-size)] leading-[var(--bh-text-body-xs-regular-line-height)] tracking-[var(--bh-text-body-xs-regular-letter-spacing)]",
651
+ className
652
+ )}
653
+ {...props}
654
+ >
655
+ {children ?? (
656
+ <>
657
+ {hasRenderableContent(label) ? (
658
+ <span
659
+ data-slot="menu-label-primary"
660
+ dir="auto"
661
+ className="min-w-0 flex-1 truncate text-start font-[var(--bh-text-body-xs-medium-font-weight)] text-[var(--bh-content-default)]"
662
+ >
663
+ {label}
664
+ </span>
665
+ ) : null}
666
+ {hasRenderableContent(caption) ? (
667
+ <span
668
+ data-slot="menu-label-caption"
669
+ dir="auto"
670
+ className="shrink-0 whitespace-nowrap text-end font-[var(--bh-text-body-xs-regular-font-weight)] text-[var(--bh-content-subtle)]"
671
+ >
672
+ {caption}
673
+ </span>
674
+ ) : null}
675
+ </>
676
+ )}
677
+ </div>
658
678
  )
679
+
680
+ if (isStaticMenu) return labelContent
681
+
682
+ return <DropdownMenuPrimitive.Label asChild>{labelContent}</DropdownMenuPrimitive.Label>
659
683
  }
660
684
 
661
685
  function hasRenderableContent(content: React.ReactNode) {
@@ -696,21 +720,24 @@ function MenuSeparator({
696
720
  role = "separator",
697
721
  ...props
698
722
  }: React.ComponentProps<"div">) {
699
- return (
700
- <DropdownMenuPrimitive.Separator asChild>
701
- <div
702
- data-slot="menu-separator"
703
- role={role}
704
- className={cn(
705
- "flex h-[var(--bh-menu-item-divider-height)] w-full items-center py-[var(--bh-menu-divider-padding-y)]",
706
- className
707
- )}
708
- {...props}
709
- >
710
- <span className="block w-full border-t border-[var(--bh-border-subtle)]" />
711
- </div>
712
- </DropdownMenuPrimitive.Separator>
723
+ const isStaticMenu = React.useContext(MenuStaticContext)
724
+ const separator = (
725
+ <div
726
+ data-slot="menu-separator"
727
+ role={role}
728
+ className={cn(
729
+ "flex h-[var(--bh-menu-item-divider-height)] w-full items-center py-[var(--bh-menu-divider-padding-y)]",
730
+ className
731
+ )}
732
+ {...props}
733
+ >
734
+ <span className="block w-full border-t border-[var(--bh-border-subtle)]" />
735
+ </div>
713
736
  )
737
+
738
+ if (isStaticMenu) return separator
739
+
740
+ return <DropdownMenuPrimitive.Separator asChild>{separator}</DropdownMenuPrimitive.Separator>
714
741
  }
715
742
 
716
743
  export {
@@ -1,5 +1,6 @@
1
1
  import * as React from "react"
2
2
  import { cva, type VariantProps } from "class-variance-authority"
3
+ import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react"
3
4
 
4
5
  import { cn } from "@/lib/utils"
5
6
 
@@ -444,29 +445,16 @@ function PaginationPageItem({
444
445
  }
445
446
 
446
447
  function PaginationArrowIcon({ type }: { type: "previous" | "next" }) {
448
+ const Icon = type === "previous" ? ChevronLeftIcon : ChevronRightIcon
449
+
447
450
  return (
448
- <svg
451
+ <Icon
449
452
  aria-hidden="true"
450
453
  data-rtl-flip="true"
451
454
  data-slot="pagination-icon"
452
- fill="none"
453
455
  focusable="false"
454
- viewBox="0 0 20 20"
455
- >
456
- {type === "previous" ? (
457
- <path
458
- d="M2.357 5.30332L6.48183 9.42807L5.30333 10.6066L0 5.30332L5.30333 0L6.48183 1.17851L2.357 5.30332Z"
459
- fill="currentColor"
460
- transform="translate(6.666 4.697)"
461
- />
462
- ) : (
463
- <path
464
- d="M4.12482 5.30332L0 1.17851L1.17852 0L6.48182 5.30332L1.17852 10.6066L0 9.42807L4.12482 5.30332Z"
465
- fill="currentColor"
466
- transform="translate(6.852 4.697)"
467
- />
468
- )}
469
- </svg>
456
+ strokeWidth={2.25}
457
+ />
470
458
  )
471
459
  }
472
460
 
@@ -1,5 +1,6 @@
1
1
  import * as React from "react"
2
2
  import { cva, type VariantProps } from "class-variance-authority"
3
+ import { Building2Icon, CreditCardIcon, UserIcon } from "lucide-react"
3
4
 
4
5
  import { cn } from "@/lib/utils"
5
6
 
@@ -87,8 +88,16 @@ function RadioCard({
87
88
  inputProps,
88
89
  ...props
89
90
  }: RadioCardProps) {
91
+ const hasControlledChecked =
92
+ checked !== undefined || inputProps?.checked !== undefined
93
+ const controlledChecked = checked ?? inputProps?.checked
90
94
  const inputChecked =
91
- checked ?? inputProps?.checked ?? Boolean(inputProps?.defaultChecked)
95
+ controlledChecked ?? Boolean(inputProps?.defaultChecked)
96
+ const inputReadOnly =
97
+ inputProps?.readOnly ??
98
+ (hasControlledChecked && inputProps?.onChange === undefined
99
+ ? true
100
+ : undefined)
92
101
  const isDisabled = disabled ?? inputProps?.disabled ?? state === "disabled"
93
102
  const visualState: RadioCardState = isDisabled
94
103
  ? "disabled"
@@ -112,9 +121,11 @@ function RadioCard({
112
121
  <input
113
122
  aria-label={typeof textLabel === "string" ? textLabel : undefined}
114
123
  {...inputProps}
115
- checked={inputProps?.checked ?? checked}
124
+ checked={hasControlledChecked ? controlledChecked : undefined}
116
125
  className={cn("sr-only", inputProps?.className)}
126
+ defaultChecked={hasControlledChecked ? undefined : inputProps?.defaultChecked}
117
127
  disabled={isDisabled}
128
+ readOnly={inputReadOnly}
118
129
  type="radio"
119
130
  />
120
131
  <span
@@ -550,17 +561,11 @@ function RadioCardMedia({
550
561
 
551
562
  function RadioCardUserIcon() {
552
563
  return (
553
- <svg
564
+ <UserIcon
554
565
  aria-hidden="true"
555
566
  className="size-[var(--bh-space-4xl-20)]"
556
- fill="none"
557
- viewBox="0 0 20 20"
558
- >
559
- <path
560
- d="M3.333 18.333c0-3.681 2.985-6.666 6.667-6.666s6.667 2.985 6.667 6.666H15c0-2.761-2.239-5-5-5s-5 2.239-5 5H3.333ZM10 10.833a5 5 0 1 1 0-10 5 5 0 0 1 0 10Zm0-1.666a3.333 3.333 0 1 0 0-6.667 3.333 3.333 0 0 0 0 6.667Z"
561
- fill="currentColor"
562
- />
563
- </svg>
567
+ strokeWidth={2}
568
+ />
564
569
  )
565
570
  }
566
571
 
@@ -568,21 +573,13 @@ function RadioCardPaymentIcon() {
568
573
  return (
569
574
  <span
570
575
  data-slot="radio-card-payment-icon"
571
- className="flex h-[var(--bh-radio-card-payment-height)] w-[var(--bh-radio-card-payment-width)] shrink-0 items-center justify-center rounded-[var(--bh-radius-md-6)] bg-[var(--bh-bg-always-white)] shadow-[inset_0_0_0_var(--bh-border-width-default)_var(--bh-border-subtle)]"
576
+ className="flex h-[var(--bh-radio-card-payment-height)] w-[var(--bh-radio-card-payment-width)] shrink-0 items-center justify-center rounded-[var(--bh-radius-md-6)] bg-[var(--bh-bg-always-white)] text-[var(--bh-radio-card-payment-logo-color)] shadow-[inset_0_0_0_var(--bh-border-width-default)_var(--bh-border-subtle)]"
572
577
  >
573
- <svg
578
+ <CreditCardIcon
574
579
  aria-hidden="true"
575
580
  className="h-[var(--bh-radio-card-payment-logo-height)] w-[var(--bh-radio-card-payment-logo-width)]"
576
- fill="none"
577
- viewBox="0 0 33 11"
578
- >
579
- <path
580
- clipRule="evenodd"
581
- d="M8.05 10.344H5.263L3.174 2.256c-.1-.372-.31-.7-.62-.856C1.78 1.011.852.701 0 .544V.233h4.489c.62 0 1.084.468 1.162 1.011l1.084 5.834L9.52.233h2.71L8.05 10.344Zm5.728 0h-2.632L13.313.233h2.632l-2.167 10.111Zm5.572-7.31c.078-.544.542-.855 1.085-.855.852-.078 1.78.078 2.554.467l.465-2.177A7.09 7.09 0 0 0 21.055 0c-2.554 0-4.413 1.401-4.413 3.344 0 1.479 1.317 2.255 2.246 2.723 1.006.467 1.393.778 1.315 1.244 0 .7-.774 1.011-1.547 1.011-.93 0-1.86-.233-2.71-.622l-.465 2.178c.93.388 1.935.545 2.864.545 2.864.076 4.644-1.323 4.644-3.423 0-2.645-3.639-2.8-3.639-3.966Zm12.85 7.31L30.11.233h-2.245c-.464 0-.929.311-1.084.778l-3.87 9.333h2.71l.541-1.477h3.329l.31 1.477H32.2ZM28.253 2.956l.773 3.811H26.86l1.394-3.811Z"
582
- fill="var(--bh-radio-card-payment-logo-color)"
583
- fillRule="evenodd"
584
- />
585
- </svg>
581
+ strokeWidth={2}
582
+ />
586
583
  </span>
587
584
  )
588
585
  }
@@ -610,14 +607,11 @@ function RadioCardCompanyIcon({ disabled }: { disabled: boolean }) {
610
607
  disabled && "opacity-[var(--bh-opacity-disabled-media)]"
611
608
  )}
612
609
  >
613
- <svg aria-hidden="true" className="size-full" fill="none" viewBox="0 0 40 40">
614
- <path
615
- clipRule="evenodd"
616
- d="M20.5 37.5c9.941 0 18-8.059 18-18s-8.059-18-18-18-18 8.059-18 18 8.059 18 18 18Zm2.778-22.093a3.929 3.929 0 0 0 0-5.556 3.929 3.929 0 0 0-5.556 0 3.929 3.929 0 0 0 0 5.556l2.778 2.778 2.778-2.778Zm1.315 6.871a3.929 3.929 0 0 0 5.556 0 3.929 3.929 0 0 0 0-5.556 3.929 3.929 0 0 0-5.556 0L21.815 19.5l2.778 2.778Zm-1.315 6.871a3.929 3.929 0 0 0 0-5.556L20.5 20.815l-2.778 2.778a3.929 3.929 0 0 0 0 5.556 3.929 3.929 0 0 0 5.556 0Zm-12.427-6.871a3.929 3.929 0 0 1 0-5.556 3.929 3.929 0 0 1 5.556 0L19.185 19.5l-2.778 2.778a3.929 3.929 0 0 1-5.556 0Z"
617
- fill="var(--bh-radio-card-company-mark-color)"
618
- fillRule="evenodd"
619
- />
620
- </svg>
610
+ <Building2Icon
611
+ aria-hidden="true"
612
+ className="size-[var(--bh-space-5xl-24)] text-[var(--bh-radio-card-company-mark-color)]"
613
+ strokeWidth={1.8}
614
+ />
621
615
  </span>
622
616
  )
623
617
  }