@tinybigui/react 0.6.0 → 0.8.0

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.cjs CHANGED
@@ -5070,106 +5070,48 @@ var Drawer = React.forwardRef(
5070
5070
  }
5071
5071
  );
5072
5072
  Drawer.displayName = "Drawer";
5073
- var BadgeHeadless = React.forwardRef(
5074
- ({ className, children, ...props }, ref) => {
5075
- return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("relative inline-flex", className), ...props, children });
5076
- }
5077
- );
5078
- BadgeHeadless.displayName = "BadgeHeadless";
5079
- var badgeVariants2 = classVarianceAuthority.cva(
5080
- ["absolute -top-1 -right-1 rounded-full flex items-center justify-center"],
5081
- {
5082
- variants: {
5083
- size: {
5084
- small: "size-1.5",
5085
- large: "min-w-4 h-4 px-1 text-label-small"
5086
- },
5087
- color: {
5088
- error: "bg-error text-on-error",
5089
- primary: "bg-primary text-on-primary"
5090
- },
5091
- invisible: {
5092
- true: "scale-0 opacity-0",
5093
- false: "scale-100 opacity-100"
5094
- },
5095
- reducedMotion: {
5096
- true: "",
5097
- false: "transition-[transform,opacity] duration-spring-standard-fast-effects ease-spring-standard-fast-effects"
5098
- }
5099
- },
5100
- defaultVariants: {
5101
- size: "large",
5102
- color: "error",
5103
- invisible: false,
5104
- reducedMotion: false
5105
- }
5106
- }
5107
- );
5108
- var getDisplayValue = (count, max) => {
5073
+ var badgeAppearance = [
5074
+ // ── Shape ─────────────────────────────────────────────────────────────────────
5075
+ "flex items-center justify-center",
5076
+ "rounded-full",
5077
+ // ── Large (count) sizing — base defaults ──────────────────────────────────────
5078
+ // Height 16dp, min-width 16dp, horizontal padding 4dp
5079
+ "h-4 min-w-4 px-1",
5080
+ // ── Color error role (only MD3-spec role for badges) ────────────────────────
5081
+ "bg-error text-on-error",
5082
+ // ── Typography — label-small, tight leading, tabular numbers ──────────────────
5083
+ "text-label-small leading-none tabular-nums",
5084
+ // ── Visibility (runtime flag) ──────────────────────────────────────────────────
5085
+ // Base: fully visible
5086
+ "scale-100",
5087
+ // data-invisible: scale to zero (visually hidden; aria-label still readable by SR)
5088
+ "data-[invisible]:scale-0",
5089
+ // ── Dot content flag overrides (placed last — cascade wins over base sizing) ───
5090
+ // Clear out the count-pill sizing, set 6dp circle
5091
+ "data-[dot]:size-1.5",
5092
+ "data-[dot]:min-w-0",
5093
+ "data-[dot]:p-0",
5094
+ "data-[dot]:text-[0]"
5095
+ // suppress any stray text rendering on dot
5096
+ ];
5097
+ var badgeVariants2 = classVarianceAuthority.cva([
5098
+ // ── Anchored placement — badge center on host's top-right corner ──────────────
5099
+ // top-0 right-0 places the badge's own top-right at the host's top-right,
5100
+ // then the 1/2-element translate moves the badge center onto that corner.
5101
+ // Host-size-agnostic: works for any wrapped element (icon, avatar, nav chip).
5102
+ "absolute top-0 right-0 -translate-y-1/2 translate-x-1/2",
5103
+ ...badgeAppearance
5104
+ ]);
5105
+ var badgeStaticVariants = classVarianceAuthority.cva(["inline-flex", ...badgeAppearance]);
5106
+ function isBadgeConfig(badge) {
5107
+ return typeof badge === "object" && badge !== null && !React.isValidElement(badge) && "count" in badge;
5108
+ }
5109
+ function getBadgeDisplayValue(count, max) {
5109
5110
  if (count === void 0) return "";
5110
5111
  return count > max ? `${max}+` : count.toString();
5111
- };
5112
- var getAriaLabel = (count, override) => {
5113
- if (override) return override;
5112
+ }
5113
+ function getBadgeAriaLabel(count) {
5114
5114
  return count === void 0 ? "New" : `${count} notifications`;
5115
- };
5116
- var BadgeContent = React.forwardRef(
5117
- ({
5118
- count,
5119
- max = 999,
5120
- color = "error",
5121
- invisible = false,
5122
- "aria-label": ariaLabelOverride,
5123
- reducedMotion = false,
5124
- className
5125
- }, ref) => {
5126
- const size = count === void 0 ? "small" : "large";
5127
- const displayValue = getDisplayValue(count, max);
5128
- const ariaLabel = getAriaLabel(count, ariaLabelOverride);
5129
- return /* @__PURE__ */ jsxRuntime.jsx(
5130
- "span",
5131
- {
5132
- ref,
5133
- role: "status",
5134
- "aria-label": ariaLabel,
5135
- className: cn(badgeVariants2({ size, color, invisible, reducedMotion }), className),
5136
- children: displayValue
5137
- }
5138
- );
5139
- }
5140
- );
5141
- BadgeContent.displayName = "BadgeContent";
5142
- var Badge = React.forwardRef(
5143
- ({
5144
- count,
5145
- max = 999,
5146
- color = "error",
5147
- invisible = false,
5148
- "aria-label": ariaLabel,
5149
- className,
5150
- children
5151
- }, ref) => {
5152
- const isReduced = useReducedMotion();
5153
- const shouldShow = !invisible && (count === void 0 || count > 0);
5154
- return /* @__PURE__ */ jsxRuntime.jsxs(BadgeHeadless, { ref, className, children: [
5155
- children,
5156
- /* @__PURE__ */ jsxRuntime.jsx(
5157
- BadgeContent,
5158
- {
5159
- count,
5160
- max,
5161
- color,
5162
- invisible: !shouldShow,
5163
- "aria-label": ariaLabel,
5164
- reducedMotion: isReduced
5165
- }
5166
- )
5167
- ] });
5168
- }
5169
- );
5170
- Badge.displayName = "Badge";
5171
- function isBadgeConfig(badge) {
5172
- return typeof badge === "object" && badge !== null && !React.isValidElement(badge) && ("count" in badge || "color" in badge);
5173
5115
  }
5174
5116
  var DrawerItem = React.forwardRef(
5175
5117
  ({
@@ -5197,12 +5139,18 @@ var DrawerItem = React.forwardRef(
5197
5139
  const renderBadge = () => {
5198
5140
  if (!badge) return null;
5199
5141
  if (isBadgeConfig(badge)) {
5142
+ const max = 999;
5143
+ const isDot = badge.count === void 0;
5144
+ const displayValue = getBadgeDisplayValue(badge.count, max);
5145
+ const ariaLabel = getBadgeAriaLabel(badge.count);
5200
5146
  return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "relative z-10 ml-auto flex shrink-0 items-center pr-2", children: /* @__PURE__ */ jsxRuntime.jsx(
5201
- Badge,
5147
+ "span",
5202
5148
  {
5203
- ...badge.count !== void 0 ? { count: badge.count } : {},
5204
- ...badge.color !== void 0 ? { color: badge.color } : {},
5205
- children: /* @__PURE__ */ jsxRuntime.jsx("span", {})
5149
+ role: "status",
5150
+ "aria-label": ariaLabel,
5151
+ "data-dot": isDot ? "" : void 0,
5152
+ className: cn(badgeStaticVariants()),
5153
+ children: displayValue
5206
5154
  }
5207
5155
  ) });
5208
5156
  }
@@ -8701,6 +8649,75 @@ var Search = React.forwardRef(
8701
8649
  }
8702
8650
  );
8703
8651
  Search.displayName = "Search";
8652
+ var BadgeHeadless = React.forwardRef(
8653
+ ({ className, children, ...props }, ref) => {
8654
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("relative inline-flex", className), ...props, children });
8655
+ }
8656
+ );
8657
+ BadgeHeadless.displayName = "BadgeHeadless";
8658
+ var getDisplayValue = (count, max) => {
8659
+ if (count === void 0) return "";
8660
+ return count > max ? `${max}+` : count.toString();
8661
+ };
8662
+ var getAriaLabel = (count, override) => {
8663
+ if (override) return override;
8664
+ return count === void 0 ? "New" : `${count} notifications`;
8665
+ };
8666
+ var BadgeContent = React.forwardRef(
8667
+ ({
8668
+ count,
8669
+ max = 999,
8670
+ invisible = false,
8671
+ "aria-label": ariaLabelOverride,
8672
+ reducedMotion = false,
8673
+ className
8674
+ }, ref) => {
8675
+ const isDot = count === void 0;
8676
+ const displayValue = getDisplayValue(count, max);
8677
+ const ariaLabel = getAriaLabel(count, ariaLabelOverride);
8678
+ return /* @__PURE__ */ jsxRuntime.jsx(
8679
+ "span",
8680
+ {
8681
+ ref,
8682
+ role: "status",
8683
+ "aria-label": ariaLabel,
8684
+ "data-dot": isDot ? "" : void 0,
8685
+ "data-invisible": invisible ? "" : void 0,
8686
+ className: cn(
8687
+ badgeVariants2(),
8688
+ // MD3 Expressive spatial motion for show/hide scale animation.
8689
+ // Spatial pairing: scale transform → expressive-fast-spatial token.
8690
+ // Guarded at the component level; do NOT use CSS-only reduced-motion
8691
+ // because this is a JS-conditional class, not a persistent transition.
8692
+ !reducedMotion && "duration-expressive-fast-spatial ease-expressive-fast-spatial transition-transform",
8693
+ className
8694
+ ),
8695
+ children: displayValue
8696
+ }
8697
+ );
8698
+ }
8699
+ );
8700
+ BadgeContent.displayName = "BadgeContent";
8701
+ var Badge = React.forwardRef(
8702
+ ({ count, max = 999, invisible = false, "aria-label": ariaLabel, className, children }, ref) => {
8703
+ const isReduced = useReducedMotion();
8704
+ const shouldShow = !invisible && (count === void 0 || count > 0);
8705
+ return /* @__PURE__ */ jsxRuntime.jsxs(BadgeHeadless, { ref, className, children: [
8706
+ children,
8707
+ /* @__PURE__ */ jsxRuntime.jsx(
8708
+ BadgeContent,
8709
+ {
8710
+ count,
8711
+ max,
8712
+ invisible: !shouldShow,
8713
+ "aria-label": ariaLabel,
8714
+ reducedMotion: isReduced
8715
+ }
8716
+ )
8717
+ ] });
8718
+ }
8719
+ );
8720
+ Badge.displayName = "Badge";
8704
8721
  var splitButtonContainerVariants = classVarianceAuthority.cva(
8705
8722
  ["inline-flex items-center rounded-full overflow-hidden"],
8706
8723
  {
@@ -9335,13 +9352,30 @@ var FABMenuHeadless = React.forwardRef(
9335
9352
  }
9336
9353
  );
9337
9354
  FABMenuHeadless.displayName = "FABMenuHeadless";
9338
- var fabMenuVariants = classVarianceAuthority.cva(["relative", "inline-flex", "items-end"], {
9355
+ var fabMenuVariants = classVarianceAuthority.cva(["relative", "inline-flex"]);
9356
+ var fabMenuListVariants = classVarianceAuthority.cva(["absolute", "z-10", "flex", "gap-3"], {
9339
9357
  variants: {
9340
9358
  direction: {
9341
- up: ["flex-col-reverse", "gap-3"],
9342
- down: ["flex-col", "gap-3"],
9343
- left: ["flex-row-reverse", "gap-3", "items-center"],
9344
- right: ["flex-row", "gap-3", "items-center"]
9359
+ up: ["bottom-full", "mb-3", "end-0", "flex-col-reverse", "items-end", "origin-bottom"],
9360
+ down: ["top-full", "mt-3", "end-0", "flex-col", "items-end", "origin-top"],
9361
+ left: [
9362
+ "end-full",
9363
+ "me-3",
9364
+ "top-1/2",
9365
+ "-translate-y-1/2",
9366
+ "flex-row-reverse",
9367
+ "items-center",
9368
+ "origin-right"
9369
+ ],
9370
+ right: [
9371
+ "start-full",
9372
+ "ms-3",
9373
+ "top-1/2",
9374
+ "-translate-y-1/2",
9375
+ "flex-row",
9376
+ "items-center",
9377
+ "origin-left"
9378
+ ]
9345
9379
  }
9346
9380
  },
9347
9381
  defaultVariants: {
@@ -9349,19 +9383,129 @@ var fabMenuVariants = classVarianceAuthority.cva(["relative", "inline-flex", "it
9349
9383
  }
9350
9384
  });
9351
9385
  var fabMenuItemVariants = classVarianceAuthority.cva(
9352
- ["relative", "flex", "items-center", "gap-3", "cursor-pointer"],
9386
+ [
9387
+ // Layout — full-rounded pill, NO overflow-hidden (focus ring extends outside)
9388
+ "relative inline-flex items-center cursor-pointer select-none",
9389
+ "rounded-full",
9390
+ "h-14 pl-4 pr-5 gap-3",
9391
+ // 56dp height, 16dp leading, 20dp trailing, 12dp gap
9392
+ // Effects transition for color/bg/shadow
9393
+ "transition-[color,background-color,box-shadow]",
9394
+ "duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
9395
+ // Disabled — self-targeting data-[x]: selectors
9396
+ "data-[disabled]:bg-on-surface/12 data-[disabled]:text-on-surface/38",
9397
+ "data-[disabled]:shadow-none data-[disabled]:cursor-not-allowed data-[disabled]:pointer-events-none"
9398
+ ],
9353
9399
  {
9354
9400
  variants: {
9355
- isOpen: {
9356
- true: ["pointer-events-auto", "opacity-100"],
9357
- false: ["pointer-events-none", "opacity-0"]
9401
+ /**
9402
+ * Color role — controls container background, text color, and elevation.
9403
+ * State-layer color must equal the on-color (see fabMenuItemStateLayerVariants).
9404
+ *
9405
+ * Elevation per state:
9406
+ * base → elevation-3
9407
+ * hover → elevation-4 (group-data-[hovered]/fab-menu-item)
9408
+ * focus → elevation-3 (doubled selector wins over hover)
9409
+ * pressed→ elevation-3 (doubled selector wins over hover)
9410
+ * disabled → shadow-none (self-targeting data-[disabled])
9411
+ */
9412
+ color: {
9413
+ "primary-container": [
9414
+ "bg-primary-container text-on-primary-container",
9415
+ "shadow-elevation-3",
9416
+ "group-data-[hovered]/fab-menu-item:shadow-elevation-4",
9417
+ "group-data-[focus-visible]/fab-menu-item:shadow-elevation-3",
9418
+ "group-data-[pressed]/fab-menu-item:group-data-[pressed]/fab-menu-item:shadow-elevation-3"
9419
+ ],
9420
+ "secondary-container": [
9421
+ "bg-secondary-container text-on-secondary-container",
9422
+ "shadow-elevation-3",
9423
+ "group-data-[hovered]/fab-menu-item:shadow-elevation-4",
9424
+ "group-data-[focus-visible]/fab-menu-item:shadow-elevation-3",
9425
+ "group-data-[pressed]/fab-menu-item:group-data-[pressed]/fab-menu-item:shadow-elevation-3"
9426
+ ],
9427
+ "tertiary-container": [
9428
+ "bg-tertiary-container text-on-tertiary-container",
9429
+ "shadow-elevation-3",
9430
+ "group-data-[hovered]/fab-menu-item:shadow-elevation-4",
9431
+ "group-data-[focus-visible]/fab-menu-item:shadow-elevation-3",
9432
+ "group-data-[pressed]/fab-menu-item:group-data-[pressed]/fab-menu-item:shadow-elevation-3"
9433
+ ],
9434
+ primary: [
9435
+ "bg-primary text-on-primary",
9436
+ "shadow-elevation-3",
9437
+ "group-data-[hovered]/fab-menu-item:shadow-elevation-4",
9438
+ "group-data-[focus-visible]/fab-menu-item:shadow-elevation-3",
9439
+ "group-data-[pressed]/fab-menu-item:group-data-[pressed]/fab-menu-item:shadow-elevation-3"
9440
+ ],
9441
+ secondary: [
9442
+ "bg-secondary text-on-secondary",
9443
+ "shadow-elevation-3",
9444
+ "group-data-[hovered]/fab-menu-item:shadow-elevation-4",
9445
+ "group-data-[focus-visible]/fab-menu-item:shadow-elevation-3",
9446
+ "group-data-[pressed]/fab-menu-item:group-data-[pressed]/fab-menu-item:shadow-elevation-3"
9447
+ ],
9448
+ tertiary: [
9449
+ "bg-tertiary text-on-tertiary",
9450
+ "shadow-elevation-3",
9451
+ "group-data-[hovered]/fab-menu-item:shadow-elevation-4",
9452
+ "group-data-[focus-visible]/fab-menu-item:shadow-elevation-3",
9453
+ "group-data-[pressed]/fab-menu-item:group-data-[pressed]/fab-menu-item:shadow-elevation-3"
9454
+ ]
9358
9455
  }
9359
9456
  },
9360
9457
  defaultVariants: {
9361
- isOpen: false
9458
+ color: "primary-container"
9362
9459
  }
9363
9460
  }
9364
9461
  );
9462
+ var fabMenuItemStateLayerVariants = classVarianceAuthority.cva(
9463
+ [
9464
+ "absolute inset-0 rounded-[inherit] overflow-hidden pointer-events-none opacity-0",
9465
+ // Effects transition — opacity must not overshoot
9466
+ "transition-opacity duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
9467
+ // Hover: 8%
9468
+ "group-data-[hovered]/fab-menu-item:opacity-8",
9469
+ // Focus: 10%
9470
+ "group-data-[focus-visible]/fab-menu-item:opacity-10",
9471
+ // Pressed: 10% — doubled selector wins over hover's 8%
9472
+ "group-data-[pressed]/fab-menu-item:group-data-[pressed]/fab-menu-item:opacity-10",
9473
+ // No state layer when disabled
9474
+ "group-data-[disabled]/fab-menu-item:hidden"
9475
+ ],
9476
+ {
9477
+ variants: {
9478
+ color: {
9479
+ "primary-container": "bg-on-primary-container",
9480
+ "secondary-container": "bg-on-secondary-container",
9481
+ "tertiary-container": "bg-on-tertiary-container",
9482
+ primary: "bg-on-primary",
9483
+ secondary: "bg-on-secondary",
9484
+ tertiary: "bg-on-tertiary"
9485
+ }
9486
+ },
9487
+ defaultVariants: { color: "primary-container" }
9488
+ }
9489
+ );
9490
+ var fabMenuItemFocusRingVariants = classVarianceAuthority.cva([
9491
+ "pointer-events-none absolute inset-[-3px] rounded-full",
9492
+ "outline outline-2 outline-offset-0 outline-secondary",
9493
+ // Effects transition — opacity change must not overshoot
9494
+ "transition-opacity duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
9495
+ "opacity-0",
9496
+ "group-data-[focus-visible]/fab-menu-item:opacity-100"
9497
+ ]);
9498
+ var fabMenuItemIconVariants = classVarianceAuthority.cva([
9499
+ "relative z-10 inline-flex shrink-0 items-center justify-center",
9500
+ "size-6",
9501
+ // 24dp per MD3 spec
9502
+ // Color transition uses effects token (no spatial overshoot on color)
9503
+ "transition-colors duration-spring-standard-fast-effects ease-spring-standard-fast-effects"
9504
+ ]);
9505
+ var fabMenuItemLabelVariants = classVarianceAuthority.cva([
9506
+ "relative z-10 inline-flex items-center",
9507
+ "text-title-medium"
9508
+ ]);
9365
9509
  var FABMenu = React.forwardRef(
9366
9510
  ({
9367
9511
  open: controlledOpen,
@@ -9400,7 +9544,7 @@ var FABMenu = React.forwardRef(
9400
9544
  setIsOpen(false);
9401
9545
  }, [setIsOpen]);
9402
9546
  const prevIsOpenRef = React.useRef(void 0);
9403
- React.useEffect(() => {
9547
+ React.useLayoutEffect(() => {
9404
9548
  if (prevIsOpenRef.current === void 0) {
9405
9549
  prevIsOpenRef.current = isOpen;
9406
9550
  return;
@@ -9463,119 +9607,143 @@ var FABMenu = React.forwardRef(
9463
9607
  }
9464
9608
  return child;
9465
9609
  });
9466
- return /* @__PURE__ */ jsxRuntime.jsx(FABMenuContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(
9467
- "div",
9468
- {
9469
- ref: rootRef,
9470
- className: cn(fabMenuVariants({ direction }), className),
9471
- onKeyDown: handleKeyDown,
9472
- children: [
9473
- (isOpen || isExiting) && /* @__PURE__ */ jsxRuntime.jsx(
9474
- "div",
9610
+ return /* @__PURE__ */ jsxRuntime.jsx(FABMenuContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: rootRef, className: cn(fabMenuVariants(), className), onKeyDown: handleKeyDown, children: [
9611
+ (isOpen || isExiting) && /* @__PURE__ */ jsxRuntime.jsx(
9612
+ "div",
9613
+ {
9614
+ className: cn(fabMenuListVariants({ direction })),
9615
+ role: "group",
9616
+ "aria-label": `${ariaLabel} actions`,
9617
+ children: indexedChildren
9618
+ }
9619
+ ),
9620
+ /* @__PURE__ */ jsxRuntime.jsx(
9621
+ FAB,
9622
+ {
9623
+ ref: triggerRef,
9624
+ onPress: toggle,
9625
+ "aria-label": ariaLabel,
9626
+ "aria-expanded": isOpen,
9627
+ icon: /* @__PURE__ */ jsxRuntime.jsx(
9628
+ "svg",
9475
9629
  {
9630
+ xmlns: "http://www.w3.org/2000/svg",
9631
+ viewBox: "0 0 24 24",
9632
+ fill: "currentColor",
9476
9633
  className: cn(
9477
- "inline-flex items-center gap-3",
9478
- direction === "up" && "flex-col-reverse",
9479
- direction === "down" && "flex-col",
9480
- direction === "left" && "flex-row-reverse",
9481
- direction === "right" && "flex-row"
9634
+ // Expressive fast-spatial: FAB icon is small, high-emphasis — matches FAB enter motion
9635
+ "h-6 w-6 transition-transform",
9636
+ reducedMotion ? "" : "duration-expressive-fast-spatial ease-expressive-fast-spatial",
9637
+ isOpen && "rotate-45"
9482
9638
  ),
9483
- role: "group",
9484
- "aria-label": `${ariaLabel} actions`,
9485
- children: indexedChildren
9639
+ "aria-hidden": "true",
9640
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" })
9486
9641
  }
9487
9642
  ),
9488
- /* @__PURE__ */ jsxRuntime.jsx(
9489
- FAB,
9490
- {
9491
- ref: triggerRef,
9492
- onPress: toggle,
9493
- "aria-label": ariaLabel,
9494
- "aria-expanded": isOpen,
9495
- icon: /* @__PURE__ */ jsxRuntime.jsx(
9496
- "svg",
9497
- {
9498
- xmlns: "http://www.w3.org/2000/svg",
9499
- viewBox: "0 0 24 24",
9500
- fill: "currentColor",
9501
- className: cn(
9502
- "duration-short4 ease-standard h-6 w-6 transition-transform",
9503
- isOpen && "rotate-45"
9504
- ),
9505
- "aria-hidden": "true",
9506
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" })
9507
- }
9508
- ),
9509
- className: cn(isOpen && "shadow-elevation-4")
9510
- }
9511
- )
9512
- ]
9513
- }
9514
- ) });
9643
+ className: cn(isOpen && "shadow-elevation-4")
9644
+ }
9645
+ )
9646
+ ] }) });
9515
9647
  }
9516
9648
  );
9517
9649
  FABMenu.displayName = "FABMenu";
9518
9650
  var FABMenuItem = React.forwardRef(
9519
- ({ icon, label, onPress, "aria-label": ariaLabel, isDisabled = false, className, index = 0 }, forwardedRef) => {
9651
+ ({
9652
+ icon,
9653
+ label,
9654
+ "aria-label": ariaLabel,
9655
+ onPress,
9656
+ color = "primary-container",
9657
+ isDisabled = false,
9658
+ className,
9659
+ index = 0
9660
+ }, forwardedRef) => {
9520
9661
  const internalRef = React.useRef(null);
9521
9662
  const buttonRef = forwardedRef ?? internalRef;
9522
- const { isOpen, isExiting, direction, reducedMotion, itemCount } = useFABMenuContext();
9663
+ const { isOpen, isExiting, reducedMotion, itemCount, direction } = useFABMenuContext();
9664
+ if (process.env.NODE_ENV === "development") {
9665
+ if (!label && !ariaLabel) {
9666
+ console.warn(
9667
+ "[FABMenuItem] Either `label` or `aria-label` must be provided for accessibility."
9668
+ );
9669
+ }
9670
+ }
9671
+ const [isPressed, setIsPressed] = React.useState(false);
9672
+ const handlePressStart = React.useCallback(() => setIsPressed(true), []);
9673
+ const handlePressEnd = React.useCallback(() => setIsPressed(false), []);
9674
+ const { isHovered, hoverProps } = reactAria.useHover({ isDisabled });
9675
+ const { isFocusVisible, focusProps } = reactAria.useFocusRing();
9523
9676
  const { buttonProps } = reactAria.useButton(
9524
9677
  {
9525
- ...onPress && { onPress },
9526
- "aria-label": ariaLabel,
9527
- isDisabled
9678
+ ...onPress ? { onPress } : {},
9679
+ isDisabled,
9680
+ onPressStart: handlePressStart,
9681
+ onPressEnd: handlePressEnd,
9682
+ ...ariaLabel ? { "aria-label": ariaLabel } : {},
9683
+ elementType: "button"
9528
9684
  },
9529
9685
  buttonRef
9530
9686
  );
9531
- const { onMouseDown: handleRipple, ripples } = useRipple({
9532
- disabled: isDisabled
9533
- });
9534
- const mergedProps = utils.mergeProps(buttonProps, {
9535
- type: "button",
9536
- onMouseDown: handleRipple
9537
- });
9687
+ const { onMouseDown: handleRipple, ripples } = useRipple({ disabled: isDisabled });
9538
9688
  const staggerDelay = reducedMotion ? 0 : isExiting ? Math.max(0, itemCount - 1 - index) * 30 : index * 30;
9689
+ const DIRECTION_ORIGIN = {
9690
+ up: "origin-bottom",
9691
+ down: "origin-top",
9692
+ left: "origin-right",
9693
+ right: "origin-left"
9694
+ };
9695
+ const originClass = reducedMotion ? void 0 : DIRECTION_ORIGIN[direction];
9539
9696
  const animationClass = reducedMotion ? void 0 : isOpen ? "animate-md-scale-in" : isExiting ? "animate-md-scale-out" : void 0;
9540
- const labelPosition = direction === "right" ? "after" : "before";
9541
- const labelChip = label ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bg-surface-container text-label-large text-on-surface shadow-elevation-1 rounded-full px-3 py-1", children: label }) : null;
9542
- const isVisible = isOpen || isExiting;
9697
+ const mergedButtonProps = reactAria.mergeProps(buttonProps, hoverProps, focusProps, {
9698
+ onMouseDown: handleRipple
9699
+ });
9700
+ const {
9701
+ isDisabled: _isDisabled,
9702
+ onPress: _onPress,
9703
+ onPressStart: _onPressStart,
9704
+ onPressEnd: _onPressEnd,
9705
+ onPressChange: _onPressChange,
9706
+ onPressUp: _onPressUp,
9707
+ ...htmlButtonProps
9708
+ } = mergedButtonProps;
9543
9709
  return /* @__PURE__ */ jsxRuntime.jsxs(
9544
- "div",
9710
+ "button",
9545
9711
  {
9712
+ ...htmlButtonProps,
9713
+ ref: buttonRef,
9714
+ type: "button",
9715
+ ...getInteractionDataAttributes({
9716
+ isHovered,
9717
+ isFocusVisible,
9718
+ isPressed,
9719
+ isDisabled
9720
+ }),
9721
+ "data-with-icon": icon ? "" : void 0,
9722
+ "data-with-label": label ? "" : void 0,
9546
9723
  className: cn(
9547
- "relative flex cursor-pointer items-center gap-3",
9548
- isVisible ? "opacity-100" : "opacity-0",
9549
- isOpen ? "pointer-events-auto" : "pointer-events-none",
9724
+ fabMenuItemVariants({ color }),
9725
+ // group/fab-menu-item: enables group-data-[x]/fab-menu-item child selectors in all slots
9726
+ "group/fab-menu-item",
9727
+ // Scale pivot toward the FAB so items appear to emanate from the trigger
9728
+ originClass,
9729
+ // Stagger animation class (animate-md-scale-in / animate-md-scale-out)
9730
+ animationClass,
9550
9731
  className
9551
9732
  ),
9733
+ style: staggerDelay > 0 ? { animationDelay: `${staggerDelay}ms` } : void 0,
9552
9734
  children: [
9553
- labelPosition === "before" && labelChip,
9554
- /* @__PURE__ */ jsxRuntime.jsxs(
9555
- "button",
9735
+ ripples,
9736
+ /* @__PURE__ */ jsxRuntime.jsx(
9737
+ "span",
9556
9738
  {
9557
- ...mergedProps,
9558
- ref: buttonRef,
9559
- className: cn(
9560
- "relative flex size-10 items-center justify-center overflow-hidden rounded-xl",
9561
- "bg-primary-container text-on-primary-container shadow-elevation-3",
9562
- animationClass
9563
- ),
9564
- style: staggerDelay > 0 ? { animationDelay: `${staggerDelay}ms` } : void 0,
9565
- children: [
9566
- /* @__PURE__ */ jsxRuntime.jsx(
9567
- "span",
9568
- {
9569
- "data-state-layer": true,
9570
- className: "bg-on-primary-container duration-spring-standard-fast-effects ease-spring-standard-fast-effects pointer-events-none absolute inset-0 rounded-xl opacity-0 transition-opacity hover:opacity-8"
9571
- }
9572
- ),
9573
- ripples,
9574
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "relative z-10 inline-flex shrink-0", children: icon })
9575
- ]
9739
+ className: cn(fabMenuItemStateLayerVariants({ color })),
9740
+ "data-state-layer": true,
9741
+ "aria-hidden": "true"
9576
9742
  }
9577
9743
  ),
9578
- labelPosition === "after" && labelChip
9744
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(fabMenuItemFocusRingVariants()), "aria-hidden": "true" }),
9745
+ icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(fabMenuItemIconVariants()), "aria-hidden": "true", children: icon }),
9746
+ label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(fabMenuItemLabelVariants()), children: label })
9579
9747
  ]
9580
9748
  }
9581
9749
  );
@@ -15282,7 +15450,12 @@ exports.datePickerScrimVariants = datePickerScrimVariants;
15282
15450
  exports.datePickerSupportingTextVariants = datePickerSupportingTextVariants;
15283
15451
  exports.datePickerWeekdayVariants = datePickerWeekdayVariants;
15284
15452
  exports.dividerVariants = dividerVariants;
15453
+ exports.fabMenuItemFocusRingVariants = fabMenuItemFocusRingVariants;
15454
+ exports.fabMenuItemIconVariants = fabMenuItemIconVariants;
15455
+ exports.fabMenuItemLabelVariants = fabMenuItemLabelVariants;
15456
+ exports.fabMenuItemStateLayerVariants = fabMenuItemStateLayerVariants;
15285
15457
  exports.fabMenuItemVariants = fabMenuItemVariants;
15458
+ exports.fabMenuListVariants = fabMenuListVariants;
15286
15459
  exports.fabMenuVariants = fabMenuVariants;
15287
15460
  exports.generateMD3Theme = generateMD3Theme;
15288
15461
  exports.getColorValue = getColorValue;