@tinybigui/react 0.4.1 → 0.5.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 +667 -390
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +330 -198
- package/dist/index.d.ts +330 -198
- package/dist/index.js +668 -393
- package/dist/index.js.map +1 -1
- package/dist/styles.css +2 -1
- package/dist/styles.css.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -426,218 +426,206 @@ var ButtonHeadless = React.forwardRef(
|
|
|
426
426
|
ButtonHeadless.displayName = "ButtonHeadless";
|
|
427
427
|
var buttonVariants = classVarianceAuthority.cva(
|
|
428
428
|
[
|
|
429
|
-
//
|
|
430
|
-
"relative inline-flex items-center justify-center
|
|
431
|
-
"
|
|
432
|
-
// Split MD3 transition: spatial (border-radius)
|
|
433
|
-
// effects (color/bg/shadow)
|
|
429
|
+
// Layout + shape — NO overflow-hidden here (see note above)
|
|
430
|
+
"relative inline-flex items-center justify-center",
|
|
431
|
+
"rounded-full cursor-pointer select-none",
|
|
432
|
+
// Split MD3 transition: spatial (border-radius) → expressive spring;
|
|
433
|
+
// effects (color/bg/shadow) → standard effects spring.
|
|
434
434
|
"btn-transition",
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
"focus-visible:outline-primary focus-visible:outline-2 focus-visible:outline-offset-2"
|
|
435
|
+
// Disabled — self-targeting data-[x]: selectors
|
|
436
|
+
"data-[disabled]:cursor-not-allowed data-[disabled]:pointer-events-none"
|
|
438
437
|
],
|
|
439
438
|
{
|
|
440
439
|
variants: {
|
|
441
440
|
/**
|
|
442
|
-
* Button variant (MD3 specification)
|
|
441
|
+
* Button variant (MD3 specification — strict, no color override)
|
|
442
|
+
*
|
|
443
|
+
* Elevation per state follows _md-comp-*-button.scss tokens:
|
|
444
|
+
* Filled/Tonal hover→level-1, focus/pressed→level-0
|
|
445
|
+
* Elevated base→level-1, hover→level-2, focus/pressed→level-1
|
|
446
|
+
* Outlined/Text no elevation
|
|
443
447
|
*/
|
|
444
448
|
variant: {
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
449
|
+
/**
|
|
450
|
+
* Filled — highest emphasis.
|
|
451
|
+
* MD3: container=primary, label=on-primary, state-layer=on-primary
|
|
452
|
+
* Elevation: 0 base → 1 hover → 0 focus → 0 pressed
|
|
453
|
+
*/
|
|
454
|
+
filled: [
|
|
455
|
+
"bg-primary text-on-primary shadow-none",
|
|
456
|
+
// Hover: gains level-1 elevation
|
|
457
|
+
"group-data-[hovered]/button:shadow-elevation-1",
|
|
458
|
+
// Focus/pressed: shadow must explicitly return to level-0
|
|
459
|
+
// (doubled attribute selector → higher specificity than hover)
|
|
460
|
+
"group-data-[focus-visible]/button:shadow-none",
|
|
461
|
+
"group-data-[pressed]/button:group-data-[pressed]/button:shadow-none",
|
|
462
|
+
// Disabled overrides
|
|
463
|
+
"group-data-[disabled]/button:bg-on-surface/12",
|
|
464
|
+
"group-data-[disabled]/button:text-on-surface/38",
|
|
465
|
+
"group-data-[disabled]/button:shadow-none"
|
|
466
|
+
],
|
|
467
|
+
/**
|
|
468
|
+
* Outlined — medium emphasis. Transparent with border.
|
|
469
|
+
* MD3: container=transparent, outline=outline, label=primary, state-layer=primary
|
|
470
|
+
* Elevation: always 0
|
|
471
|
+
*/
|
|
472
|
+
outlined: [
|
|
473
|
+
"bg-transparent border border-outline text-primary",
|
|
474
|
+
// Disabled overrides
|
|
475
|
+
"group-data-[disabled]/button:border-on-surface/12",
|
|
476
|
+
"group-data-[disabled]/button:text-on-surface/38"
|
|
477
|
+
],
|
|
478
|
+
/**
|
|
479
|
+
* Tonal — secondary emphasis.
|
|
480
|
+
* MD3 name: "Filled tonal". container=secondary-container, label=on-secondary-container
|
|
481
|
+
* Elevation: 0 base → 1 hover → 0 focus → 0 pressed
|
|
482
|
+
*/
|
|
483
|
+
tonal: [
|
|
484
|
+
"bg-secondary-container text-on-secondary-container shadow-none",
|
|
485
|
+
// Hover: gains level-1 elevation (same as filled)
|
|
486
|
+
"group-data-[hovered]/button:shadow-elevation-1",
|
|
487
|
+
// Focus/pressed: return to level-0
|
|
488
|
+
"group-data-[focus-visible]/button:shadow-none",
|
|
489
|
+
"group-data-[pressed]/button:group-data-[pressed]/button:shadow-none",
|
|
490
|
+
// Disabled overrides
|
|
491
|
+
"group-data-[disabled]/button:bg-on-surface/12",
|
|
492
|
+
"group-data-[disabled]/button:text-on-surface/38",
|
|
493
|
+
"group-data-[disabled]/button:shadow-none"
|
|
494
|
+
],
|
|
495
|
+
/**
|
|
496
|
+
* Elevated — separation via shadow.
|
|
497
|
+
* MD3: container=surface-container-low, label=primary
|
|
498
|
+
* Elevation: 1 base → 2 hover → 1 focus → 1 pressed
|
|
499
|
+
*/
|
|
500
|
+
elevated: [
|
|
501
|
+
"bg-surface-container-low text-primary shadow-elevation-1",
|
|
502
|
+
// Hover: gains extra elevation
|
|
503
|
+
"group-data-[hovered]/button:shadow-elevation-2",
|
|
504
|
+
// Focus/pressed: return to base level-1
|
|
505
|
+
// (doubled selector wins over single hover selector at same cascade position)
|
|
506
|
+
"group-data-[focus-visible]/button:shadow-elevation-1",
|
|
507
|
+
"group-data-[pressed]/button:group-data-[pressed]/button:shadow-elevation-1",
|
|
508
|
+
// Disabled overrides
|
|
509
|
+
"group-data-[disabled]/button:bg-on-surface/12",
|
|
510
|
+
"group-data-[disabled]/button:text-on-surface/38",
|
|
511
|
+
"group-data-[disabled]/button:shadow-none"
|
|
512
|
+
],
|
|
513
|
+
/**
|
|
514
|
+
* Text — lowest emphasis.
|
|
515
|
+
* MD3: container=transparent, label=primary, state-layer=primary
|
|
516
|
+
* Elevation: always 0
|
|
517
|
+
*/
|
|
518
|
+
text: [
|
|
519
|
+
"bg-transparent text-primary",
|
|
520
|
+
// Disabled overrides
|
|
521
|
+
"group-data-[disabled]/button:text-on-surface/38"
|
|
522
|
+
]
|
|
461
523
|
},
|
|
462
524
|
/**
|
|
463
525
|
* Button size
|
|
526
|
+
* MD3 spec: small=32dp, medium=40dp, large=56dp
|
|
527
|
+
* Padding: small=16dp, medium=24dp, large=32dp
|
|
528
|
+
* Text variant uses reduced padding: small=12dp, medium=12dp
|
|
464
529
|
*/
|
|
465
530
|
size: {
|
|
466
|
-
small: "h-8 px-4 text-
|
|
467
|
-
medium: "h-10 px-6 text-
|
|
468
|
-
large: "h-
|
|
531
|
+
small: "h-8 px-4 gap-1 text-label-medium tracking-[0.1px]",
|
|
532
|
+
medium: "h-10 px-6 gap-2 text-label-large tracking-[0.1px]",
|
|
533
|
+
large: "h-14 px-8 gap-2 text-title-medium"
|
|
469
534
|
},
|
|
470
535
|
/**
|
|
471
|
-
* Full width
|
|
536
|
+
* Full width button (spans container)
|
|
472
537
|
*/
|
|
473
538
|
fullWidth: {
|
|
474
539
|
true: "w-full",
|
|
475
540
|
false: ""
|
|
476
|
-
},
|
|
477
|
-
/**
|
|
478
|
-
* Disabled state (MD3 spec: container 12% opacity, content 38% opacity)
|
|
479
|
-
*/
|
|
480
|
-
disabled: {
|
|
481
|
-
true: [
|
|
482
|
-
"pointer-events-none cursor-not-allowed",
|
|
483
|
-
"bg-on-surface/12",
|
|
484
|
-
// MD3: disabled container uses on-surface at 12%
|
|
485
|
-
"text-on-surface/38",
|
|
486
|
-
// MD3: disabled text/icons use on-surface at 38%
|
|
487
|
-
"border-on-surface/12",
|
|
488
|
-
// For outlined variant
|
|
489
|
-
"shadow-none"
|
|
490
|
-
// Remove elevation when disabled
|
|
491
|
-
],
|
|
492
|
-
false: ""
|
|
493
|
-
},
|
|
494
|
-
/**
|
|
495
|
-
* Loading state
|
|
496
|
-
*/
|
|
497
|
-
loading: {
|
|
498
|
-
true: "cursor-wait",
|
|
499
|
-
false: ""
|
|
500
541
|
}
|
|
501
542
|
},
|
|
502
543
|
/**
|
|
503
|
-
* Compound variants
|
|
544
|
+
* Compound variants for text variant reduced padding per size
|
|
545
|
+
* MD3: text buttons use 12dp padding (px-3) instead of standard padding
|
|
504
546
|
*/
|
|
505
547
|
compoundVariants: [
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
{
|
|
510
|
-
variant: "filled",
|
|
511
|
-
color: "primary",
|
|
512
|
-
className: "bg-primary text-on-primary"
|
|
513
|
-
},
|
|
514
|
-
{
|
|
515
|
-
variant: "filled",
|
|
516
|
-
color: "secondary",
|
|
517
|
-
className: "bg-secondary text-on-secondary"
|
|
518
|
-
},
|
|
519
|
-
{
|
|
520
|
-
variant: "filled",
|
|
521
|
-
color: "tertiary",
|
|
522
|
-
className: "bg-tertiary text-on-tertiary"
|
|
523
|
-
},
|
|
524
|
-
{
|
|
525
|
-
variant: "filled",
|
|
526
|
-
color: "error",
|
|
527
|
-
className: "bg-error text-on-error"
|
|
528
|
-
},
|
|
529
|
-
// ====================
|
|
530
|
-
// OUTLINED VARIANTS
|
|
531
|
-
// ====================
|
|
532
|
-
{
|
|
533
|
-
variant: "outlined",
|
|
534
|
-
color: "primary",
|
|
535
|
-
className: "text-primary"
|
|
536
|
-
},
|
|
537
|
-
{
|
|
538
|
-
variant: "outlined",
|
|
539
|
-
color: "secondary",
|
|
540
|
-
className: "text-secondary"
|
|
541
|
-
},
|
|
542
|
-
{
|
|
543
|
-
variant: "outlined",
|
|
544
|
-
color: "tertiary",
|
|
545
|
-
className: "text-tertiary"
|
|
546
|
-
},
|
|
547
|
-
{
|
|
548
|
-
variant: "outlined",
|
|
549
|
-
color: "error",
|
|
550
|
-
className: "text-error"
|
|
551
|
-
},
|
|
552
|
-
// ====================
|
|
553
|
-
// TONAL VARIANTS
|
|
554
|
-
// ====================
|
|
555
|
-
{
|
|
556
|
-
variant: "tonal",
|
|
557
|
-
color: "primary",
|
|
558
|
-
className: "bg-primary-container text-on-primary-container"
|
|
559
|
-
},
|
|
560
|
-
{
|
|
561
|
-
variant: "tonal",
|
|
562
|
-
color: "secondary",
|
|
563
|
-
className: "bg-secondary-container text-on-secondary-container"
|
|
564
|
-
},
|
|
565
|
-
{
|
|
566
|
-
variant: "tonal",
|
|
567
|
-
color: "tertiary",
|
|
568
|
-
className: "bg-tertiary-container text-on-tertiary-container"
|
|
569
|
-
},
|
|
570
|
-
{
|
|
571
|
-
variant: "tonal",
|
|
572
|
-
color: "error",
|
|
573
|
-
className: "bg-error-container text-on-error-container"
|
|
574
|
-
},
|
|
575
|
-
// ====================
|
|
576
|
-
// ELEVATED VARIANTS
|
|
577
|
-
// ====================
|
|
578
|
-
{
|
|
579
|
-
variant: "elevated",
|
|
580
|
-
color: "primary",
|
|
581
|
-
className: "bg-surface-container-low text-primary"
|
|
582
|
-
},
|
|
583
|
-
{
|
|
584
|
-
variant: "elevated",
|
|
585
|
-
color: "secondary",
|
|
586
|
-
className: "bg-surface-container-low text-secondary"
|
|
587
|
-
},
|
|
588
|
-
{
|
|
589
|
-
variant: "elevated",
|
|
590
|
-
color: "tertiary",
|
|
591
|
-
className: "bg-surface-container-low text-tertiary"
|
|
592
|
-
},
|
|
593
|
-
{
|
|
594
|
-
variant: "elevated",
|
|
595
|
-
color: "error",
|
|
596
|
-
className: "bg-surface-container-low text-error"
|
|
597
|
-
},
|
|
598
|
-
// ====================
|
|
599
|
-
// TEXT VARIANTS
|
|
600
|
-
// ====================
|
|
601
|
-
{
|
|
602
|
-
variant: "text",
|
|
603
|
-
color: "primary",
|
|
604
|
-
className: "text-primary hover:bg-primary/[0.08]"
|
|
605
|
-
// MD3: text buttons gain primary color at 8% opacity on hover
|
|
606
|
-
},
|
|
607
|
-
{
|
|
608
|
-
variant: "text",
|
|
609
|
-
color: "secondary",
|
|
610
|
-
className: "text-secondary hover:bg-secondary/[0.08]"
|
|
611
|
-
// MD3: text buttons gain secondary color at 8% opacity on hover
|
|
612
|
-
},
|
|
613
|
-
{
|
|
614
|
-
variant: "text",
|
|
615
|
-
color: "tertiary",
|
|
616
|
-
className: "text-tertiary hover:bg-tertiary/[0.08]"
|
|
617
|
-
// MD3: text buttons gain tertiary color at 8% opacity on hover
|
|
618
|
-
},
|
|
619
|
-
{
|
|
620
|
-
variant: "text",
|
|
621
|
-
color: "error",
|
|
622
|
-
className: "text-error hover:bg-error/[0.08]"
|
|
623
|
-
// MD3: text buttons gain error color at 8% opacity on hover
|
|
624
|
-
}
|
|
548
|
+
{ variant: "text", size: "small", className: "px-3" },
|
|
549
|
+
{ variant: "text", size: "medium", className: "px-3" },
|
|
550
|
+
{ variant: "text", size: "large", className: "px-4" }
|
|
625
551
|
],
|
|
626
|
-
/**
|
|
627
|
-
* Default variants
|
|
628
|
-
*/
|
|
629
552
|
defaultVariants: {
|
|
630
553
|
variant: "filled",
|
|
631
|
-
color: "primary",
|
|
632
554
|
size: "medium",
|
|
633
|
-
fullWidth: false
|
|
634
|
-
disabled: false,
|
|
635
|
-
loading: false
|
|
555
|
+
fullWidth: false
|
|
636
556
|
}
|
|
637
557
|
}
|
|
638
558
|
);
|
|
559
|
+
var buttonStateLayerVariants = classVarianceAuthority.cva(
|
|
560
|
+
[
|
|
561
|
+
"absolute inset-0 rounded-[inherit] overflow-hidden pointer-events-none opacity-0",
|
|
562
|
+
// Effects transition for opacity — standard spring, no overshoot
|
|
563
|
+
"transition-opacity duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
|
|
564
|
+
// Hover: 8%
|
|
565
|
+
"group-data-[hovered]/button:opacity-8",
|
|
566
|
+
// Focus: 10%
|
|
567
|
+
"group-data-[focus-visible]/button:opacity-10",
|
|
568
|
+
// Pressed: 10%, doubled selector wins over hover
|
|
569
|
+
"group-data-[pressed]/button:group-data-[pressed]/button:opacity-10",
|
|
570
|
+
// No state layer when disabled
|
|
571
|
+
"group-data-[disabled]/button:hidden"
|
|
572
|
+
],
|
|
573
|
+
{
|
|
574
|
+
variants: {
|
|
575
|
+
variant: {
|
|
576
|
+
filled: "bg-on-primary",
|
|
577
|
+
outlined: "bg-primary",
|
|
578
|
+
tonal: "bg-on-secondary-container",
|
|
579
|
+
elevated: "bg-primary",
|
|
580
|
+
text: "bg-primary"
|
|
581
|
+
}
|
|
582
|
+
},
|
|
583
|
+
defaultVariants: { variant: "filled" }
|
|
584
|
+
}
|
|
585
|
+
);
|
|
586
|
+
var buttonFocusRingVariants = classVarianceAuthority.cva([
|
|
587
|
+
"pointer-events-none absolute inset-[-3px] rounded-full",
|
|
588
|
+
"outline outline-2 outline-offset-0 outline-secondary",
|
|
589
|
+
// Effects transition — opacity change must not overshoot
|
|
590
|
+
"transition-opacity duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
|
|
591
|
+
"opacity-0",
|
|
592
|
+
"group-data-[focus-visible]/button:opacity-100"
|
|
593
|
+
]);
|
|
594
|
+
var buttonIconVariants = classVarianceAuthority.cva(
|
|
595
|
+
[
|
|
596
|
+
"relative z-10 inline-flex shrink-0 items-center justify-center",
|
|
597
|
+
"size-[18px]",
|
|
598
|
+
// Color transition uses effects token (no spatial overshoot on color)
|
|
599
|
+
"transition-colors duration-spring-standard-fast-effects ease-spring-standard-fast-effects"
|
|
600
|
+
],
|
|
601
|
+
{
|
|
602
|
+
variants: {
|
|
603
|
+
hidden: {
|
|
604
|
+
true: "invisible",
|
|
605
|
+
false: ""
|
|
606
|
+
}
|
|
607
|
+
},
|
|
608
|
+
defaultVariants: { hidden: false }
|
|
609
|
+
}
|
|
610
|
+
);
|
|
611
|
+
var buttonLabelVariants = classVarianceAuthority.cva(["relative z-10 inline-flex items-center"]);
|
|
612
|
+
var QUERY = "(prefers-reduced-motion: reduce)";
|
|
613
|
+
function useReducedMotion() {
|
|
614
|
+
const [reduced, setReduced] = React.useState(() => {
|
|
615
|
+
if (typeof window === "undefined") return false;
|
|
616
|
+
return window.matchMedia(QUERY).matches;
|
|
617
|
+
});
|
|
618
|
+
React.useEffect(() => {
|
|
619
|
+
const mql = window.matchMedia(QUERY);
|
|
620
|
+
const handler = (e) => setReduced(e.matches);
|
|
621
|
+
mql.addEventListener("change", handler);
|
|
622
|
+
return () => mql.removeEventListener("change", handler);
|
|
623
|
+
}, []);
|
|
624
|
+
return reduced;
|
|
625
|
+
}
|
|
639
626
|
function useRipple(options = {}) {
|
|
640
627
|
const { disabled = false, color = "currentColor", duration = 450 } = options;
|
|
628
|
+
const prefersReducedMotion = useReducedMotion();
|
|
641
629
|
const [ripples, setRipples] = React.useState([]);
|
|
642
630
|
const rippleKeyCounter = React.useRef(0);
|
|
643
631
|
const timersRef = React.useRef([]);
|
|
@@ -648,7 +636,7 @@ function useRipple(options = {}) {
|
|
|
648
636
|
}, []);
|
|
649
637
|
const onMouseDown = React.useCallback(
|
|
650
638
|
(event) => {
|
|
651
|
-
if (disabled) return;
|
|
639
|
+
if (disabled || prefersReducedMotion) return;
|
|
652
640
|
const element = event.currentTarget;
|
|
653
641
|
const rect = element.getBoundingClientRect();
|
|
654
642
|
const x = event.clientX - rect.left;
|
|
@@ -664,9 +652,9 @@ function useRipple(options = {}) {
|
|
|
664
652
|
}, duration);
|
|
665
653
|
timersRef.current.push(timer);
|
|
666
654
|
},
|
|
667
|
-
[disabled, duration]
|
|
655
|
+
[disabled, duration, prefersReducedMotion]
|
|
668
656
|
);
|
|
669
|
-
const rippleElements = disabled ? null : /* @__PURE__ */ jsxRuntime.jsx(
|
|
657
|
+
const rippleElements = disabled || prefersReducedMotion ? null : /* @__PURE__ */ jsxRuntime.jsx(
|
|
670
658
|
"span",
|
|
671
659
|
{
|
|
672
660
|
"data-ripple-container": true,
|
|
@@ -674,7 +662,7 @@ function useRipple(options = {}) {
|
|
|
674
662
|
children: ripples.map((ripple) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
675
663
|
"span",
|
|
676
664
|
{
|
|
677
|
-
className: "animate-ripple absolute rounded-full opacity-12",
|
|
665
|
+
className: "animate-md-ripple absolute rounded-full opacity-12",
|
|
678
666
|
style: {
|
|
679
667
|
left: ripple.x,
|
|
680
668
|
top: ripple.y,
|
|
@@ -766,7 +754,7 @@ var Spinner = () => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
|
766
754
|
{
|
|
767
755
|
role: "progressbar",
|
|
768
756
|
"aria-label": "Loading",
|
|
769
|
-
className: "h-
|
|
757
|
+
className: "relative z-10 h-[18px] w-[18px] animate-spin",
|
|
770
758
|
xmlns: "http://www.w3.org/2000/svg",
|
|
771
759
|
fill: "none",
|
|
772
760
|
viewBox: "0 0 24 24",
|
|
@@ -787,7 +775,6 @@ var Button = React.forwardRef(
|
|
|
787
775
|
({
|
|
788
776
|
// Variant props (CVA)
|
|
789
777
|
variant = "filled",
|
|
790
|
-
color = "primary",
|
|
791
778
|
size = "medium",
|
|
792
779
|
fullWidth = false,
|
|
793
780
|
// Content props
|
|
@@ -800,64 +787,84 @@ var Button = React.forwardRef(
|
|
|
800
787
|
isDisabled = false,
|
|
801
788
|
// Styling
|
|
802
789
|
className,
|
|
803
|
-
// Other props
|
|
790
|
+
// Other button props
|
|
804
791
|
tabIndex = 0,
|
|
805
792
|
type = "button",
|
|
806
|
-
|
|
793
|
+
// Passed through to ButtonHeadless → useButton
|
|
807
794
|
...props
|
|
808
795
|
}, ref) => {
|
|
796
|
+
const buttonRef = React.useRef(null);
|
|
797
|
+
const resolvedRef = ref ?? buttonRef;
|
|
809
798
|
const groupCtx = useOptionalButtonGroup();
|
|
810
799
|
const isConnected = groupCtx?.variant === "connected";
|
|
811
|
-
if (process.env.NODE_ENV === "development") {
|
|
812
|
-
if (!children) {
|
|
813
|
-
console.warn(
|
|
814
|
-
"[Button] Button should have text content. Use IconButton for icon-only buttons."
|
|
815
|
-
);
|
|
816
|
-
}
|
|
817
|
-
if (icon && trailingIcon) {
|
|
818
|
-
console.warn("[Button] Button should have either icon or trailingIcon, not both.");
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
800
|
const isButtonDisabled = isDisabled || loading;
|
|
801
|
+
const [isPressed, setIsPressed] = React.useState(false);
|
|
802
|
+
const handlePressStart = React.useCallback(() => setIsPressed(true), []);
|
|
803
|
+
const handlePressEnd = React.useCallback(() => setIsPressed(false), []);
|
|
804
|
+
const { isHovered, hoverProps } = reactAria.useHover({ isDisabled: isButtonDisabled });
|
|
805
|
+
const { isFocusVisible, focusProps } = reactAria.useFocusRing();
|
|
822
806
|
const { onMouseDown: handleRipple, ripples } = useRipple({
|
|
823
807
|
disabled: isButtonDisabled || disableRipple
|
|
824
808
|
});
|
|
809
|
+
const buttonValue = props.value;
|
|
810
|
+
const isGroupSelected = isConnected && groupCtx && buttonValue ? groupCtx.selectedValues.has(buttonValue) : false;
|
|
825
811
|
const connectedClasses = isConnected && groupCtx ? [
|
|
826
|
-
...getConnectedRadiusClasses(groupCtx,
|
|
812
|
+
...getConnectedRadiusClasses(groupCtx, buttonValue),
|
|
827
813
|
groupCtx.enforceMinWidth ? "min-w-12" : ""
|
|
828
814
|
] : [];
|
|
815
|
+
const hasIcon = !!icon || !!trailingIcon;
|
|
816
|
+
if (process.env.NODE_ENV === "development") {
|
|
817
|
+
if (!children) {
|
|
818
|
+
console.warn(
|
|
819
|
+
"[Button] Button should have text content. Use IconButton for icon-only buttons."
|
|
820
|
+
);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
829
823
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
830
824
|
ButtonHeadless,
|
|
831
825
|
{
|
|
832
|
-
...
|
|
833
|
-
|
|
826
|
+
...reactAria.mergeProps(
|
|
827
|
+
hoverProps,
|
|
828
|
+
focusProps,
|
|
829
|
+
// Track pressed state via useButton's press lifecycle callbacks,
|
|
830
|
+
// rather than a separate usePress hook, to avoid event handler conflicts.
|
|
831
|
+
{ onPressStart: handlePressStart, onPressEnd: handlePressEnd },
|
|
832
|
+
props
|
|
833
|
+
),
|
|
834
|
+
ref: resolvedRef,
|
|
834
835
|
type,
|
|
835
836
|
isDisabled: isButtonDisabled,
|
|
836
|
-
...onPress && { onPress },
|
|
837
837
|
tabIndex,
|
|
838
838
|
onMouseDown: handleRipple,
|
|
839
|
+
...getInteractionDataAttributes({
|
|
840
|
+
isHovered,
|
|
841
|
+
isFocusVisible,
|
|
842
|
+
isPressed,
|
|
843
|
+
isDisabled: isButtonDisabled
|
|
844
|
+
}),
|
|
839
845
|
"data-variant": variant,
|
|
840
|
-
"data-
|
|
846
|
+
"data-with-icon": hasIcon ? "" : void 0,
|
|
847
|
+
"data-loading": loading ? "" : void 0,
|
|
848
|
+
"data-group-selected": isGroupSelected ? "" : void 0,
|
|
841
849
|
className: cn(
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
disabled: isButtonDisabled,
|
|
849
|
-
loading
|
|
850
|
-
}),
|
|
850
|
+
buttonVariants({ variant, size, fullWidth }),
|
|
851
|
+
// group/button: enables group-data-[x]/button child selectors in all slots
|
|
852
|
+
// (added here, not in CVA, following the Switch pattern)
|
|
853
|
+
"group/button",
|
|
854
|
+
// Asymmetric border-radius easing: expressive when selected, decelerate when not
|
|
855
|
+
isGroupSelected ? "btn-transition-selected" : "",
|
|
851
856
|
...connectedClasses,
|
|
852
857
|
// User custom classes
|
|
853
858
|
className
|
|
854
859
|
),
|
|
855
860
|
children: [
|
|
856
861
|
ripples,
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className:
|
|
860
|
-
|
|
862
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(buttonStateLayerVariants({ variant })), "aria-hidden": "true" }),
|
|
863
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(buttonFocusRingVariants()), "aria-hidden": "true" }),
|
|
864
|
+
icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(buttonIconVariants({ hidden: loading })), children: icon }),
|
|
865
|
+
loading && /* @__PURE__ */ jsxRuntime.jsx(Spinner, {}),
|
|
866
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(buttonLabelVariants()), children }),
|
|
867
|
+
trailingIcon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(buttonIconVariants({ hidden: loading })), children: trailingIcon })
|
|
861
868
|
]
|
|
862
869
|
}
|
|
863
870
|
);
|
|
@@ -871,6 +878,7 @@ var ButtonGroupHeadless = React.forwardRef(
|
|
|
871
878
|
size = "medium",
|
|
872
879
|
shape = "round",
|
|
873
880
|
selectionMode,
|
|
881
|
+
isDisabled = false,
|
|
874
882
|
// Selection — controlled
|
|
875
883
|
selectedValues: controlledValues,
|
|
876
884
|
onSelectionChange: onControlledChange,
|
|
@@ -892,7 +900,7 @@ var ButtonGroupHeadless = React.forwardRef(
|
|
|
892
900
|
const isControlled = controlledValues !== void 0;
|
|
893
901
|
const selectedValues = isControlled ? controlledValues : uncontrolledValues;
|
|
894
902
|
const handleSelectionChange = (value) => {
|
|
895
|
-
if (!selectionMode) return;
|
|
903
|
+
if (!selectionMode || isDisabled) return;
|
|
896
904
|
let nextValues;
|
|
897
905
|
if (selectionMode === "multi") {
|
|
898
906
|
nextValues = new Set(selectedValues);
|
|
@@ -933,6 +941,7 @@ var ButtonGroupHeadless = React.forwardRef(
|
|
|
933
941
|
selectionMode,
|
|
934
942
|
selectedValues,
|
|
935
943
|
onSelectionChange: handleSelectionChange,
|
|
944
|
+
isDisabled,
|
|
936
945
|
connectedInnerRadius: getInnerRadius(size),
|
|
937
946
|
connectedOuterRadius: getOuterRadius(shape, size),
|
|
938
947
|
enforceMinWidth: variant === "connected" && (size === "extra-small" || size === "small")
|
|
@@ -945,9 +954,15 @@ var ButtonGroupHeadless = React.forwardRef(
|
|
|
945
954
|
}
|
|
946
955
|
);
|
|
947
956
|
ButtonGroupHeadless.displayName = "ButtonGroupHeadless";
|
|
948
|
-
var
|
|
949
|
-
|
|
950
|
-
|
|
957
|
+
var buttonGroupRootVariants = classVarianceAuthority.cva(
|
|
958
|
+
[
|
|
959
|
+
// Layout
|
|
960
|
+
"items-center",
|
|
961
|
+
// Spatial motion for gap changes — standard spring (calm, utility UI)
|
|
962
|
+
"transition-[gap] duration-spring-standard-fast-spatial ease-spring-standard-fast-spatial",
|
|
963
|
+
// Disabled state — self-targeting data-[x]: selector on root
|
|
964
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-38"
|
|
965
|
+
],
|
|
951
966
|
{
|
|
952
967
|
variants: {
|
|
953
968
|
/**
|
|
@@ -995,6 +1010,14 @@ var buttonGroupVariants = classVarianceAuthority.cva(
|
|
|
995
1010
|
}
|
|
996
1011
|
}
|
|
997
1012
|
);
|
|
1013
|
+
var buttonGroupFocusRingVariants = classVarianceAuthority.cva([
|
|
1014
|
+
"pointer-events-none absolute inset-[-3px] rounded-[inherit]",
|
|
1015
|
+
"outline outline-2 outline-offset-0 outline-secondary",
|
|
1016
|
+
"transition-opacity duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
|
|
1017
|
+
"opacity-0",
|
|
1018
|
+
"group-data-[focus-visible]/button-group:opacity-100"
|
|
1019
|
+
]);
|
|
1020
|
+
var buttonGroupVariants = buttonGroupRootVariants;
|
|
998
1021
|
var ButtonGroup = React.forwardRef(
|
|
999
1022
|
({
|
|
1000
1023
|
variant = "standard",
|
|
@@ -1004,6 +1027,7 @@ var ButtonGroup = React.forwardRef(
|
|
|
1004
1027
|
selectedValues,
|
|
1005
1028
|
onSelectionChange,
|
|
1006
1029
|
defaultValue,
|
|
1030
|
+
isDisabled = false,
|
|
1007
1031
|
children,
|
|
1008
1032
|
className,
|
|
1009
1033
|
...htmlProps
|
|
@@ -1020,6 +1044,13 @@ var ButtonGroup = React.forwardRef(
|
|
|
1020
1044
|
},
|
|
1021
1045
|
[ref]
|
|
1022
1046
|
);
|
|
1047
|
+
const hasSelection = React.useMemo(() => {
|
|
1048
|
+
if (selectedValues) return selectedValues.size > 0;
|
|
1049
|
+
if (defaultValue) {
|
|
1050
|
+
return Array.isArray(defaultValue) ? defaultValue.length > 0 : true;
|
|
1051
|
+
}
|
|
1052
|
+
return false;
|
|
1053
|
+
}, [selectedValues, defaultValue]);
|
|
1023
1054
|
if (process.env.NODE_ENV === "development") {
|
|
1024
1055
|
const childArray = Array.isArray(children) ? children : [children];
|
|
1025
1056
|
for (const child of childArray) {
|
|
@@ -1068,7 +1099,12 @@ var ButtonGroup = React.forwardRef(
|
|
|
1068
1099
|
selectedValues,
|
|
1069
1100
|
onSelectionChange,
|
|
1070
1101
|
defaultValue,
|
|
1071
|
-
|
|
1102
|
+
isDisabled,
|
|
1103
|
+
className: cn(buttonGroupRootVariants({ variant, size }), "group/button-group", className),
|
|
1104
|
+
...getInteractionDataAttributes({ isDisabled }),
|
|
1105
|
+
"data-connected": variant === "connected" ? "" : void 0,
|
|
1106
|
+
"data-has-selection": hasSelection ? "" : void 0,
|
|
1107
|
+
"data-selection-mode": selectionMode ?? void 0,
|
|
1072
1108
|
children
|
|
1073
1109
|
}
|
|
1074
1110
|
);
|
|
@@ -1082,79 +1118,113 @@ var IconButtonHeadless = React.forwardRef(
|
|
|
1082
1118
|
tabIndex = 0,
|
|
1083
1119
|
onMouseDown,
|
|
1084
1120
|
type,
|
|
1085
|
-
|
|
1121
|
+
isSelected,
|
|
1122
|
+
isToggle = false,
|
|
1123
|
+
isDisabled = false,
|
|
1086
1124
|
"aria-label": ariaLabel,
|
|
1087
1125
|
title,
|
|
1088
1126
|
...props
|
|
1089
1127
|
}, forwardedRef) => {
|
|
1090
1128
|
const internalRef = React.useRef(null);
|
|
1091
1129
|
const ref = forwardedRef ?? internalRef;
|
|
1092
|
-
const { buttonProps } = reactAria.useButton(
|
|
1130
|
+
const { buttonProps, isPressed } = reactAria.useButton(
|
|
1093
1131
|
{
|
|
1094
1132
|
...props,
|
|
1095
|
-
// Ensure element type is 'button' for proper semantics
|
|
1096
1133
|
elementType: "button",
|
|
1097
|
-
|
|
1098
|
-
|
|
1134
|
+
"aria-label": ariaLabel,
|
|
1135
|
+
isDisabled
|
|
1099
1136
|
},
|
|
1100
1137
|
ref
|
|
1101
1138
|
);
|
|
1139
|
+
const { isHovered, hoverProps } = reactAria.useHover({ isDisabled });
|
|
1140
|
+
const { isFocusVisible, focusProps } = reactAria.useFocusRing();
|
|
1102
1141
|
const domProps = utils.filterDOMProps(props);
|
|
1103
|
-
const mergedProps = utils.mergeProps(
|
|
1104
|
-
|
|
1105
|
-
|
|
1142
|
+
const mergedProps = utils.mergeProps(buttonProps, hoverProps, focusProps, domProps, {
|
|
1143
|
+
tabIndex,
|
|
1144
|
+
className,
|
|
1145
|
+
onMouseDown,
|
|
1146
|
+
type: type ?? "button",
|
|
1147
|
+
...title && { title },
|
|
1148
|
+
// aria-pressed only when acting as a toggle button
|
|
1149
|
+
...isToggle && { "aria-pressed": isSelected ?? false }
|
|
1150
|
+
});
|
|
1151
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1152
|
+
"button",
|
|
1106
1153
|
{
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1154
|
+
...mergedProps,
|
|
1155
|
+
ref,
|
|
1156
|
+
type: type === "submit" ? "submit" : type === "reset" ? "reset" : "button",
|
|
1157
|
+
...getInteractionDataAttributes({
|
|
1158
|
+
isHovered,
|
|
1159
|
+
isFocusVisible,
|
|
1160
|
+
isPressed,
|
|
1161
|
+
...isToggle ? { isSelected: isSelected ?? false } : {},
|
|
1162
|
+
isDisabled
|
|
1163
|
+
}),
|
|
1164
|
+
"data-toggle": isToggle ? "" : void 0,
|
|
1165
|
+
children
|
|
1115
1166
|
}
|
|
1116
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1117
|
-
);
|
|
1118
|
-
return (
|
|
1119
|
-
// eslint-disable-next-line react/button-has-type
|
|
1120
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { ...mergedProps, ref, type: type ?? "button", children })
|
|
1121
1167
|
);
|
|
1122
1168
|
}
|
|
1123
1169
|
);
|
|
1124
1170
|
IconButtonHeadless.displayName = "IconButtonHeadless";
|
|
1125
|
-
var
|
|
1171
|
+
var iconButtonRootVariants = classVarianceAuthority.cva(
|
|
1126
1172
|
[
|
|
1127
|
-
//
|
|
1128
|
-
"relative inline-flex items-center justify-center
|
|
1129
|
-
"
|
|
1130
|
-
|
|
1131
|
-
//
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
"
|
|
1173
|
+
// Layout
|
|
1174
|
+
"relative inline-flex items-center justify-center",
|
|
1175
|
+
"cursor-pointer select-none",
|
|
1176
|
+
"overflow-hidden",
|
|
1177
|
+
// Corner radius driven by CSS variable — set per shape×size in compoundVariants.
|
|
1178
|
+
// Fallback 9999px is only reached if both shape and size props are absent,
|
|
1179
|
+
// which cannot happen in normal usage.
|
|
1180
|
+
"rounded-[var(--ib-radius,9999px)]",
|
|
1181
|
+
// Split MD3 transition via the existing btn-transition utility:
|
|
1182
|
+
// border-radius → emphasized-decelerate (no overshoot, no sharp-corner flash)
|
|
1183
|
+
// color/bg/border/opacity → standard-fast-effects (no overshoot on effects)
|
|
1184
|
+
// This is identical to the approach used by Button/connected ButtonGroup and is
|
|
1185
|
+
// the standard fix for the 9999px overshoot problem documented in styles.css.
|
|
1186
|
+
"btn-transition",
|
|
1187
|
+
// Background + border + text driven from CSS role variables
|
|
1188
|
+
"bg-[var(--ib-bg,transparent)]",
|
|
1189
|
+
"border border-[var(--ib-border,transparent)]",
|
|
1190
|
+
"text-[var(--ib-fg,currentColor)]",
|
|
1191
|
+
// Toggle: off state (data-toggle present but data-selected absent)
|
|
1192
|
+
// Uses doubly-chained selector to beat single-chain specificity of defaults
|
|
1193
|
+
"data-[toggle]:bg-[var(--ib-bg-off,var(--ib-bg,transparent))]",
|
|
1194
|
+
"data-[toggle]:text-[var(--ib-fg-off,var(--ib-fg,currentColor))]",
|
|
1195
|
+
// Selected state
|
|
1196
|
+
"data-[selected]:bg-[var(--ib-bg-on,var(--ib-bg,transparent))]",
|
|
1197
|
+
"data-[selected]:text-[var(--ib-fg-on,var(--ib-fg,currentColor))]",
|
|
1198
|
+
"data-[selected]:border-transparent",
|
|
1199
|
+
// Press shape-morph: radius collapses to --ib-radius-press on press
|
|
1200
|
+
// (only has visual effect when --ib-radius-press differs from --ib-radius)
|
|
1201
|
+
"data-[pressed]:rounded-[var(--ib-radius-press,var(--ib-radius,9999px))]",
|
|
1202
|
+
// Focus ring (outline, not a state layer — stays outside overflow-hidden
|
|
1203
|
+
// because it's drawn as outline on the root element itself)
|
|
1204
|
+
"outline-none",
|
|
1205
|
+
"group-data-[focus-visible]/icon-button:outline-2",
|
|
1206
|
+
"group-data-[focus-visible]/icon-button:outline-offset-2",
|
|
1207
|
+
"group-data-[focus-visible]/icon-button:outline-secondary",
|
|
1208
|
+
// Disabled — content opacity 38%, container handled per variant via CSS vars
|
|
1209
|
+
"data-[disabled]:cursor-not-allowed data-[disabled]:pointer-events-none",
|
|
1210
|
+
"data-[disabled]:text-on-surface/38"
|
|
1211
|
+
// Filled/tonal/outlined-selected backgrounds collapse to on-surface/12 — set
|
|
1212
|
+
// via compoundVariants on the root for variants that have a container.
|
|
1213
|
+
// For variants with transparent bg (standard, outlined-unselected) we do nothing.
|
|
1141
1214
|
],
|
|
1142
1215
|
{
|
|
1143
1216
|
variants: {
|
|
1144
1217
|
/**
|
|
1145
|
-
*
|
|
1218
|
+
* Visual style variant (MD3 icon button types)
|
|
1146
1219
|
*/
|
|
1147
1220
|
variant: {
|
|
1148
|
-
standard: "
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
tonal: "",
|
|
1153
|
-
// Container background
|
|
1154
|
-
outlined: "bg-transparent border border-outline"
|
|
1221
|
+
standard: "",
|
|
1222
|
+
filled: "data-[disabled]:bg-on-surface/12",
|
|
1223
|
+
tonal: "data-[disabled]:bg-on-surface/12",
|
|
1224
|
+
outlined: ""
|
|
1155
1225
|
},
|
|
1156
1226
|
/**
|
|
1157
|
-
* Color scheme
|
|
1227
|
+
* Color scheme — sets CSS role variables via compoundVariants.
|
|
1158
1228
|
*/
|
|
1159
1229
|
color: {
|
|
1160
1230
|
primary: "",
|
|
@@ -1163,180 +1233,400 @@ var iconButtonVariants = classVarianceAuthority.cva(
|
|
|
1163
1233
|
error: ""
|
|
1164
1234
|
},
|
|
1165
1235
|
/**
|
|
1166
|
-
*
|
|
1236
|
+
* Size tier (M3 Expressive 5-tier)
|
|
1167
1237
|
*/
|
|
1168
1238
|
size: {
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
medium: "h-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
// 48×48px
|
|
1239
|
+
xsmall: "h-8",
|
|
1240
|
+
small: "h-10",
|
|
1241
|
+
medium: "h-14",
|
|
1242
|
+
large: "h-24",
|
|
1243
|
+
xlarge: "h-[8.5rem]"
|
|
1175
1244
|
},
|
|
1176
1245
|
/**
|
|
1177
|
-
*
|
|
1246
|
+
* Width variant — adjusts container width
|
|
1178
1247
|
*/
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1248
|
+
width: {
|
|
1249
|
+
narrow: "",
|
|
1250
|
+
default: "",
|
|
1251
|
+
wide: ""
|
|
1182
1252
|
},
|
|
1183
1253
|
/**
|
|
1184
|
-
*
|
|
1254
|
+
* Shape — base values only; per-size radii set via compoundVariants below.
|
|
1255
|
+
*
|
|
1256
|
+
* round: --ib-radius = half the container height (true circle), set per size.
|
|
1257
|
+
* --ib-radius-press = square corner for that size (set per size).
|
|
1258
|
+
* Morph distance is small (e.g. 28px → 16px for medium), so the
|
|
1259
|
+
* emphasized-decelerate curve from btn-transition produces a smooth,
|
|
1260
|
+
* non-overshooting transition. The old 9999px fallback caused the
|
|
1261
|
+
* spring to overshoot below 0 = sharp-corner flash.
|
|
1262
|
+
* square: --ib-radius = size-tiered MD3 corner, set per size. No press morph.
|
|
1185
1263
|
*/
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1264
|
+
shape: {
|
|
1265
|
+
round: [],
|
|
1266
|
+
square: []
|
|
1189
1267
|
}
|
|
1190
1268
|
},
|
|
1191
|
-
/**
|
|
1192
|
-
* Compound variants - combinations of variant + color + selected
|
|
1193
|
-
*/
|
|
1194
1269
|
compoundVariants: [
|
|
1195
|
-
//
|
|
1196
|
-
//
|
|
1197
|
-
//
|
|
1198
|
-
{
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
},
|
|
1270
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
1271
|
+
// SIZE × WIDTH — container width
|
|
1272
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
1273
|
+
{ size: "xsmall", width: "narrow", className: "w-6" },
|
|
1274
|
+
{ size: "xsmall", width: "default", className: "w-8" },
|
|
1275
|
+
{ size: "xsmall", width: "wide", className: "w-10" },
|
|
1276
|
+
{ size: "small", width: "narrow", className: "w-8" },
|
|
1277
|
+
{ size: "small", width: "default", className: "w-10" },
|
|
1278
|
+
{ size: "small", width: "wide", className: "w-13" },
|
|
1279
|
+
{ size: "medium", width: "narrow", className: "w-12" },
|
|
1280
|
+
{ size: "medium", width: "default", className: "w-14" },
|
|
1281
|
+
{ size: "medium", width: "wide", className: "w-18" },
|
|
1282
|
+
{ size: "large", width: "narrow", className: "w-18" },
|
|
1283
|
+
{ size: "large", width: "default", className: "w-24" },
|
|
1284
|
+
{ size: "large", width: "wide", className: "w-32" },
|
|
1285
|
+
{ size: "xlarge", width: "narrow", className: "w-24" },
|
|
1286
|
+
{ size: "xlarge", width: "default", className: "w-[8.5rem]" },
|
|
1287
|
+
{ size: "xlarge", width: "wide", className: "w-42" },
|
|
1288
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
1289
|
+
// SHAPE × SIZE — corner radii for both round and square shapes
|
|
1290
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
1291
|
+
//
|
|
1292
|
+
// Round rest radius = half container height (true circle).
|
|
1293
|
+
// Using the exact half-height keeps the morph distance small, so the
|
|
1294
|
+
// no-overshoot emphasized-decelerate curve in btn-transition produces a
|
|
1295
|
+
// smooth animation. Using 9999px was the original cause of the sharp-
|
|
1296
|
+
// corner flash (the spring overshoots below 0 before settling).
|
|
1297
|
+
//
|
|
1298
|
+
// xsmall h-8 = 32px → half = 16px = 1rem
|
|
1299
|
+
// small h-10 = 40px → half = 20px = 1.25rem
|
|
1300
|
+
// medium h-14 = 56px → half = 28px = 1.75rem
|
|
1301
|
+
// large h-24 = 96px → half = 48px = 3rem
|
|
1302
|
+
// xlarge h-34 = 136px → half = 68px = 4.25rem
|
|
1303
|
+
//
|
|
1304
|
+
// Round press-morph target = MD3 square corner for that size tier.
|
|
1305
|
+
// Square rest radius = same MD3 corner (no morph).
|
|
1306
|
+
// ── round: rest radius (half height) ──────────────────────────────────
|
|
1307
|
+
{ shape: "round", size: "xsmall", className: "[--ib-radius:1rem]" },
|
|
1308
|
+
{ shape: "round", size: "small", className: "[--ib-radius:1.25rem]" },
|
|
1309
|
+
{ shape: "round", size: "medium", className: "[--ib-radius:1.75rem]" },
|
|
1310
|
+
{ shape: "round", size: "large", className: "[--ib-radius:3rem]" },
|
|
1311
|
+
{ shape: "round", size: "xlarge", className: "[--ib-radius:4.25rem]" },
|
|
1312
|
+
// ── round: press-morph target (square corner for that size) ───────────
|
|
1313
|
+
{ shape: "round", size: "xsmall", className: "[--ib-radius-press:0.75rem]" },
|
|
1314
|
+
{ shape: "round", size: "small", className: "[--ib-radius-press:0.75rem]" },
|
|
1315
|
+
{ shape: "round", size: "medium", className: "[--ib-radius-press:1rem]" },
|
|
1316
|
+
{ shape: "round", size: "large", className: "[--ib-radius-press:1.75rem]" },
|
|
1317
|
+
{ shape: "round", size: "xlarge", className: "[--ib-radius-press:1.75rem]" },
|
|
1318
|
+
// ── square: rest radius (MD3 shape scale) ─────────────────────────────
|
|
1319
|
+
// xsmall / small → 12px (0.75rem), medium → 16px (1rem), large / xlarge → 28px (1.75rem)
|
|
1320
|
+
{ shape: "square", size: "xsmall", className: "[--ib-radius:0.75rem]" },
|
|
1321
|
+
{ shape: "square", size: "small", className: "[--ib-radius:0.75rem]" },
|
|
1322
|
+
{ shape: "square", size: "medium", className: "[--ib-radius:1rem]" },
|
|
1323
|
+
{ shape: "square", size: "large", className: "[--ib-radius:1.75rem]" },
|
|
1324
|
+
{ shape: "square", size: "xlarge", className: "[--ib-radius:1.75rem]" },
|
|
1325
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
1326
|
+
// VARIANT × COLOR — CSS role variable assignments
|
|
1327
|
+
// Only variant × color (design-time decisions); no state variants here.
|
|
1328
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
1329
|
+
// ── STANDARD ──────────────────────────────────────────────────────────
|
|
1330
|
+
// Non-toggle standard: transparent bg, on-surface-variant fg
|
|
1331
|
+
// Selected: primary fg
|
|
1332
|
+
// State layer: on-surface-variant (unselected) / primary (selected)
|
|
1203
1333
|
{
|
|
1204
1334
|
variant: "standard",
|
|
1205
|
-
selected: true,
|
|
1206
|
-
className: "text-primary"
|
|
1207
|
-
},
|
|
1208
|
-
// ====================
|
|
1209
|
-
// FILLED VARIANTS (UNSELECTED)
|
|
1210
|
-
// ====================
|
|
1211
|
-
{
|
|
1212
|
-
variant: "filled",
|
|
1213
1335
|
color: "primary",
|
|
1214
|
-
|
|
1215
|
-
|
|
1336
|
+
className: [
|
|
1337
|
+
"[--ib-bg:transparent]",
|
|
1338
|
+
"[--ib-fg:var(--color-on-surface-variant)]",
|
|
1339
|
+
"[--ib-fg-on:var(--color-primary)]",
|
|
1340
|
+
"[--ib-sl:var(--color-on-surface-variant)]",
|
|
1341
|
+
// toggle-off same as non-toggle
|
|
1342
|
+
"[--ib-bg-off:transparent]",
|
|
1343
|
+
"[--ib-fg-off:var(--color-on-surface-variant)]",
|
|
1344
|
+
// toggle-on: selected
|
|
1345
|
+
"[--ib-bg-on:transparent]"
|
|
1346
|
+
]
|
|
1216
1347
|
},
|
|
1217
1348
|
{
|
|
1218
|
-
variant: "
|
|
1349
|
+
variant: "standard",
|
|
1219
1350
|
color: "secondary",
|
|
1220
|
-
|
|
1221
|
-
|
|
1351
|
+
className: [
|
|
1352
|
+
"[--ib-bg:transparent]",
|
|
1353
|
+
"[--ib-fg:var(--color-on-surface-variant)]",
|
|
1354
|
+
"[--ib-fg-on:var(--color-secondary)]",
|
|
1355
|
+
"[--ib-sl:var(--color-on-surface-variant)]",
|
|
1356
|
+
"[--ib-bg-off:transparent]",
|
|
1357
|
+
"[--ib-fg-off:var(--color-on-surface-variant)]",
|
|
1358
|
+
"[--ib-bg-on:transparent]"
|
|
1359
|
+
]
|
|
1222
1360
|
},
|
|
1223
1361
|
{
|
|
1224
|
-
variant: "
|
|
1362
|
+
variant: "standard",
|
|
1225
1363
|
color: "tertiary",
|
|
1226
|
-
|
|
1227
|
-
|
|
1364
|
+
className: [
|
|
1365
|
+
"[--ib-bg:transparent]",
|
|
1366
|
+
"[--ib-fg:var(--color-on-surface-variant)]",
|
|
1367
|
+
"[--ib-fg-on:var(--color-tertiary)]",
|
|
1368
|
+
"[--ib-sl:var(--color-on-surface-variant)]",
|
|
1369
|
+
"[--ib-bg-off:transparent]",
|
|
1370
|
+
"[--ib-fg-off:var(--color-on-surface-variant)]",
|
|
1371
|
+
"[--ib-bg-on:transparent]"
|
|
1372
|
+
]
|
|
1228
1373
|
},
|
|
1229
1374
|
{
|
|
1230
|
-
variant: "
|
|
1375
|
+
variant: "standard",
|
|
1231
1376
|
color: "error",
|
|
1232
|
-
|
|
1233
|
-
|
|
1377
|
+
className: [
|
|
1378
|
+
"[--ib-bg:transparent]",
|
|
1379
|
+
"[--ib-fg:var(--color-on-surface-variant)]",
|
|
1380
|
+
"[--ib-fg-on:var(--color-error)]",
|
|
1381
|
+
"[--ib-sl:var(--color-on-surface-variant)]",
|
|
1382
|
+
"[--ib-bg-off:transparent]",
|
|
1383
|
+
"[--ib-fg-off:var(--color-on-surface-variant)]",
|
|
1384
|
+
"[--ib-bg-on:transparent]"
|
|
1385
|
+
]
|
|
1234
1386
|
},
|
|
1235
|
-
//
|
|
1236
|
-
//
|
|
1237
|
-
//
|
|
1387
|
+
// ── FILLED ────────────────────────────────────────────────────────────
|
|
1388
|
+
// Non-toggle: bg primary / fg on-primary
|
|
1389
|
+
// Toggle off: bg surface-container-highest / fg primary
|
|
1390
|
+
// Toggle on (selected): bg primary / fg on-primary
|
|
1391
|
+
// State layer: on-primary (non-toggle / selected), primary (toggle-off)
|
|
1238
1392
|
{
|
|
1239
1393
|
variant: "filled",
|
|
1240
1394
|
color: "primary",
|
|
1241
|
-
|
|
1242
|
-
|
|
1395
|
+
className: [
|
|
1396
|
+
"[--ib-bg:var(--color-primary)]",
|
|
1397
|
+
"[--ib-fg:var(--color-on-primary)]",
|
|
1398
|
+
"[--ib-sl:var(--color-on-primary)]",
|
|
1399
|
+
"[--ib-bg-off:var(--color-surface-container-highest)]",
|
|
1400
|
+
"[--ib-fg-off:var(--color-primary)]",
|
|
1401
|
+
"[--ib-bg-on:var(--color-primary)]",
|
|
1402
|
+
"[--ib-fg-on:var(--color-on-primary)]"
|
|
1403
|
+
]
|
|
1243
1404
|
},
|
|
1244
1405
|
{
|
|
1245
1406
|
variant: "filled",
|
|
1246
1407
|
color: "secondary",
|
|
1247
|
-
|
|
1248
|
-
|
|
1408
|
+
className: [
|
|
1409
|
+
"[--ib-bg:var(--color-secondary)]",
|
|
1410
|
+
"[--ib-fg:var(--color-on-secondary)]",
|
|
1411
|
+
"[--ib-sl:var(--color-on-secondary)]",
|
|
1412
|
+
"[--ib-bg-off:var(--color-surface-container-highest)]",
|
|
1413
|
+
"[--ib-fg-off:var(--color-secondary)]",
|
|
1414
|
+
"[--ib-bg-on:var(--color-secondary)]",
|
|
1415
|
+
"[--ib-fg-on:var(--color-on-secondary)]"
|
|
1416
|
+
]
|
|
1249
1417
|
},
|
|
1250
1418
|
{
|
|
1251
1419
|
variant: "filled",
|
|
1252
1420
|
color: "tertiary",
|
|
1253
|
-
|
|
1254
|
-
|
|
1421
|
+
className: [
|
|
1422
|
+
"[--ib-bg:var(--color-tertiary)]",
|
|
1423
|
+
"[--ib-fg:var(--color-on-tertiary)]",
|
|
1424
|
+
"[--ib-sl:var(--color-on-tertiary)]",
|
|
1425
|
+
"[--ib-bg-off:var(--color-surface-container-highest)]",
|
|
1426
|
+
"[--ib-fg-off:var(--color-tertiary)]",
|
|
1427
|
+
"[--ib-bg-on:var(--color-tertiary)]",
|
|
1428
|
+
"[--ib-fg-on:var(--color-on-tertiary)]"
|
|
1429
|
+
]
|
|
1255
1430
|
},
|
|
1256
1431
|
{
|
|
1257
1432
|
variant: "filled",
|
|
1258
1433
|
color: "error",
|
|
1259
|
-
|
|
1260
|
-
|
|
1434
|
+
className: [
|
|
1435
|
+
"[--ib-bg:var(--color-error)]",
|
|
1436
|
+
"[--ib-fg:var(--color-on-error)]",
|
|
1437
|
+
"[--ib-sl:var(--color-on-error)]",
|
|
1438
|
+
"[--ib-bg-off:var(--color-surface-container-highest)]",
|
|
1439
|
+
"[--ib-fg-off:var(--color-error)]",
|
|
1440
|
+
"[--ib-bg-on:var(--color-error)]",
|
|
1441
|
+
"[--ib-fg-on:var(--color-on-error)]"
|
|
1442
|
+
]
|
|
1261
1443
|
},
|
|
1262
|
-
//
|
|
1263
|
-
//
|
|
1264
|
-
//
|
|
1444
|
+
// ── TONAL ─────────────────────────────────────────────────────────────
|
|
1445
|
+
// Non-toggle: bg secondary-container / fg on-secondary-container
|
|
1446
|
+
// Toggle off: bg surface-container-highest / fg on-surface-variant
|
|
1447
|
+
// Toggle on (selected): bg secondary-container / fg on-secondary-container
|
|
1265
1448
|
{
|
|
1266
1449
|
variant: "tonal",
|
|
1267
1450
|
color: "primary",
|
|
1268
|
-
|
|
1269
|
-
|
|
1451
|
+
className: [
|
|
1452
|
+
"[--ib-bg:var(--color-secondary-container)]",
|
|
1453
|
+
"[--ib-fg:var(--color-on-secondary-container)]",
|
|
1454
|
+
"[--ib-sl:var(--color-on-secondary-container)]",
|
|
1455
|
+
"[--ib-bg-off:var(--color-surface-container-highest)]",
|
|
1456
|
+
"[--ib-fg-off:var(--color-on-surface-variant)]",
|
|
1457
|
+
"[--ib-bg-on:var(--color-secondary-container)]",
|
|
1458
|
+
"[--ib-fg-on:var(--color-on-secondary-container)]"
|
|
1459
|
+
]
|
|
1270
1460
|
},
|
|
1271
1461
|
{
|
|
1272
1462
|
variant: "tonal",
|
|
1273
1463
|
color: "secondary",
|
|
1274
|
-
|
|
1275
|
-
|
|
1464
|
+
className: [
|
|
1465
|
+
"[--ib-bg:var(--color-secondary-container)]",
|
|
1466
|
+
"[--ib-fg:var(--color-on-secondary-container)]",
|
|
1467
|
+
"[--ib-sl:var(--color-on-secondary-container)]",
|
|
1468
|
+
"[--ib-bg-off:var(--color-surface-container-highest)]",
|
|
1469
|
+
"[--ib-fg-off:var(--color-on-surface-variant)]",
|
|
1470
|
+
"[--ib-bg-on:var(--color-secondary-container)]",
|
|
1471
|
+
"[--ib-fg-on:var(--color-on-secondary-container)]"
|
|
1472
|
+
]
|
|
1276
1473
|
},
|
|
1277
1474
|
{
|
|
1278
1475
|
variant: "tonal",
|
|
1279
1476
|
color: "tertiary",
|
|
1280
|
-
|
|
1281
|
-
|
|
1477
|
+
className: [
|
|
1478
|
+
"[--ib-bg:var(--color-tertiary-container)]",
|
|
1479
|
+
"[--ib-fg:var(--color-on-tertiary-container)]",
|
|
1480
|
+
"[--ib-sl:var(--color-on-tertiary-container)]",
|
|
1481
|
+
"[--ib-bg-off:var(--color-surface-container-highest)]",
|
|
1482
|
+
"[--ib-fg-off:var(--color-on-surface-variant)]",
|
|
1483
|
+
"[--ib-bg-on:var(--color-tertiary-container)]",
|
|
1484
|
+
"[--ib-fg-on:var(--color-on-tertiary-container)]"
|
|
1485
|
+
]
|
|
1282
1486
|
},
|
|
1283
1487
|
{
|
|
1284
1488
|
variant: "tonal",
|
|
1285
1489
|
color: "error",
|
|
1286
|
-
|
|
1287
|
-
|
|
1490
|
+
className: [
|
|
1491
|
+
"[--ib-bg:var(--color-error-container)]",
|
|
1492
|
+
"[--ib-fg:var(--color-on-error-container)]",
|
|
1493
|
+
"[--ib-sl:var(--color-on-error-container)]",
|
|
1494
|
+
"[--ib-bg-off:var(--color-surface-container-highest)]",
|
|
1495
|
+
"[--ib-fg-off:var(--color-on-surface-variant)]",
|
|
1496
|
+
"[--ib-bg-on:var(--color-error-container)]",
|
|
1497
|
+
"[--ib-fg-on:var(--color-on-error-container)]"
|
|
1498
|
+
]
|
|
1288
1499
|
},
|
|
1289
|
-
//
|
|
1290
|
-
//
|
|
1291
|
-
//
|
|
1500
|
+
// ── OUTLINED ──────────────────────────────────────────────────────────
|
|
1501
|
+
// Non-toggle: transparent bg, border-outline, on-surface-variant fg
|
|
1502
|
+
// Toggle off: same as non-toggle
|
|
1503
|
+
// Toggle on (selected): inverse-surface bg, inverse-on-surface fg, no border
|
|
1504
|
+
// Disabled: border becomes on-surface/12 (set via Tailwind utility on root)
|
|
1292
1505
|
{
|
|
1293
|
-
variant: "
|
|
1294
|
-
|
|
1295
|
-
className:
|
|
1506
|
+
variant: "outlined",
|
|
1507
|
+
color: "primary",
|
|
1508
|
+
className: [
|
|
1509
|
+
"[--ib-bg:transparent]",
|
|
1510
|
+
"[--ib-fg:var(--color-on-surface-variant)]",
|
|
1511
|
+
"[--ib-sl:var(--color-on-surface-variant)]",
|
|
1512
|
+
"[--ib-border:var(--color-outline)]",
|
|
1513
|
+
"[--ib-bg-off:transparent]",
|
|
1514
|
+
"[--ib-fg-off:var(--color-on-surface-variant)]",
|
|
1515
|
+
"[--ib-bg-on:var(--color-inverse-surface)]",
|
|
1516
|
+
"[--ib-fg-on:var(--color-inverse-on-surface)]",
|
|
1517
|
+
// Disabled outlined border
|
|
1518
|
+
"data-[disabled]:border-on-surface/12"
|
|
1519
|
+
]
|
|
1296
1520
|
},
|
|
1297
|
-
// ====================
|
|
1298
|
-
// OUTLINED VARIANTS (UNSELECTED)
|
|
1299
|
-
// ====================
|
|
1300
1521
|
{
|
|
1301
1522
|
variant: "outlined",
|
|
1302
|
-
|
|
1303
|
-
className:
|
|
1523
|
+
color: "secondary",
|
|
1524
|
+
className: [
|
|
1525
|
+
"[--ib-bg:transparent]",
|
|
1526
|
+
"[--ib-fg:var(--color-on-surface-variant)]",
|
|
1527
|
+
"[--ib-sl:var(--color-on-surface-variant)]",
|
|
1528
|
+
"[--ib-border:var(--color-outline)]",
|
|
1529
|
+
"[--ib-bg-off:transparent]",
|
|
1530
|
+
"[--ib-fg-off:var(--color-on-surface-variant)]",
|
|
1531
|
+
"[--ib-bg-on:var(--color-inverse-surface)]",
|
|
1532
|
+
"[--ib-fg-on:var(--color-inverse-on-surface)]",
|
|
1533
|
+
"data-[disabled]:border-on-surface/12"
|
|
1534
|
+
]
|
|
1304
1535
|
},
|
|
1305
|
-
// ====================
|
|
1306
|
-
// OUTLINED VARIANTS (SELECTED - uses inverse colors)
|
|
1307
|
-
// ====================
|
|
1308
1536
|
{
|
|
1309
1537
|
variant: "outlined",
|
|
1310
|
-
|
|
1311
|
-
className:
|
|
1538
|
+
color: "tertiary",
|
|
1539
|
+
className: [
|
|
1540
|
+
"[--ib-bg:transparent]",
|
|
1541
|
+
"[--ib-fg:var(--color-on-surface-variant)]",
|
|
1542
|
+
"[--ib-sl:var(--color-on-surface-variant)]",
|
|
1543
|
+
"[--ib-border:var(--color-outline)]",
|
|
1544
|
+
"[--ib-bg-off:transparent]",
|
|
1545
|
+
"[--ib-fg-off:var(--color-on-surface-variant)]",
|
|
1546
|
+
"[--ib-bg-on:var(--color-inverse-surface)]",
|
|
1547
|
+
"[--ib-fg-on:var(--color-inverse-on-surface)]",
|
|
1548
|
+
"data-[disabled]:border-on-surface/12"
|
|
1549
|
+
]
|
|
1550
|
+
},
|
|
1551
|
+
{
|
|
1552
|
+
variant: "outlined",
|
|
1553
|
+
color: "error",
|
|
1554
|
+
className: [
|
|
1555
|
+
"[--ib-bg:transparent]",
|
|
1556
|
+
"[--ib-fg:var(--color-on-surface-variant)]",
|
|
1557
|
+
"[--ib-sl:var(--color-on-surface-variant)]",
|
|
1558
|
+
"[--ib-border:var(--color-outline)]",
|
|
1559
|
+
"[--ib-bg-off:transparent]",
|
|
1560
|
+
"[--ib-fg-off:var(--color-on-surface-variant)]",
|
|
1561
|
+
"[--ib-bg-on:var(--color-inverse-surface)]",
|
|
1562
|
+
"[--ib-fg-on:var(--color-inverse-on-surface)]",
|
|
1563
|
+
"data-[disabled]:border-on-surface/12"
|
|
1564
|
+
]
|
|
1312
1565
|
}
|
|
1313
1566
|
],
|
|
1314
|
-
/**
|
|
1315
|
-
* Default variants
|
|
1316
|
-
*/
|
|
1317
1567
|
defaultVariants: {
|
|
1318
1568
|
variant: "standard",
|
|
1319
1569
|
color: "primary",
|
|
1320
1570
|
size: "medium",
|
|
1321
|
-
|
|
1322
|
-
|
|
1571
|
+
width: "default",
|
|
1572
|
+
shape: "round"
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
);
|
|
1576
|
+
var iconButtonStateLayerVariants = classVarianceAuthority.cva([
|
|
1577
|
+
"absolute inset-0 rounded-[inherit] pointer-events-none opacity-0",
|
|
1578
|
+
"bg-[var(--ib-sl,currentColor)]",
|
|
1579
|
+
// Effects transition (opacity — no spatial overshoot)
|
|
1580
|
+
"transition-opacity duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
|
|
1581
|
+
// Interaction opacities (MD3: hover 8%, focus/pressed 10%)
|
|
1582
|
+
"group-data-[hovered]/icon-button:opacity-8",
|
|
1583
|
+
"group-data-[focus-visible]/icon-button:opacity-10",
|
|
1584
|
+
"group-data-[pressed]/icon-button:opacity-10",
|
|
1585
|
+
// No state layer when disabled
|
|
1586
|
+
"group-data-[disabled]/icon-button:hidden"
|
|
1587
|
+
]);
|
|
1588
|
+
var iconButtonIconVariants = classVarianceAuthority.cva(
|
|
1589
|
+
[
|
|
1590
|
+
"relative z-10 inline-flex shrink-0 items-center justify-center",
|
|
1591
|
+
"transition-colors duration-spring-standard-fast-effects ease-spring-standard-fast-effects"
|
|
1592
|
+
],
|
|
1593
|
+
{
|
|
1594
|
+
variants: {
|
|
1595
|
+
size: {
|
|
1596
|
+
xsmall: "size-5",
|
|
1597
|
+
// 20dp
|
|
1598
|
+
small: "size-6",
|
|
1599
|
+
// 24dp
|
|
1600
|
+
medium: "size-6",
|
|
1601
|
+
// 24dp
|
|
1602
|
+
large: "size-8",
|
|
1603
|
+
// 32dp
|
|
1604
|
+
xlarge: "size-10"
|
|
1605
|
+
// 40dp
|
|
1606
|
+
}
|
|
1607
|
+
},
|
|
1608
|
+
defaultVariants: {
|
|
1609
|
+
size: "medium"
|
|
1323
1610
|
}
|
|
1324
1611
|
}
|
|
1325
1612
|
);
|
|
1326
1613
|
var IconButton = React.forwardRef(
|
|
1327
1614
|
({
|
|
1328
|
-
// Variant props (CVA)
|
|
1615
|
+
// Variant props (CVA / design-time)
|
|
1329
1616
|
variant = "standard",
|
|
1330
1617
|
color = "primary",
|
|
1331
1618
|
size = "medium",
|
|
1619
|
+
width = "default",
|
|
1620
|
+
shape = "round",
|
|
1332
1621
|
// IconButton specific props
|
|
1333
1622
|
children,
|
|
1623
|
+
selectedIcon,
|
|
1334
1624
|
value,
|
|
1335
1625
|
selected,
|
|
1336
1626
|
disableRipple = false,
|
|
1337
1627
|
className,
|
|
1338
1628
|
// React Aria props
|
|
1339
|
-
isDisabled
|
|
1629
|
+
isDisabled = false,
|
|
1340
1630
|
onPress,
|
|
1341
1631
|
onMouseDown,
|
|
1342
1632
|
"aria-label": ariaLabel,
|
|
@@ -1355,7 +1645,8 @@ var IconButton = React.forwardRef(
|
|
|
1355
1645
|
console.warn("[IconButton] IconButton should have an icon as children.");
|
|
1356
1646
|
}
|
|
1357
1647
|
}
|
|
1358
|
-
const
|
|
1648
|
+
const isToggle = selected !== void 0;
|
|
1649
|
+
const isSelected = isToggle ? selected ?? false : false;
|
|
1359
1650
|
const { onMouseDown: handleRipple, ripples } = useRipple({
|
|
1360
1651
|
disabled: isDisabled || disableRipple
|
|
1361
1652
|
});
|
|
@@ -1368,44 +1659,42 @@ var IconButton = React.forwardRef(
|
|
|
1368
1659
|
onMouseDown: mergedOnMouseDown,
|
|
1369
1660
|
isDisabled
|
|
1370
1661
|
});
|
|
1662
|
+
const isGroupSelected = isConnected && groupCtx && value ? groupCtx.selectedValues.has(value) : false;
|
|
1371
1663
|
const connectedClasses = isConnected && groupCtx ? [
|
|
1372
1664
|
...getConnectedRadiusClasses(groupCtx, value),
|
|
1373
1665
|
groupCtx.enforceMinWidth ? "min-w-12" : ""
|
|
1374
1666
|
] : [];
|
|
1667
|
+
const iconNode = isToggle && isSelected && selectedIcon ? selectedIcon : children;
|
|
1375
1668
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1376
1669
|
IconButtonHeadless,
|
|
1377
1670
|
{
|
|
1378
1671
|
ref,
|
|
1379
1672
|
className: cn(
|
|
1380
|
-
//
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
//
|
|
1385
|
-
"
|
|
1386
|
-
"focus-visible:outline-primary focus-visible:outline-2 focus-visible:outline-offset-2",
|
|
1387
|
-
// State layers (hover, focus, active) — effects token: opacity, no overshoot
|
|
1388
|
-
"before:absolute before:inset-0 before:rounded-[inherit]",
|
|
1389
|
-
"before:duration-spring-standard-fast-effects before:ease-spring-standard-fast-effects before:transition-opacity",
|
|
1390
|
-
"before:bg-current before:opacity-0",
|
|
1391
|
-
"hover:before:opacity-8",
|
|
1392
|
-
"focus-visible:before:opacity-12",
|
|
1393
|
-
"active:before:opacity-12",
|
|
1394
|
-
// CVA variants
|
|
1395
|
-
iconButtonVariants({ variant, color, size, selected: selected ?? false, isDisabled }),
|
|
1673
|
+
// Root CVA — sets CSS role variables, dimensions, shape, transitions
|
|
1674
|
+
iconButtonRootVariants({ variant, color, size, width, shape }),
|
|
1675
|
+
// Group scope for child slot selectors
|
|
1676
|
+
"group/icon-button",
|
|
1677
|
+
// ButtonGroup asymmetric border-radius easing (connected selection morph)
|
|
1678
|
+
isGroupSelected ? "btn-transition-selected" : "",
|
|
1396
1679
|
...connectedClasses,
|
|
1397
|
-
//
|
|
1680
|
+
// Consumer custom classes
|
|
1398
1681
|
className
|
|
1399
1682
|
),
|
|
1400
1683
|
"aria-label": ariaLabel,
|
|
1684
|
+
isSelected,
|
|
1685
|
+
isToggle,
|
|
1401
1686
|
"data-variant": variant,
|
|
1402
1687
|
"data-color": color,
|
|
1403
|
-
|
|
1688
|
+
"data-size": size,
|
|
1689
|
+
"data-width": width,
|
|
1690
|
+
"data-shape": shape,
|
|
1691
|
+
"data-group-selected": isGroupSelected ? "" : void 0,
|
|
1404
1692
|
...title && { title },
|
|
1405
1693
|
...mergedPropsValue,
|
|
1406
1694
|
children: [
|
|
1695
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: iconButtonStateLayerVariants(), "aria-hidden": "true", "data-state-layer": "" }),
|
|
1407
1696
|
ripples,
|
|
1408
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className:
|
|
1697
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: iconButtonIconVariants({ size }), "data-icon-slot": "", "aria-hidden": "true", children: iconNode })
|
|
1409
1698
|
]
|
|
1410
1699
|
}
|
|
1411
1700
|
);
|
|
@@ -4791,20 +5080,6 @@ var BadgeContent = React.forwardRef(
|
|
|
4791
5080
|
}
|
|
4792
5081
|
);
|
|
4793
5082
|
BadgeContent.displayName = "BadgeContent";
|
|
4794
|
-
var QUERY = "(prefers-reduced-motion: reduce)";
|
|
4795
|
-
function useReducedMotion() {
|
|
4796
|
-
const [reduced, setReduced] = React.useState(() => {
|
|
4797
|
-
if (typeof window === "undefined") return false;
|
|
4798
|
-
return window.matchMedia(QUERY).matches;
|
|
4799
|
-
});
|
|
4800
|
-
React.useEffect(() => {
|
|
4801
|
-
const mql = window.matchMedia(QUERY);
|
|
4802
|
-
const handler = (e) => setReduced(e.matches);
|
|
4803
|
-
mql.addEventListener("change", handler);
|
|
4804
|
-
return () => mql.removeEventListener("change", handler);
|
|
4805
|
-
}, []);
|
|
4806
|
-
return reduced;
|
|
4807
|
-
}
|
|
4808
5083
|
var Badge = React.forwardRef(
|
|
4809
5084
|
({
|
|
4810
5085
|
count,
|
|
@@ -14924,6 +15199,8 @@ exports.bottomSheetHandlePillVariants = bottomSheetHandlePillVariants;
|
|
|
14924
15199
|
exports.bottomSheetHandleWrapperVariants = bottomSheetHandleWrapperVariants;
|
|
14925
15200
|
exports.bottomSheetScrimVariants = bottomSheetScrimVariants;
|
|
14926
15201
|
exports.bottomSheetVariants = bottomSheetVariants;
|
|
15202
|
+
exports.buttonGroupFocusRingVariants = buttonGroupFocusRingVariants;
|
|
15203
|
+
exports.buttonGroupRootVariants = buttonGroupRootVariants;
|
|
14927
15204
|
exports.buttonGroupVariants = buttonGroupVariants;
|
|
14928
15205
|
exports.calendarCellVariants = calendarCellVariants;
|
|
14929
15206
|
exports.cardVariants = cardVariants;
|