@tinybigui/react 0.7.0 → 0.8.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/dist/index.js CHANGED
@@ -2665,205 +2665,132 @@ var TextField = forwardRef(
2665
2665
  }
2666
2666
  );
2667
2667
  TextField.displayName = "TextField";
2668
- var checkboxVariants = cva(
2669
- [
2670
- // Base classes (always applied to label wrapper)
2671
- "relative inline-flex items-center cursor-pointer select-none",
2672
- "transition-opacity duration-200"
2673
- ],
2674
- {
2675
- variants: {
2676
- /**
2677
- * Disabled state
2678
- */
2679
- disabled: {
2680
- true: "opacity-38 cursor-not-allowed pointer-events-none",
2681
- false: ""
2682
- }
2683
- },
2684
- defaultVariants: {
2685
- disabled: false
2686
- }
2687
- }
2688
- );
2689
- var checkboxContainerVariants = cva(
2690
- [
2691
- // Base classes for checkbox visual container
2692
- "relative inline-flex items-center justify-center m-1",
2693
- "w-10 h-10",
2694
- // 40x40dp touch target (MD3 spec)
2695
- "flex-shrink-0",
2696
- "transition-all duration-200",
2697
- // State layer (hover, focus, active) - MD3 spec: 8%/12%/12% opacity
2698
- "before:absolute before:inset-0 before:rounded-full before:transition-opacity before:duration-200",
2699
- "before:bg-current before:opacity-0",
2700
- "hover:before:opacity-8",
2701
- "active:before:opacity-12"
2702
- ],
2703
- {
2704
- variants: {
2705
- /**
2706
- * Checkbox state (determines visual appearance)
2707
- */
2708
- state: {
2709
- unchecked: "text-on-surface-variant",
2710
- checked: "text-primary",
2711
- indeterminate: "text-primary"
2712
- },
2713
- /**
2714
- * Error/invalid state
2715
- */
2716
- isInvalid: {
2717
- true: "text-error",
2718
- false: ""
2719
- },
2720
- /**
2721
- * Disabled state
2722
- */
2723
- disabled: {
2724
- true: "text-on-surface pointer-events-none",
2725
- false: ""
2726
- }
2727
- },
2728
- compoundVariants: [
2729
- // Error state overrides normal colors for all states
2730
- {
2731
- state: "unchecked",
2732
- isInvalid: true,
2733
- disabled: false,
2734
- className: "text-error"
2735
- },
2736
- {
2737
- state: "checked",
2738
- isInvalid: true,
2739
- disabled: false,
2740
- className: "text-error"
2741
- },
2742
- {
2743
- state: "indeterminate",
2744
- isInvalid: true,
2745
- disabled: false,
2746
- className: "text-error"
2747
- }
2748
- ],
2749
- defaultVariants: {
2750
- state: "unchecked",
2751
- isInvalid: false,
2752
- disabled: false
2753
- }
2754
- }
2755
- );
2756
- var checkboxIconBoxVariants = cva(
2757
- [
2758
- // Base classes for the checkbox box
2759
- // Note: Border radius is applied via SVG rx/ry attributes (2dp) in the component
2760
- "transition-all duration-200"
2761
- ],
2762
- {
2763
- variants: {
2764
- /**
2765
- * Checkbox state
2766
- */
2767
- state: {
2768
- unchecked: [
2769
- "fill-transparent",
2770
- "stroke-outline",
2771
- // MD3: outline color for unchecked
2772
- "stroke-4"
2773
- // MD3: 2dp outline width
2774
- ],
2775
- checked: [
2776
- "fill-current",
2777
- // Uses parent text color (primary or error)
2778
- "stroke-none"
2779
- ],
2780
- indeterminate: [
2781
- "fill-current",
2782
- // Uses parent text color (primary or error)
2783
- "stroke-none"
2784
- ]
2785
- },
2786
- /**
2787
- * Disabled state
2788
- */
2789
- disabled: {
2790
- true: ["fill-transparent", "stroke-current", "stroke-2"],
2791
- false: ""
2792
- }
2793
- },
2794
- compoundVariants: [
2795
- // Disabled state overrides fill for checked/indeterminate
2796
- {
2797
- state: "checked",
2798
- disabled: true,
2799
- className: "fill-current stroke-none"
2800
- },
2801
- {
2802
- state: "indeterminate",
2803
- disabled: true,
2804
- className: "fill-current stroke-none"
2805
- }
2806
- ],
2807
- defaultVariants: {
2808
- state: "unchecked",
2809
- disabled: false
2810
- }
2811
- }
2812
- );
2813
- var checkboxIconVariants = cva(
2814
- [
2815
- "fill-current",
2816
- // Inherits color from parent
2817
- "transition-all duration-200"
2818
- ],
2819
- {
2820
- variants: {
2821
- /**
2822
- * Icon type
2823
- */
2824
- type: {
2825
- check: "",
2826
- // Checkmark icon
2827
- dash: ""
2828
- // Dash/minus icon
2829
- }
2830
- },
2831
- defaultVariants: {
2832
- type: "check"
2668
+ var checkboxRootVariants = cva([
2669
+ "relative inline-flex items-center cursor-pointer select-none",
2670
+ "data-[disabled]:cursor-not-allowed data-[disabled]:pointer-events-none",
2671
+ "data-[disabled]:opacity-38"
2672
+ ]);
2673
+ var checkboxControlVariants = cva([
2674
+ "relative flex items-center justify-center flex-shrink-0",
2675
+ "w-10 h-10 rounded-full",
2676
+ "overflow-hidden"
2677
+ ]);
2678
+ var checkboxStateLayerVariants = cva([
2679
+ "absolute inset-0 rounded-full pointer-events-none opacity-0",
2680
+ // Base state-layer color (unselected)
2681
+ "bg-on-surface",
2682
+ // Effects transition for opacity changes
2683
+ "transition-opacity duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
2684
+ // Selected / indeterminate → primary color
2685
+ "group-data-[selected]/checkbox:bg-primary",
2686
+ "group-data-[indeterminate]/checkbox:bg-primary",
2687
+ // Invalid → error color (singly-chained)
2688
+ "group-data-[invalid]/checkbox:bg-error",
2689
+ // Invalid + selected → error wins (doubly-chained for specificity)
2690
+ "group-data-[invalid]/checkbox:group-data-[selected]/checkbox:bg-error",
2691
+ // Interaction opacities (MD3: hover 8%, focus-visible 10%, pressed 10%)
2692
+ "group-data-[hovered]/checkbox:opacity-8",
2693
+ "group-data-[focus-visible]/checkbox:opacity-10",
2694
+ "group-data-[pressed]/checkbox:opacity-10",
2695
+ // No state layer when disabled
2696
+ "group-data-[disabled]/checkbox:hidden"
2697
+ ]);
2698
+ var checkboxFocusRingVariants = cva([
2699
+ "pointer-events-none absolute inset-0 rounded-full",
2700
+ "outline outline-2 outline-offset-0 outline-secondary",
2701
+ "transition-opacity duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
2702
+ "opacity-0",
2703
+ "group-data-[focus-visible]/checkbox:opacity-100"
2704
+ ]);
2705
+ var checkboxBoxVariants = cva([
2706
+ // Base geometry
2707
+ "relative flex items-center justify-center flex-shrink-0",
2708
+ "w-[18px] h-[18px] rounded-[2px] border-2",
2709
+ // Effects transition for color changes
2710
+ "transition-[background-color,border-color] duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
2711
+ // ── Unselected base ───────────────────────────────────────────────────────
2712
+ "bg-transparent border-outline",
2713
+ // ── Selected ──────────────────────────────────────────────────────────────
2714
+ "group-data-[selected]/checkbox:bg-primary group-data-[selected]/checkbox:border-primary",
2715
+ // ── Indeterminate ─────────────────────────────────────────────────────────
2716
+ "group-data-[indeterminate]/checkbox:bg-primary group-data-[indeterminate]/checkbox:border-primary",
2717
+ // ── Invalid (unselected) — singly chained ─────────────────────────────────
2718
+ "group-data-[invalid]/checkbox:border-error",
2719
+ // ── Invalid + selected — doubly chained (higher specificity) ──────────────
2720
+ "group-data-[invalid]/checkbox:group-data-[selected]/checkbox:bg-error",
2721
+ "group-data-[invalid]/checkbox:group-data-[selected]/checkbox:border-error",
2722
+ // ── Invalid + indeterminate — doubly chained ───────────────────────────────
2723
+ "group-data-[invalid]/checkbox:group-data-[indeterminate]/checkbox:bg-error",
2724
+ "group-data-[invalid]/checkbox:group-data-[indeterminate]/checkbox:border-error",
2725
+ // ── Disabled (placed last — cascade wins over interaction colors) ──────────
2726
+ "group-data-[disabled]/checkbox:border-on-surface/38",
2727
+ "group-data-[disabled]/checkbox:bg-transparent",
2728
+ // ── Disabled + selected — doubly chained ──────────────────────────────────
2729
+ "group-data-[selected]/checkbox:group-data-[disabled]/checkbox:bg-on-surface/38",
2730
+ "group-data-[selected]/checkbox:group-data-[disabled]/checkbox:border-transparent",
2731
+ // ── Disabled + indeterminate — doubly chained ─────────────────────────────
2732
+ "group-data-[indeterminate]/checkbox:group-data-[disabled]/checkbox:bg-on-surface/38",
2733
+ "group-data-[indeterminate]/checkbox:group-data-[disabled]/checkbox:border-transparent"
2734
+ ]);
2735
+ var checkboxIconVariants = cva([
2736
+ "absolute inset-0 flex items-center justify-center",
2737
+ // Spatial transition for icon appearing (scale)
2738
+ "transition-transform duration-spring-standard-fast-spatial ease-spring-standard-fast-spatial",
2739
+ // Base icon color
2740
+ "text-on-primary",
2741
+ // Invalid → on-error (doubly-chained for both selected and indeterminate)
2742
+ "group-data-[invalid]/checkbox:group-data-[selected]/checkbox:text-on-error",
2743
+ "group-data-[invalid]/checkbox:group-data-[indeterminate]/checkbox:text-on-error",
2744
+ // Disabled → surface (doubly-chained)
2745
+ "group-data-[selected]/checkbox:group-data-[disabled]/checkbox:text-surface",
2746
+ "group-data-[indeterminate]/checkbox:group-data-[disabled]/checkbox:text-surface"
2747
+ ]);
2748
+ var checkboxLabelVariants = cva(["text-body-large text-on-surface select-none ml-4"]);
2749
+ function CheckboxCheckIcon() {
2750
+ return /* @__PURE__ */ jsx(
2751
+ "svg",
2752
+ {
2753
+ xmlns: "http://www.w3.org/2000/svg",
2754
+ width: "18",
2755
+ height: "18",
2756
+ viewBox: "0 0 18 18",
2757
+ fill: "none",
2758
+ "aria-hidden": "true",
2759
+ children: /* @__PURE__ */ jsx(
2760
+ "path",
2761
+ {
2762
+ d: "M4.5 9L7.2 11.7L13.5 5.4",
2763
+ stroke: "currentColor",
2764
+ strokeWidth: "2",
2765
+ strokeLinecap: "square",
2766
+ strokeLinejoin: "miter"
2767
+ }
2768
+ )
2833
2769
  }
2834
- }
2835
- );
2836
- var checkboxLabelVariants = cva(
2837
- [
2838
- "text-sm",
2839
- // MD3: Body Medium (14px)
2840
- "text-on-surface",
2841
- "select-none"
2842
- ],
2843
- {
2844
- variants: {
2845
- disabled: {
2846
- true: "",
2847
- false: ""
2848
- }
2849
- },
2850
- defaultVariants: {
2851
- disabled: false
2770
+ );
2771
+ }
2772
+ function CheckboxIndeterminateIcon() {
2773
+ return /* @__PURE__ */ jsx(
2774
+ "svg",
2775
+ {
2776
+ xmlns: "http://www.w3.org/2000/svg",
2777
+ width: "18",
2778
+ height: "18",
2779
+ viewBox: "0 0 18 18",
2780
+ fill: "none",
2781
+ "aria-hidden": "true",
2782
+ children: /* @__PURE__ */ jsx("rect", { x: "4", y: "8", width: "10", height: "2", rx: "1", fill: "currentColor" })
2852
2783
  }
2853
- }
2854
- );
2784
+ );
2785
+ }
2855
2786
  var Checkbox = forwardRef(
2856
2787
  ({
2857
- // Content props
2858
2788
  children,
2859
- // State props
2860
2789
  isIndeterminate = false,
2861
2790
  isInvalid = false,
2862
2791
  disableRipple = false,
2863
2792
  isDisabled = false,
2864
- // Styling
2865
2793
  className,
2866
- // Other props
2867
2794
  ...props
2868
2795
  }, forwardedRef) => {
2869
2796
  const internalRef = useRef(null);
@@ -2876,17 +2803,17 @@ var Checkbox = forwardRef(
2876
2803
  "data-testid": _dataTestId,
2877
2804
  id: _htmlId,
2878
2805
  title: _htmlTitle,
2879
- ...restPropsWithoutHtmlAttrs
2806
+ ...ariaProps
2880
2807
  } = props;
2881
- const state = useToggleState(restPropsWithoutHtmlAttrs);
2882
- const { inputProps, labelProps } = useCheckbox(
2883
- restPropsWithoutHtmlAttrs,
2808
+ const state = useToggleState(ariaProps);
2809
+ const { inputProps, labelProps, isPressed } = useCheckbox(
2810
+ ariaProps,
2884
2811
  state,
2885
2812
  ref
2886
2813
  );
2887
2814
  const { isFocusVisible, focusProps } = useFocusRing();
2815
+ const { isHovered, hoverProps } = useHover({ isDisabled });
2888
2816
  const isSelected = state.isSelected;
2889
- const visualState = isIndeterminate ? "indeterminate" : isSelected ? "checked" : "unchecked";
2890
2817
  const { onMouseDown: handleRipple, ripples } = useRipple({
2891
2818
  disabled: isDisabled || disableRipple
2892
2819
  });
@@ -2896,8 +2823,8 @@ var Checkbox = forwardRef(
2896
2823
  }
2897
2824
  }, [isIndeterminate, ref]);
2898
2825
  if (process.env.NODE_ENV === "development") {
2899
- const ariaProps = restPropsWithoutHtmlAttrs;
2900
- if (!children && !ariaProps["aria-label"] && !ariaProps["aria-labelledby"]) {
2826
+ const a = ariaProps;
2827
+ if (!children && !a["aria-label"] && !a["aria-labelledby"]) {
2901
2828
  console.warn(
2902
2829
  "[Checkbox] Checkbox should have a label (children) or aria-label for accessibility."
2903
2830
  );
@@ -2906,105 +2833,40 @@ var Checkbox = forwardRef(
2906
2833
  return /* @__PURE__ */ jsxs(
2907
2834
  "label",
2908
2835
  {
2909
- ...labelProps,
2910
- className: cn(
2911
- checkboxVariants({
2912
- disabled: isDisabled
2913
- }),
2914
- className
2915
- ),
2836
+ ...mergeProps$1(labelProps, hoverProps),
2837
+ className: cn(checkboxRootVariants(), "group/checkbox", className),
2916
2838
  "data-testid": dataTestId,
2917
2839
  title: htmlTitle,
2840
+ ...getInteractionDataAttributes({
2841
+ isHovered,
2842
+ isFocusVisible,
2843
+ isPressed,
2844
+ isSelected,
2845
+ isDisabled,
2846
+ isInvalid,
2847
+ isIndeterminate,
2848
+ isReadOnly: ariaProps.isReadOnly ?? false
2849
+ }),
2918
2850
  children: [
2919
2851
  /* @__PURE__ */ jsx(VisuallyHidden, { children: /* @__PURE__ */ jsx("input", { ...mergeProps$1(inputProps, focusProps), ref, id: htmlId }) }),
2920
2852
  /* @__PURE__ */ jsxs(
2921
2853
  "div",
2922
2854
  {
2923
2855
  role: "presentation",
2924
- className: cn(
2925
- checkboxContainerVariants({
2926
- state: visualState,
2927
- isInvalid,
2928
- disabled: isDisabled
2929
- })
2930
- ),
2856
+ className: cn(checkboxControlVariants()),
2931
2857
  onMouseDown: handleRipple,
2932
2858
  children: [
2933
2859
  ripples,
2934
- /* @__PURE__ */ jsxs(
2935
- "svg",
2936
- {
2937
- width: "18",
2938
- height: "18",
2939
- viewBox: "0 0 18 18",
2940
- "aria-hidden": "true",
2941
- className: "relative z-10",
2942
- children: [
2943
- /* @__PURE__ */ jsx(
2944
- "rect",
2945
- {
2946
- x: "0",
2947
- y: "0",
2948
- width: "18",
2949
- height: "18",
2950
- rx: "2",
2951
- ry: "2",
2952
- className: cn(
2953
- checkboxIconBoxVariants({
2954
- state: visualState,
2955
- disabled: isDisabled
2956
- })
2957
- )
2958
- }
2959
- ),
2960
- isSelected && !isIndeterminate && /* @__PURE__ */ jsx(
2961
- "path",
2962
- {
2963
- d: "M14.1 4.5L6.3 12.3l-3.4-3.4L1.5 10.3l4.8 4.8 9.2-9.2z",
2964
- className: cn(checkboxIconVariants({ type: "check" }), "fill-on-primary")
2965
- }
2966
- ),
2967
- isIndeterminate && /* @__PURE__ */ jsx(
2968
- "rect",
2969
- {
2970
- x: "4",
2971
- y: "8",
2972
- width: "10",
2973
- height: "2",
2974
- className: cn(checkboxIconVariants({ type: "dash" }), "fill-on-primary")
2975
- }
2976
- ),
2977
- isFocusVisible && /* @__PURE__ */ jsx(
2978
- "rect",
2979
- {
2980
- x: "-3",
2981
- y: "-3",
2982
- width: "24",
2983
- height: "24",
2984
- rx: "12",
2985
- fill: "none",
2986
- stroke: "currentColor",
2987
- strokeWidth: "2",
2988
- className: "animate-pulse"
2989
- }
2990
- )
2991
- ]
2992
- }
2993
- )
2860
+ /* @__PURE__ */ jsx("span", { className: cn(checkboxStateLayerVariants()), "aria-hidden": "true" }),
2861
+ /* @__PURE__ */ jsx("span", { className: cn(checkboxFocusRingVariants()), "aria-hidden": "true" }),
2862
+ /* @__PURE__ */ jsxs("div", { className: cn(checkboxBoxVariants()), "aria-hidden": "true", children: [
2863
+ isSelected && !isIndeterminate && /* @__PURE__ */ jsx("span", { className: cn(checkboxIconVariants()), children: /* @__PURE__ */ jsx(CheckboxCheckIcon, {}) }),
2864
+ isIndeterminate && /* @__PURE__ */ jsx("span", { className: cn(checkboxIconVariants()), children: /* @__PURE__ */ jsx(CheckboxIndeterminateIcon, {}) })
2865
+ ] })
2994
2866
  ]
2995
2867
  }
2996
2868
  ),
2997
- children && /* @__PURE__ */ jsx(
2998
- "span",
2999
- {
3000
- className: cn(
3001
- checkboxLabelVariants({
3002
- disabled: isDisabled
3003
- })
3004
- ),
3005
- children
3006
- }
3007
- )
2869
+ children && /* @__PURE__ */ jsx("span", { className: cn(checkboxLabelVariants()), children })
3008
2870
  ]
3009
2871
  }
3010
2872
  );
@@ -5065,106 +4927,48 @@ var Drawer = forwardRef(
5065
4927
  }
5066
4928
  );
5067
4929
  Drawer.displayName = "Drawer";
5068
- var BadgeHeadless = forwardRef(
5069
- ({ className, children, ...props }, ref) => {
5070
- return /* @__PURE__ */ jsx("div", { ref, className: cn("relative inline-flex", className), ...props, children });
5071
- }
5072
- );
5073
- BadgeHeadless.displayName = "BadgeHeadless";
5074
- var badgeVariants2 = cva(
5075
- ["absolute -top-1 -right-1 rounded-full flex items-center justify-center"],
5076
- {
5077
- variants: {
5078
- size: {
5079
- small: "size-1.5",
5080
- large: "min-w-4 h-4 px-1 text-label-small"
5081
- },
5082
- color: {
5083
- error: "bg-error text-on-error",
5084
- primary: "bg-primary text-on-primary"
5085
- },
5086
- invisible: {
5087
- true: "scale-0 opacity-0",
5088
- false: "scale-100 opacity-100"
5089
- },
5090
- reducedMotion: {
5091
- true: "",
5092
- false: "transition-[transform,opacity] duration-spring-standard-fast-effects ease-spring-standard-fast-effects"
5093
- }
5094
- },
5095
- defaultVariants: {
5096
- size: "large",
5097
- color: "error",
5098
- invisible: false,
5099
- reducedMotion: false
5100
- }
5101
- }
5102
- );
5103
- var getDisplayValue = (count, max) => {
4930
+ var badgeAppearance = [
4931
+ // ── Shape ─────────────────────────────────────────────────────────────────────
4932
+ "flex items-center justify-center",
4933
+ "rounded-full",
4934
+ // ── Large (count) sizing — base defaults ──────────────────────────────────────
4935
+ // Height 16dp, min-width 16dp, horizontal padding 4dp
4936
+ "h-4 min-w-4 px-1",
4937
+ // ── Color error role (only MD3-spec role for badges) ────────────────────────
4938
+ "bg-error text-on-error",
4939
+ // ── Typography — label-small, tight leading, tabular numbers ──────────────────
4940
+ "text-label-small leading-none tabular-nums",
4941
+ // ── Visibility (runtime flag) ──────────────────────────────────────────────────
4942
+ // Base: fully visible
4943
+ "scale-100",
4944
+ // data-invisible: scale to zero (visually hidden; aria-label still readable by SR)
4945
+ "data-[invisible]:scale-0",
4946
+ // ── Dot content flag overrides (placed last — cascade wins over base sizing) ───
4947
+ // Clear out the count-pill sizing, set 6dp circle
4948
+ "data-[dot]:size-1.5",
4949
+ "data-[dot]:min-w-0",
4950
+ "data-[dot]:p-0",
4951
+ "data-[dot]:text-[0]"
4952
+ // suppress any stray text rendering on dot
4953
+ ];
4954
+ var badgeVariants2 = cva([
4955
+ // ── Anchored placement — badge center on host's top-right corner ──────────────
4956
+ // top-0 right-0 places the badge's own top-right at the host's top-right,
4957
+ // then the 1/2-element translate moves the badge center onto that corner.
4958
+ // Host-size-agnostic: works for any wrapped element (icon, avatar, nav chip).
4959
+ "absolute top-0 right-0 -translate-y-1/2 translate-x-1/2",
4960
+ ...badgeAppearance
4961
+ ]);
4962
+ var badgeStaticVariants = cva(["inline-flex", ...badgeAppearance]);
4963
+ function isBadgeConfig(badge) {
4964
+ return typeof badge === "object" && badge !== null && !isValidElement(badge) && "count" in badge;
4965
+ }
4966
+ function getBadgeDisplayValue(count, max) {
5104
4967
  if (count === void 0) return "";
5105
4968
  return count > max ? `${max}+` : count.toString();
5106
- };
5107
- var getAriaLabel = (count, override) => {
5108
- if (override) return override;
4969
+ }
4970
+ function getBadgeAriaLabel(count) {
5109
4971
  return count === void 0 ? "New" : `${count} notifications`;
5110
- };
5111
- var BadgeContent = forwardRef(
5112
- ({
5113
- count,
5114
- max = 999,
5115
- color = "error",
5116
- invisible = false,
5117
- "aria-label": ariaLabelOverride,
5118
- reducedMotion = false,
5119
- className
5120
- }, ref) => {
5121
- const size = count === void 0 ? "small" : "large";
5122
- const displayValue = getDisplayValue(count, max);
5123
- const ariaLabel = getAriaLabel(count, ariaLabelOverride);
5124
- return /* @__PURE__ */ jsx(
5125
- "span",
5126
- {
5127
- ref,
5128
- role: "status",
5129
- "aria-label": ariaLabel,
5130
- className: cn(badgeVariants2({ size, color, invisible, reducedMotion }), className),
5131
- children: displayValue
5132
- }
5133
- );
5134
- }
5135
- );
5136
- BadgeContent.displayName = "BadgeContent";
5137
- var Badge = forwardRef(
5138
- ({
5139
- count,
5140
- max = 999,
5141
- color = "error",
5142
- invisible = false,
5143
- "aria-label": ariaLabel,
5144
- className,
5145
- children
5146
- }, ref) => {
5147
- const isReduced = useReducedMotion();
5148
- const shouldShow = !invisible && (count === void 0 || count > 0);
5149
- return /* @__PURE__ */ jsxs(BadgeHeadless, { ref, className, children: [
5150
- children,
5151
- /* @__PURE__ */ jsx(
5152
- BadgeContent,
5153
- {
5154
- count,
5155
- max,
5156
- color,
5157
- invisible: !shouldShow,
5158
- "aria-label": ariaLabel,
5159
- reducedMotion: isReduced
5160
- }
5161
- )
5162
- ] });
5163
- }
5164
- );
5165
- Badge.displayName = "Badge";
5166
- function isBadgeConfig(badge) {
5167
- return typeof badge === "object" && badge !== null && !isValidElement(badge) && ("count" in badge || "color" in badge);
5168
4972
  }
5169
4973
  var DrawerItem = forwardRef(
5170
4974
  ({
@@ -5192,12 +4996,18 @@ var DrawerItem = forwardRef(
5192
4996
  const renderBadge = () => {
5193
4997
  if (!badge) return null;
5194
4998
  if (isBadgeConfig(badge)) {
4999
+ const max = 999;
5000
+ const isDot = badge.count === void 0;
5001
+ const displayValue = getBadgeDisplayValue(badge.count, max);
5002
+ const ariaLabel = getBadgeAriaLabel(badge.count);
5195
5003
  return /* @__PURE__ */ jsx("span", { className: "relative z-10 ml-auto flex shrink-0 items-center pr-2", children: /* @__PURE__ */ jsx(
5196
- Badge,
5004
+ "span",
5197
5005
  {
5198
- ...badge.count !== void 0 ? { count: badge.count } : {},
5199
- ...badge.color !== void 0 ? { color: badge.color } : {},
5200
- children: /* @__PURE__ */ jsx("span", {})
5006
+ role: "status",
5007
+ "aria-label": ariaLabel,
5008
+ "data-dot": isDot ? "" : void 0,
5009
+ className: cn(badgeStaticVariants()),
5010
+ children: displayValue
5201
5011
  }
5202
5012
  ) });
5203
5013
  }
@@ -8696,6 +8506,75 @@ var Search = forwardRef(
8696
8506
  }
8697
8507
  );
8698
8508
  Search.displayName = "Search";
8509
+ var BadgeHeadless = forwardRef(
8510
+ ({ className, children, ...props }, ref) => {
8511
+ return /* @__PURE__ */ jsx("div", { ref, className: cn("relative inline-flex", className), ...props, children });
8512
+ }
8513
+ );
8514
+ BadgeHeadless.displayName = "BadgeHeadless";
8515
+ var getDisplayValue = (count, max) => {
8516
+ if (count === void 0) return "";
8517
+ return count > max ? `${max}+` : count.toString();
8518
+ };
8519
+ var getAriaLabel = (count, override) => {
8520
+ if (override) return override;
8521
+ return count === void 0 ? "New" : `${count} notifications`;
8522
+ };
8523
+ var BadgeContent = forwardRef(
8524
+ ({
8525
+ count,
8526
+ max = 999,
8527
+ invisible = false,
8528
+ "aria-label": ariaLabelOverride,
8529
+ reducedMotion = false,
8530
+ className
8531
+ }, ref) => {
8532
+ const isDot = count === void 0;
8533
+ const displayValue = getDisplayValue(count, max);
8534
+ const ariaLabel = getAriaLabel(count, ariaLabelOverride);
8535
+ return /* @__PURE__ */ jsx(
8536
+ "span",
8537
+ {
8538
+ ref,
8539
+ role: "status",
8540
+ "aria-label": ariaLabel,
8541
+ "data-dot": isDot ? "" : void 0,
8542
+ "data-invisible": invisible ? "" : void 0,
8543
+ className: cn(
8544
+ badgeVariants2(),
8545
+ // MD3 Expressive spatial motion for show/hide scale animation.
8546
+ // Spatial pairing: scale transform → expressive-fast-spatial token.
8547
+ // Guarded at the component level; do NOT use CSS-only reduced-motion
8548
+ // because this is a JS-conditional class, not a persistent transition.
8549
+ !reducedMotion && "duration-expressive-fast-spatial ease-expressive-fast-spatial transition-transform",
8550
+ className
8551
+ ),
8552
+ children: displayValue
8553
+ }
8554
+ );
8555
+ }
8556
+ );
8557
+ BadgeContent.displayName = "BadgeContent";
8558
+ var Badge = forwardRef(
8559
+ ({ count, max = 999, invisible = false, "aria-label": ariaLabel, className, children }, ref) => {
8560
+ const isReduced = useReducedMotion();
8561
+ const shouldShow = !invisible && (count === void 0 || count > 0);
8562
+ return /* @__PURE__ */ jsxs(BadgeHeadless, { ref, className, children: [
8563
+ children,
8564
+ /* @__PURE__ */ jsx(
8565
+ BadgeContent,
8566
+ {
8567
+ count,
8568
+ max,
8569
+ invisible: !shouldShow,
8570
+ "aria-label": ariaLabel,
8571
+ reducedMotion: isReduced
8572
+ }
8573
+ )
8574
+ ] });
8575
+ }
8576
+ );
8577
+ Badge.displayName = "Badge";
8699
8578
  var splitButtonContainerVariants = cva(
8700
8579
  ["inline-flex items-center rounded-full overflow-hidden"],
8701
8580
  {