infinity-ui-elements 1.5.1-beta.0 → 1.5.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/components/Avatar/Avatar.d.ts +59 -0
  2. package/dist/components/Avatar/Avatar.d.ts.map +1 -0
  3. package/dist/components/Avatar/Avatar.stories.d.ts +119 -0
  4. package/dist/components/Avatar/Avatar.stories.d.ts.map +1 -0
  5. package/dist/components/Avatar/index.d.ts +3 -0
  6. package/dist/components/Avatar/index.d.ts.map +1 -0
  7. package/dist/components/Badge/Badge.d.ts +1 -1
  8. package/dist/components/Button/Button.d.ts +1 -1
  9. package/dist/components/ButtonGroup/ButtonGroup.d.ts +26 -0
  10. package/dist/components/ButtonGroup/ButtonGroup.d.ts.map +1 -0
  11. package/dist/components/ButtonGroup/ButtonGroup.stories.d.ts +102 -0
  12. package/dist/components/ButtonGroup/ButtonGroup.stories.d.ts.map +1 -0
  13. package/dist/components/ButtonGroup/index.d.ts +3 -0
  14. package/dist/components/ButtonGroup/index.d.ts.map +1 -0
  15. package/dist/components/Checkbox/Checkbox.d.ts +1 -1
  16. package/dist/components/Counter/Counter.d.ts +1 -1
  17. package/dist/components/Dropdown/Dropdown.d.ts +1 -1
  18. package/dist/components/Dropdown/Dropdown.stories.d.ts +1 -1
  19. package/dist/components/Dropdown/DropdownMenu.d.ts +4 -0
  20. package/dist/components/Dropdown/DropdownMenu.d.ts.map +1 -1
  21. package/dist/components/FormHeader/FormHeader.d.ts.map +1 -1
  22. package/dist/components/Link/Link.d.ts +1 -1
  23. package/dist/components/Modal/Modal.d.ts +78 -0
  24. package/dist/components/Modal/Modal.d.ts.map +1 -0
  25. package/dist/components/Modal/Modal.stories.d.ts +20 -0
  26. package/dist/components/Modal/Modal.stories.d.ts.map +1 -0
  27. package/dist/components/Modal/index.d.ts +3 -0
  28. package/dist/components/Modal/index.d.ts.map +1 -0
  29. package/dist/components/Pagination/Pagination.d.ts +81 -0
  30. package/dist/components/Pagination/Pagination.d.ts.map +1 -0
  31. package/dist/components/Pagination/Pagination.stories.d.ts +22 -0
  32. package/dist/components/Pagination/Pagination.stories.d.ts.map +1 -0
  33. package/dist/components/Pagination/index.d.ts +3 -0
  34. package/dist/components/Pagination/index.d.ts.map +1 -0
  35. package/dist/components/Radio/Radio.d.ts +1 -1
  36. package/dist/components/SearchableDropdown/SearchableDropdown.d.ts +4 -0
  37. package/dist/components/SearchableDropdown/SearchableDropdown.d.ts.map +1 -1
  38. package/dist/components/SearchableDropdown/SearchableDropdown.stories.d.ts +10 -9
  39. package/dist/components/SearchableDropdown/SearchableDropdown.stories.d.ts.map +1 -1
  40. package/dist/components/Select/Select.d.ts +148 -0
  41. package/dist/components/Select/Select.d.ts.map +1 -0
  42. package/dist/components/Select/Select.stories.d.ts +32 -0
  43. package/dist/components/Select/Select.stories.d.ts.map +1 -0
  44. package/dist/components/Select/index.d.ts +2 -0
  45. package/dist/components/Select/index.d.ts.map +1 -0
  46. package/dist/components/Switch/Switch.d.ts +1 -1
  47. package/dist/components/TabItem/TabItem.d.ts +1 -1
  48. package/dist/components/Table/Table.d.ts +95 -0
  49. package/dist/components/Table/Table.d.ts.map +1 -0
  50. package/dist/components/Table/Table.stories.d.ts +21 -0
  51. package/dist/components/Table/Table.stories.d.ts.map +1 -0
  52. package/dist/components/Table/index.d.ts +3 -0
  53. package/dist/components/Table/index.d.ts.map +1 -0
  54. package/dist/components/TextArea/TextArea.d.ts +1 -1
  55. package/dist/components/TextField/TextField.d.ts +1 -1
  56. package/dist/index.css +1 -1
  57. package/dist/index.d.ts +6 -0
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.esm.js +740 -42
  60. package/dist/index.esm.js.map +1 -1
  61. package/dist/index.js +751 -40
  62. package/dist/index.js.map +1 -1
  63. package/dist/lib/icons.d.ts +4 -1
  64. package/dist/lib/icons.d.ts.map +1 -1
  65. package/dist/lib/index.d.ts +3 -0
  66. package/dist/lib/index.d.ts.map +1 -0
  67. package/dist/lib/utils.d.ts.map +1 -1
  68. package/package.json +6 -1
package/dist/index.esm.js CHANGED
@@ -5,7 +5,9 @@ import { clsx } from 'clsx';
5
5
  import { twMerge } from 'tailwind-merge';
6
6
  import { Slot } from '@radix-ui/react-slot';
7
7
  import { PulseLoader, ClipLoader } from 'react-spinners';
8
- import { ExternalLink, Loader2, Search } from 'lucide-react';
8
+ import { ExternalLink, Loader2, Search, X, ChevronDown, ChevronLeft, ChevronRight } from 'lucide-react';
9
+ import { createPortal } from 'react-dom';
10
+ import { flexRender } from '@tanstack/react-table';
9
11
 
10
12
  // Define patterns for custom classes that should be preserved
11
13
  // This approach is more scalable than hardcoding individual class names
@@ -46,6 +48,106 @@ function cn(...inputs) {
46
48
  return clsx(mergedStandard, customClasses);
47
49
  }
48
50
 
51
+ // Helper function to get the text utility class name
52
+ function getTextClassName(variant = "body", size = "medium", weight = "regular", color = "default") {
53
+ // Build the base class name
54
+ let baseClass = `text-${variant}`;
55
+ // Add size
56
+ if (size) {
57
+ baseClass += `-${size}`;
58
+ }
59
+ // Add weight
60
+ if (weight) {
61
+ baseClass += `-${weight}`;
62
+ }
63
+ // Add color class separately
64
+ const colorClass = `text-color-${color}`;
65
+ return `${baseClass} ${colorClass}`;
66
+ }
67
+ const Text = React.forwardRef(({ className, variant = "body", size = "medium", weight = "regular", color = "default", as = "p", children, ...props }, ref) => {
68
+ const Component = as;
69
+ const textClass = getTextClassName(variant, size, weight, color);
70
+ return React.createElement(Component, {
71
+ className: cn(textClass, className),
72
+ ref,
73
+ ...props,
74
+ }, children);
75
+ });
76
+ Text.displayName = "Text";
77
+
78
+ const avatarVariants = cva("inline-flex items-center justify-center font-medium text-center select-none", {
79
+ variants: {
80
+ color: {
81
+ a1: "bg-avatar-fill-a1-bg text-avatar-fill-a1-on-bg",
82
+ a2: "bg-avatar-fill-a2-bg text-avatar-fill-a2-on-bg",
83
+ a3: "bg-avatar-fill-a3-bg text-avatar-fill-a3-on-bg",
84
+ a4: "bg-avatar-fill-a4-bg text-avatar-fill-a4-on-bg",
85
+ a5: "bg-avatar-fill-a5-bg text-avatar-fill-a5-on-bg",
86
+ },
87
+ size: {
88
+ small: "h-[24px] w-[24px] text-body-medium-regular rounded-large",
89
+ medium: "h-[32px] w-[32px] text-body-medium-regular rounded-xlarge",
90
+ },
91
+ },
92
+ defaultVariants: {
93
+ color: "a1",
94
+ size: "medium",
95
+ },
96
+ });
97
+ const statusVariants = cva("absolute flex items-center justify-center rounded-full border-2 border-surface-fill-neutral-intense", {
98
+ variants: {
99
+ size: {
100
+ small: "h-5 w-5 -bottom-0.5 -right-0.5",
101
+ medium: "h-6 w-6 -bottom-1 -right-1",
102
+ },
103
+ statusColor: {
104
+ positive: "bg-action-fill-positive-default",
105
+ negative: "bg-action-fill-negative-default",
106
+ notice: "bg-action-fill-notice-default",
107
+ info: "bg-action-fill-info-default",
108
+ neutral: "bg-action-fill-neutral-default",
109
+ },
110
+ },
111
+ defaultVariants: {
112
+ size: "medium",
113
+ statusColor: "notice",
114
+ },
115
+ });
116
+ const Avatar = React.forwardRef(({ className, color, size, children, src, alt, showStatus = false, statusColor = "notice", statusIcon, label, trailingComponent, containerClassName, ...props }, ref) => {
117
+ const [imageError, setImageError] = React.useState(false);
118
+ const handleImageError = () => {
119
+ setImageError(true);
120
+ };
121
+ const getStatusIconSize = () => {
122
+ switch (size) {
123
+ case "small":
124
+ return "h-3.5 w-3.5";
125
+ case "medium":
126
+ return "h-4 w-4";
127
+ default:
128
+ return "h-4 w-4";
129
+ }
130
+ };
131
+ const getTextSize = () => {
132
+ switch (size) {
133
+ case "small":
134
+ return "small";
135
+ case "medium":
136
+ return "medium";
137
+ default:
138
+ return "medium";
139
+ }
140
+ };
141
+ const avatarElement = (jsxs("div", { className: "relative inline-block", children: [jsx("div", { className: cn(avatarVariants({ color, size }), className), ...props, children: src && !imageError ? (jsx("img", { src: src, alt: alt || "Avatar", className: cn("h-full w-full object-cover", size === "small" ? "rounded-large" : "rounded-xlarge"), onError: handleImageError })) : (children) }), showStatus && (jsx("div", { className: cn(statusVariants({ size, statusColor })), children: statusIcon && (jsx("span", { className: cn("text-action-ink-on-primary-normal", getStatusIconSize()), children: statusIcon })) }))] }));
142
+ // If no label or trailing component, return just the avatar
143
+ if (!label && !trailingComponent) {
144
+ return jsx("div", { ref: ref, children: avatarElement });
145
+ }
146
+ // Otherwise, return avatar with label and/or trailing component
147
+ return (jsxs("div", { ref: ref, className: cn("inline-flex items-center gap-3", containerClassName), children: [avatarElement, label && (jsx(Text, { variant: "body", size: getTextSize(), weight: "medium", color: "default", as: "span", children: label })), trailingComponent && (jsx("span", { className: "ml-auto", children: trailingComponent }))] }));
148
+ });
149
+ Avatar.displayName = "Avatar";
150
+
49
151
  const badgeVariants = cva("inline-flex items-center whitespace-nowrap transition-colors", {
50
152
  variants: {
51
153
  variant: {
@@ -462,32 +564,145 @@ const Button = React.forwardRef(({ className, variant = "primary", color = "prim
462
564
  });
463
565
  Button.displayName = "Button";
464
566
 
465
- // Helper function to get the text utility class name
466
- function getTextClassName(variant = "body", size = "medium", weight = "regular", color = "default") {
467
- // Build the base class name
468
- let baseClass = `text-${variant}`;
469
- // Add size
470
- if (size) {
471
- baseClass += `-${size}`;
472
- }
473
- // Add weight
474
- if (weight) {
475
- baseClass += `-${weight}`;
476
- }
477
- // Add color class separately
478
- const colorClass = `text-color-${color}`;
479
- return `${baseClass} ${colorClass}`;
480
- }
481
- const Text = React.forwardRef(({ className, variant = "body", size = "medium", weight = "regular", color = "default", as = "p", children, ...props }, ref) => {
482
- const Component = as;
483
- const textClass = getTextClassName(variant, size, weight, color);
484
- return React.createElement(Component, {
485
- className: cn(textClass, className),
486
- ref,
487
- ...props,
488
- }, children);
567
+ const buttonGroupVariants = cva("inline-flex", {
568
+ variants: {
569
+ variant: {
570
+ attached: "",
571
+ separated: "",
572
+ },
573
+ orientation: {
574
+ horizontal: "flex-row",
575
+ vertical: "flex-col",
576
+ },
577
+ size: {
578
+ xsmall: "",
579
+ small: "",
580
+ medium: "",
581
+ large: "",
582
+ },
583
+ isFullWidth: {
584
+ true: "w-full",
585
+ false: "w-fit",
586
+ },
587
+ isDisabled: {
588
+ true: "pointer-events-none opacity-50",
589
+ false: "",
590
+ },
591
+ },
592
+ compoundVariants: [
593
+ {
594
+ variant: "separated",
595
+ orientation: "horizontal",
596
+ class: "gap-2",
597
+ },
598
+ {
599
+ variant: "separated",
600
+ orientation: "vertical",
601
+ class: "gap-2",
602
+ },
603
+ ],
604
+ defaultVariants: {
605
+ variant: "attached",
606
+ orientation: "horizontal",
607
+ size: "medium",
608
+ isFullWidth: false,
609
+ isDisabled: false,
610
+ },
489
611
  });
490
- Text.displayName = "Text";
612
+ const ButtonGroup = React.forwardRef(({ className, variant = "attached", orientation = "horizontal", size = "medium", isDisabled = false, isFullWidth = false, value, onChange, children, ...props }, ref) => {
613
+ const childrenArray = React.Children.toArray(children);
614
+ const isControlled = value !== undefined && onChange !== undefined;
615
+ return (jsx("div", { ref: ref, className: cn(buttonGroupVariants({
616
+ variant,
617
+ orientation,
618
+ size,
619
+ isDisabled,
620
+ isFullWidth,
621
+ }), className), role: "group", ...props, children: childrenArray.map((child, index) => {
622
+ if (!React.isValidElement(child)) {
623
+ return child;
624
+ }
625
+ const isFirst = index === 0;
626
+ const isLast = index === childrenArray.length - 1;
627
+ const isMiddle = !isFirst && !isLast;
628
+ // Get value from child props for controlled mode
629
+ const childValue = child.props.value;
630
+ const isSelected = isControlled && childValue === value;
631
+ // Build classes to apply border radius removal and borders
632
+ let groupClasses = "";
633
+ // Only apply connected styling for "attached" variant
634
+ if (variant === "attached") {
635
+ if (orientation === "horizontal") {
636
+ if (isFirst) {
637
+ groupClasses = "rounded-r-none border-r-0";
638
+ }
639
+ else if (isLast) {
640
+ groupClasses = "rounded-l-none";
641
+ }
642
+ else if (isMiddle) {
643
+ groupClasses = "rounded-none border-r-0";
644
+ }
645
+ }
646
+ else {
647
+ // vertical
648
+ if (isFirst) {
649
+ groupClasses = "rounded-b-none border-b-0";
650
+ }
651
+ else if (isLast) {
652
+ groupClasses = "rounded-t-none";
653
+ }
654
+ else if (isMiddle) {
655
+ groupClasses = "rounded-none border-b-0";
656
+ }
657
+ }
658
+ }
659
+ // Determine the variant to use
660
+ const childVariant = child.props.variant;
661
+ const hasExplicitVariant = childVariant !== undefined;
662
+ // For controlled mode with explicit variant, maintain the variant
663
+ // and add styling classes for selected state
664
+ let finalVariant = childVariant;
665
+ let selectedStateClasses = "";
666
+ if (isControlled) {
667
+ if (hasExplicitVariant) {
668
+ // Keep the child's variant and add selected state styling
669
+ if (isSelected && childVariant === "tertiary") {
670
+ selectedStateClasses = "bg-action-fill-primary-faded";
671
+ }
672
+ else if (isSelected && childVariant === "secondary") {
673
+ selectedStateClasses =
674
+ "bg-action-fill-primary-faded border-action-outline-primary-faded";
675
+ }
676
+ }
677
+ else {
678
+ // No explicit variant: use primary for selected, secondary for unselected
679
+ finalVariant = isSelected ? "primary" : "secondary";
680
+ }
681
+ }
682
+ // Clone child and add our classes and handlers
683
+ return React.cloneElement(child, {
684
+ ...child.props,
685
+ className: cn(child.props.className, groupClasses, isFullWidth && "flex-1",
686
+ // For attached variant, ensure proper layering
687
+ variant === "attached" && "relative", variant === "attached" && !isSelected && "hover:z-10 focus:z-10", variant === "attached" && isSelected && "z-20",
688
+ // Apply selected state classes for explicit variants
689
+ selectedStateClasses),
690
+ disabled: isDisabled || child.props.disabled,
691
+ size: size || child.props.size,
692
+ // If controlled and has value, handle click
693
+ onClick: isControlled && childValue !== undefined
694
+ ? (e) => {
695
+ child.props.onClick?.(e);
696
+ if (!isDisabled && !child.props.disabled) {
697
+ onChange(childValue);
698
+ }
699
+ }
700
+ : child.props.onClick,
701
+ variant: finalVariant,
702
+ });
703
+ }) }));
704
+ });
705
+ ButtonGroup.displayName = "ButtonGroup";
491
706
 
492
707
  const FormFooter = React.forwardRef(({ helperText, trailingText, validationState = "default", size = "medium", isDisabled = false, className, helperTextClassName, trailingTextClassName, }, ref) => {
493
708
  // Size-based configurations
@@ -579,17 +794,17 @@ const Icon = ({ name, size = 24, className = "", style = {}, ...props }) => {
579
794
  console.error(`Invalid SVG content for icon "${String(name)}"`);
580
795
  return null;
581
796
  }
582
- // Extract viewBox attribute
797
+ // Get the viewBox for proper scaling
583
798
  const viewBox = svgElement.getAttribute("viewBox") || "0 0 24 24";
584
- let innerHTML = svgElement.innerHTML;
585
- // Replace hardcoded fill and stroke colors with currentColor
586
- // This allows the icon color to be controlled via CSS color property
587
- innerHTML = innerHTML
799
+ // Get all SVG content as string and replace hardcoded colors with currentColor
800
+ let innerSVG = svgElement.innerHTML;
801
+ // Replace common hardcoded colors with currentColor
802
+ // This allows the icon to inherit text color from parent
803
+ innerSVG = innerSVG
588
804
  .replace(/fill="[^"]*"/g, 'fill="currentColor"')
589
805
  .replace(/stroke="[^"]*"/g, 'stroke="currentColor"');
590
- return (jsx("svg", { width: size, height: size, viewBox: viewBox, fill: "none", xmlns: "http://www.w3.org/2000/svg", className: className, style: style, ...props, dangerouslySetInnerHTML: { __html: innerHTML } }));
806
+ return (jsx("svg", { width: size, height: size, viewBox: viewBox, className: className, style: style, xmlns: "http://www.w3.org/2000/svg", ...props, dangerouslySetInnerHTML: { __html: innerSVG } }));
591
807
  };
592
- Icon.displayName = "Icon";
593
808
  /**
594
809
  * Get all available icon names from the registry
595
810
  * @returns Array of registered icon names
@@ -1203,7 +1418,7 @@ const Link = React.forwardRef(({ className, type = "anchor", color = "primary",
1203
1418
  });
1204
1419
  Link.displayName = "Link";
1205
1420
 
1206
- const DropdownMenu = React.forwardRef(({ items = [], sectionHeading, isLoading = false, isEmpty = false, emptyTitle = "No Search Results Found", emptyDescription = "Add description of what the user can search for here.", emptyLinkText = "Link to support site", onEmptyLinkClick, primaryButtonText = "Primary", secondaryButtonText = "Secondary", onPrimaryClick, onSecondaryClick, showChevron = false, emptyIcon, disableFooter = false, onClose, focusedIndex = -1, className, width = "auto", }, ref) => {
1421
+ const DropdownMenu = React.forwardRef(({ items = [], sectionHeading, isLoading = false, isEmpty = false, emptyTitle = "No Search Results Found", emptyDescription = "Add description of what the user can search for here.", emptyLinkText = "Link to support site", onEmptyLinkClick, primaryButtonText = "Primary", secondaryButtonText = "Secondary", onPrimaryClick, onSecondaryClick, showChevron = false, emptyIcon, disableFooter = false, footerLayout = "horizontal", onClose, focusedIndex = -1, className, width = "auto", }, ref) => {
1207
1422
  const renderContent = () => {
1208
1423
  if (isLoading) {
1209
1424
  return (jsx("div", { className: "flex flex-col items-center justify-center py-12 px-6", children: jsx(Loader2, { className: "w-12 h-12 text-action-ink-primary-normal mb-4 animate-spin" }) }));
@@ -1217,10 +1432,12 @@ const DropdownMenu = React.forwardRef(({ items = [], sectionHeading, isLoading =
1217
1432
  }, containerClassName: cn(index === focusedIndex && "bg-action-fill-primary-faded") }, item.id))) })] }));
1218
1433
  };
1219
1434
  const widthClass = width === "full" ? "w-full" : width === "auto" ? "w-auto" : "";
1220
- return (jsxs("div", { ref: ref, className: cn("bg-surface-fill-primary-normal rounded-large overflow-hidden", widthClass, className), style: {
1435
+ return (jsxs("div", { ref: ref, className: cn("bg-white rounded-large overflow-hidden", widthClass, className), style: {
1221
1436
  boxShadow: "0 1px 2px rgba(25, 25, 30, 0.1), 0 2px 6px rgba(25, 25, 30, 0.06)",
1222
1437
  ...(width !== "full" && width !== "auto" ? { width } : {}),
1223
- }, children: [renderContent(), !disableFooter && (jsxs("div", { className: "flex flex-col", children: [jsx(Divider, { thickness: "thin", variant: "muted" }), jsxs("div", { className: "flex items-center gap-3 p-4", children: [jsx(Button, { variant: "secondary", color: "primary", size: "medium", isFullWidth: true, onClick: onSecondaryClick, children: secondaryButtonText }), jsx(Button, { variant: "primary", color: "primary", size: "medium", isFullWidth: true, onClick: onPrimaryClick, children: primaryButtonText })] })] }))] }));
1438
+ }, children: [renderContent(), !disableFooter && (jsxs("div", { className: "flex flex-col", children: [jsx(Divider, { thickness: "thin", variant: "muted" }), jsxs("div", { className: cn("flex gap-3 p-4", footerLayout === "vertical"
1439
+ ? "flex-col"
1440
+ : "items-center flex-row"), children: [jsx(Button, { variant: "secondary", color: "primary", size: "medium", isFullWidth: true, onClick: onSecondaryClick, children: secondaryButtonText }), jsx(Button, { variant: "primary", color: "primary", size: "medium", isFullWidth: true, onClick: onPrimaryClick, children: primaryButtonText })] })] }))] }));
1224
1441
  });
1225
1442
  DropdownMenu.displayName = "DropdownMenu";
1226
1443
 
@@ -1537,10 +1754,370 @@ const FormHeader = React.forwardRef(({ label, size = "medium", isOptional = fals
1537
1754
  },
1538
1755
  };
1539
1756
  const config = sizeConfig[size];
1540
- return (jsxs("div", { ref: ref, className: cn("flex items-center justify-between px-1", config.gap, className), children: [jsxs("div", { className: cn("flex items-center", config.gap), children: [jsxs("label", { htmlFor: htmlFor, className: cn("flex items-center", labelClassName), children: [jsx(Text, { as: "span", variant: "body", size: config.textSize, weight: "semibold", color: "subtle", children: label }), isRequired && (jsx(Text, { as: "span", variant: "body", size: config.textSize, weight: "semibold", className: "text-feedback-ink-negative-subtle ml-0.5", children: "*" })), isOptional && (jsx(Text, { as: "span", variant: "body", size: config.textSize, weight: "regular", className: "text-surface-ink-neutral-muted ml-1", children: "(optional)" }))] }), infoDescription && (jsx(Tooltip, { description: infoDescription, heading: infoHeading, children: jsxs("svg", { width: config.iconSize, height: config.iconSize, viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: "text-surface-ink-neutral-muted cursor-help", children: [jsx("circle", { cx: "7", cy: "7", r: "6", stroke: "currentColor", strokeWidth: "1" }), jsx("path", { d: "M7 6V10M7 4.5V4", stroke: "currentColor", strokeWidth: "1", strokeLinecap: "round" })] }) }))] }), linkText && (jsx("a", { href: linkHref, onClick: onLinkClick, className: cn("text-surface-ink-primary-normal hover:text-surface-ink-primary-hover transition-colors cursor-pointer font-display font-semibold leading-tight shrink-0", size === "small" && "text-xs", size === "medium" && "text-xs", size === "large" && "text-sm", linkClassName), children: linkText }))] }));
1757
+ return (jsxs("div", { ref: ref, className: cn("flex items-center justify-between px-1", config.gap, className), children: [jsxs("div", { className: cn("flex items-center", config.gap), children: [jsxs("label", { htmlFor: htmlFor, className: cn("flex items-center", labelClassName), children: [jsx(Text, { as: "span", variant: "body", size: config.textSize, weight: "semibold", color: "subtle", children: label }), isRequired && (jsx(Text, { as: "span", variant: "body", size: config.textSize, weight: "semibold", color: "negative", className: "ml-0.5", children: "*" })), isOptional && (jsx(Text, { as: "span", variant: "body", size: config.textSize, weight: "regular", className: "text-surface-ink-neutral-muted italic ml-1", children: "(optional)" }))] }), infoDescription && (jsx(Tooltip, { description: infoDescription, heading: infoHeading, children: jsxs("svg", { width: config.iconSize, height: config.iconSize, viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: "text-surface-ink-neutral-muted", children: [jsx("circle", { cx: "7", cy: "7", r: "6", stroke: "currentColor", strokeWidth: "1" }), jsx("path", { d: "M7 6V10M7 4.5V4", stroke: "currentColor", strokeWidth: "1", strokeLinecap: "round" })] }) }))] }), linkText && (jsx("a", { href: linkHref, onClick: onLinkClick, className: cn("text-surface-ink-primary-normal hover:text-surface-ink-primary-hover transition-colors cursor-pointer font-display font-semibold leading-tight shrink-0", size === "small" && "text-xs", size === "medium" && "text-xs", size === "large" && "text-sm", linkClassName), children: linkText }))] }));
1541
1758
  });
1542
1759
  FormHeader.displayName = "FormHeader";
1543
1760
 
1761
+ const Modal = React.forwardRef(({ isOpen, onClose, title, description, footer, children, size = "medium", showCloseButton = true, closeOnOverlayClick = true, closeOnEscape = true, className, contentClassName, headerClassName, bodyClassName, footerClassName, overlayClassName, ariaLabel, ariaDescribedBy, }, ref) => {
1762
+ const modalRef = React.useRef(null);
1763
+ const contentRef = ref || modalRef;
1764
+ // Size configurations
1765
+ const sizeConfig = {
1766
+ small: "max-w-sm",
1767
+ medium: "max-w-md",
1768
+ large: "max-w-lg",
1769
+ xlarge: "max-w-2xl",
1770
+ };
1771
+ // Handle escape key
1772
+ React.useEffect(() => {
1773
+ if (!isOpen || !closeOnEscape)
1774
+ return;
1775
+ const handleEscape = (e) => {
1776
+ if (e.key === "Escape") {
1777
+ onClose();
1778
+ }
1779
+ };
1780
+ document.addEventListener("keydown", handleEscape);
1781
+ return () => document.removeEventListener("keydown", handleEscape);
1782
+ }, [isOpen, closeOnEscape, onClose]);
1783
+ // Prevent body scroll when modal is open
1784
+ React.useEffect(() => {
1785
+ if (isOpen) {
1786
+ document.body.style.overflow = "hidden";
1787
+ }
1788
+ else {
1789
+ document.body.style.overflow = "";
1790
+ }
1791
+ return () => {
1792
+ document.body.style.overflow = "";
1793
+ };
1794
+ }, [isOpen]);
1795
+ // Handle overlay click
1796
+ const handleOverlayClick = (e) => {
1797
+ if (closeOnOverlayClick && e.target === e.currentTarget) {
1798
+ onClose();
1799
+ }
1800
+ };
1801
+ // Don't render if not open
1802
+ if (!isOpen)
1803
+ return null;
1804
+ const hasHeader = title || description;
1805
+ return (jsxs("div", { className: cn("fixed inset-0 z-50 flex items-center justify-center p-4", className), role: "dialog", "aria-modal": "true", "aria-label": ariaLabel || title, "aria-describedby": ariaDescribedBy, children: [jsx("div", { className: cn("absolute inset-0 bg-black/50 backdrop-blur-sm transition-opacity", overlayClassName), onClick: handleOverlayClick, "aria-hidden": "true" }), jsxs("div", { ref: contentRef, className: cn("relative w-full bg-white rounded-large shadow-xl transition-all", "flex flex-col max-h-[90vh]", sizeConfig[size], contentClassName), children: [hasHeader && (jsxs("div", { className: cn("flex items-start justify-between gap-4 px-6 pt-6", !description && "pb-4", description && "pb-2", headerClassName), children: [jsxs("div", { className: "flex-1", children: [title && (jsx(Text, { as: "h2", variant: "body", size: "large", weight: "semibold", color: "default", children: title })), description && (jsx(Text, { as: "p", variant: "body", size: "small", weight: "regular", color: "subtle", className: "mt-1", children: description }))] }), showCloseButton && (jsx("button", { type: "button", onClick: onClose, className: cn("shrink-0 rounded-medium p-1.5 transition-colors", "text-surface-ink-neutral-muted hover:text-surface-ink-neutral-default", "hover:bg-surface-fill-neutral-faded focus:outline-none focus:ring-2", "focus:ring-action-outline-primary-default focus:ring-offset-2"), "aria-label": "Close modal", children: jsx(X, { className: "h-5 w-5" }) }))] })), !hasHeader && showCloseButton && (jsx("div", { className: "absolute top-4 right-4 z-10", children: jsx("button", { type: "button", onClick: onClose, className: cn("shrink-0 rounded-medium p-1.5 transition-colors", "text-surface-ink-neutral-muted hover:text-surface-ink-neutral-default", "hover:bg-surface-fill-neutral-faded focus:outline-none focus:ring-2", "focus:ring-action-outline-primary-default focus:ring-offset-2"), "aria-label": "Close modal", children: jsx(X, { className: "h-5 w-5" }) }) })), jsx("div", { className: cn("flex-1 overflow-y-auto px-6", hasHeader ? "py-4" : "pt-6 pb-4", !footer && "pb-6", bodyClassName), children: children }), footer && (jsxs("div", { className: "flex flex-col", children: [jsx(Divider, { thickness: "thin", variant: "muted" }), jsx("div", { className: cn("flex items-center justify-end gap-3 px-6 py-4", footerClassName), children: footer })] }))] })] }));
1806
+ });
1807
+ Modal.displayName = "Modal";
1808
+
1809
+ const selectVariants = cva("relative flex items-center gap-2 border rounded-medium transition-all font-display font-size-100 leading-100", {
1810
+ variants: {
1811
+ size: {
1812
+ small: "h-[28px] px-3 text-xs gap-2",
1813
+ medium: "h-[36px] px-4 text-sm gap-2",
1814
+ large: "h-[44px] px-5 text-base gap-3",
1815
+ },
1816
+ validationState: {
1817
+ none: `
1818
+ border-action-outline-neutral-faded
1819
+ hover:border-action-outline-primary-hover
1820
+ focus-within:border-action-outline-primary-hover
1821
+ focus-within:ring-2
1822
+ ring-action-outline-primary-faded-hover`,
1823
+ positive: `
1824
+ border-action-outline-positive-default
1825
+ focus-within:border-action-outline-positive-hover
1826
+ focus-within:ring-2
1827
+ ring-action-outline-positive-faded-hover`,
1828
+ negative: `border-action-outline-negative-default
1829
+ focus-within:border-action-outline-negative-hover
1830
+ focus-within:ring-2
1831
+ ring-action-outline-negative-faded-hover`,
1832
+ },
1833
+ isDisabled: {
1834
+ true: `
1835
+ border-[var(--border-width-thinner)]
1836
+ hover:border-action-outline-neutral-disabled
1837
+ border-action-outline-neutral-disabled
1838
+ bg-surface-fill-neutral-intense cursor-not-allowed opacity-60`,
1839
+ false: "bg-surface-fill-neutral-intense",
1840
+ },
1841
+ },
1842
+ defaultVariants: {
1843
+ size: "medium",
1844
+ validationState: "none",
1845
+ isDisabled: false,
1846
+ },
1847
+ });
1848
+ const Select = React.forwardRef(({ className, options = [], value: controlledValue, defaultValue, onChange, placeholder = "Select an option", label, helperText, errorText, successText, validationState = "none", isDisabled = false, isRequired = false, isOptional = false, isLoading = false, size = "medium", prefix, suffix, showClearButton = false, onClear, containerClassName, labelClassName, triggerClassName, menuClassName, menuWidth = "full", sectionHeading, emptyTitle = "No options available", emptyDescription = "There are no options to select from.", emptyIcon, infoHeading, infoDescription, linkText, linkHref, onLinkClick, ...props }, ref) => {
1849
+ const [uncontrolledValue, setUncontrolledValue] = React.useState(defaultValue);
1850
+ const [isOpen, setIsOpen] = React.useState(false);
1851
+ const selectRef = React.useRef(null);
1852
+ const value = controlledValue !== undefined ? controlledValue : uncontrolledValue;
1853
+ // Find the selected option
1854
+ const selectedOption = options.find((opt) => opt.value === value);
1855
+ const hasValue = value !== undefined && value !== "";
1856
+ // Determine which helper text to show
1857
+ const displayHelperText = errorText || successText || helperText;
1858
+ const currentValidationState = errorText
1859
+ ? "negative"
1860
+ : successText
1861
+ ? "positive"
1862
+ : validationState;
1863
+ const handleOpenChange = (newOpen) => {
1864
+ if (!isDisabled && !isLoading) {
1865
+ setIsOpen(newOpen);
1866
+ }
1867
+ };
1868
+ const toggleOpen = () => {
1869
+ handleOpenChange(!isOpen);
1870
+ };
1871
+ const handleSelect = (option) => {
1872
+ if (controlledValue === undefined) {
1873
+ setUncontrolledValue(option.value);
1874
+ }
1875
+ onChange?.(option.value, option);
1876
+ setIsOpen(false);
1877
+ };
1878
+ const handleClear = (e) => {
1879
+ e.stopPropagation();
1880
+ if (onClear) {
1881
+ onClear();
1882
+ }
1883
+ else {
1884
+ if (controlledValue === undefined) {
1885
+ setUncontrolledValue(undefined);
1886
+ }
1887
+ onChange?.("", {});
1888
+ }
1889
+ };
1890
+ // Close dropdown when clicking outside
1891
+ React.useEffect(() => {
1892
+ const handleClickOutside = (event) => {
1893
+ if (selectRef.current &&
1894
+ !selectRef.current.contains(event.target)) {
1895
+ handleOpenChange(false);
1896
+ }
1897
+ };
1898
+ if (isOpen) {
1899
+ document.addEventListener("mousedown", handleClickOutside);
1900
+ return () => {
1901
+ document.removeEventListener("mousedown", handleClickOutside);
1902
+ };
1903
+ }
1904
+ }, [isOpen]);
1905
+ // Close on escape key
1906
+ React.useEffect(() => {
1907
+ const handleEscape = (event) => {
1908
+ if (event.key === "Escape") {
1909
+ handleOpenChange(false);
1910
+ }
1911
+ };
1912
+ if (isOpen) {
1913
+ document.addEventListener("keydown", handleEscape);
1914
+ return () => {
1915
+ document.removeEventListener("keydown", handleEscape);
1916
+ };
1917
+ }
1918
+ }, [isOpen]);
1919
+ // Handle keyboard navigation
1920
+ React.useEffect(() => {
1921
+ const handleKeyDown = (event) => {
1922
+ if (isDisabled || isLoading)
1923
+ return;
1924
+ if (!isOpen && (event.key === "Enter" || event.key === " ")) {
1925
+ event.preventDefault();
1926
+ setIsOpen(true);
1927
+ return;
1928
+ }
1929
+ if (isOpen) {
1930
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
1931
+ event.preventDefault();
1932
+ const currentIndex = options.findIndex((opt) => opt.value === value);
1933
+ const nextIndex = event.key === "ArrowDown"
1934
+ ? Math.min(currentIndex + 1, options.length - 1)
1935
+ : Math.max(currentIndex - 1, 0);
1936
+ if (options[nextIndex] && !options[nextIndex].isDisabled) {
1937
+ handleSelect(options[nextIndex]);
1938
+ }
1939
+ }
1940
+ }
1941
+ };
1942
+ if (selectRef.current) {
1943
+ selectRef.current.addEventListener("keydown", handleKeyDown);
1944
+ return () => {
1945
+ selectRef.current?.removeEventListener("keydown", handleKeyDown);
1946
+ };
1947
+ }
1948
+ }, [isOpen, value, options, isDisabled, isLoading]);
1949
+ // Transform options to dropdown menu items
1950
+ const menuItems = options.map((option) => ({
1951
+ ...option,
1952
+ onClick: () => handleSelect(option),
1953
+ }));
1954
+ const widthStyle = menuWidth === "full" ? "100%" : menuWidth === "auto" ? "auto" : menuWidth;
1955
+ const sizeConfig = {
1956
+ small: {
1957
+ gap: "gap-2",
1958
+ },
1959
+ medium: {
1960
+ gap: "gap-2",
1961
+ },
1962
+ large: {
1963
+ gap: "gap-3",
1964
+ },
1965
+ };
1966
+ return (jsxs("div", { className: cn("w-full flex flex-col", sizeConfig[size].gap, containerClassName), children: [label && (jsx(FormHeader, { label: label, size: size, isRequired: isRequired, isOptional: isOptional, infoHeading: infoHeading, infoDescription: infoDescription, linkText: linkText, linkHref: linkHref, onLinkClick: onLinkClick, htmlFor: props.id, className: "mb-2", labelClassName: labelClassName })), jsxs("div", { ref: selectRef, className: cn(selectVariants({
1967
+ size,
1968
+ validationState: currentValidationState,
1969
+ isDisabled,
1970
+ }), "relative w-full cursor-pointer", className), onClick: !isDisabled && !isLoading ? toggleOpen : undefined, role: "combobox", "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-disabled": isDisabled, ...props, children: [prefix && (jsx("span", { className: cn("shrink-0 flex items-center", isDisabled
1971
+ ? "text-surface-ink-neutral-disabled"
1972
+ : currentValidationState === "positive"
1973
+ ? "text-feedback-ink-positive-intense"
1974
+ : currentValidationState === "negative"
1975
+ ? "text-feedback-ink-negative-subtle"
1976
+ : "text-surface-ink-neutral-muted"), children: prefix })), jsx("span", { className: cn("flex-1 text-left truncate", !selectedOption && "text-surface-ink-neutral-muted", isDisabled && "text-surface-ink-neutral-disabled"), children: isLoading
1977
+ ? "Loading..."
1978
+ : selectedOption?.label || selectedOption?.title || placeholder }), showClearButton && hasValue && !isDisabled && !isLoading && (jsx("button", { type: "button", onClick: handleClear, className: "shrink-0 flex items-center justify-center text-surface-ink-neutral-muted hover:text-surface-ink-neutral-normal transition-colors", tabIndex: -1, children: jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: jsx("path", { d: "M12 4L4 12M4 4L12 12", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }) })), suffix && !showClearButton && (jsx("span", { className: cn("shrink-0 flex items-center", isDisabled
1979
+ ? "text-surface-ink-neutral-disabled"
1980
+ : currentValidationState === "positive"
1981
+ ? "text-feedback-ink-positive-intense"
1982
+ : currentValidationState === "negative"
1983
+ ? "text-feedback-ink-negative-subtle"
1984
+ : "text-surface-ink-neutral-muted"), children: suffix })), jsx(ChevronDown, { className: cn("shrink-0 w-4 h-4 transition-transform", isDisabled
1985
+ ? "text-surface-ink-neutral-disabled"
1986
+ : currentValidationState === "positive"
1987
+ ? "text-feedback-ink-positive-intense"
1988
+ : currentValidationState === "negative"
1989
+ ? "text-feedback-ink-negative-subtle"
1990
+ : "text-surface-ink-neutral-muted", isOpen && "transform rotate-180") }), isOpen && !isDisabled && !isLoading && (jsx("div", { className: "absolute z-50 left-0 right-0 top-full mt-1", children: jsx(DropdownMenu, { ref: ref, items: menuItems, sectionHeading: sectionHeading, isEmpty: options.length === 0, emptyTitle: emptyTitle, emptyDescription: emptyDescription, emptyIcon: emptyIcon, disableFooter: true, onClose: () => handleOpenChange(false), className: menuClassName, width: widthStyle }) }))] }), jsx(FormFooter, { helperText: displayHelperText, validationState: currentValidationState === "none"
1991
+ ? "default"
1992
+ : currentValidationState, size: size, isDisabled: isDisabled, className: "mt-1" })] }));
1993
+ });
1994
+ Select.displayName = "Select";
1995
+
1996
+ const paginationVariants = cva("flex items-center gap-4 font-display text-body-medium-medium", {
1997
+ variants: {
1998
+ size: {
1999
+ small: "text-xs gap-2",
2000
+ medium: "text-sm gap-4",
2001
+ large: "text-base gap-5",
2002
+ },
2003
+ },
2004
+ defaultVariants: {
2005
+ size: "medium",
2006
+ },
2007
+ });
2008
+ const Pagination = React.forwardRef(({ className, currentPage: controlledCurrentPage, totalPages, rowsPerPage: controlledRowsPerPage, rowsPerPageOptions = [5, 10, 15, 20, 25, 50, 100], showRowsPerPage = true, onPageChange, onRowsPerPageChange, size = "medium", isDisabled = false, rowsPerPageLabel = "Row per page", ofLabel = "of", prevLabel = "Prev", nextLabel = "Next", showPrevNext = true, showPageJumper = true, showPageNumber = true, pageStatus, maxPageButtons = 5, ...props }, ref) => {
2009
+ const [uncontrolledCurrentPage, setUncontrolledCurrentPage] = React.useState(1);
2010
+ const [uncontrolledRowsPerPage, setUncontrolledRowsPerPage] = React.useState(rowsPerPageOptions[0] || 10);
2011
+ const currentPage = controlledCurrentPage !== undefined
2012
+ ? controlledCurrentPage
2013
+ : uncontrolledCurrentPage;
2014
+ const rowsPerPage = controlledRowsPerPage !== undefined
2015
+ ? controlledRowsPerPage
2016
+ : uncontrolledRowsPerPage;
2017
+ // Generate rows per page options
2018
+ const rowsPerPageSelectOptions = rowsPerPageOptions.map((value) => {
2019
+ const valueStr = String(value);
2020
+ // Pad with leading zero if needed (manual implementation for compatibility)
2021
+ const label = valueStr.length < 2 ? "0" + valueStr : valueStr;
2022
+ return {
2023
+ id: `rows-${valueStr}`,
2024
+ value: valueStr,
2025
+ label: label,
2026
+ title: valueStr,
2027
+ };
2028
+ });
2029
+ // Generate page options
2030
+ const pageOptions = Array.from({ length: totalPages }, (_, i) => {
2031
+ const pageNum = i + 1;
2032
+ const pageStr = String(pageNum);
2033
+ return {
2034
+ id: `page-${pageStr}`,
2035
+ value: pageStr,
2036
+ label: pageStr,
2037
+ title: `Page ${pageNum}`,
2038
+ };
2039
+ });
2040
+ const handlePageChange = (newPage) => {
2041
+ if (newPage < 1 || newPage > totalPages)
2042
+ return;
2043
+ if (controlledCurrentPage === undefined) {
2044
+ setUncontrolledCurrentPage(newPage);
2045
+ }
2046
+ onPageChange?.(newPage);
2047
+ };
2048
+ const handleRowsPerPageChange = (value) => {
2049
+ const newRowsPerPage = typeof value === "string" ? parseInt(value) : value;
2050
+ if (controlledRowsPerPage === undefined) {
2051
+ setUncontrolledRowsPerPage(newRowsPerPage);
2052
+ }
2053
+ onRowsPerPageChange?.(newRowsPerPage);
2054
+ // Reset to first page when rows per page changes
2055
+ handlePageChange(1);
2056
+ };
2057
+ const handlePrevPage = () => {
2058
+ handlePageChange(currentPage - 1);
2059
+ };
2060
+ const handleNextPage = () => {
2061
+ handlePageChange(currentPage + 1);
2062
+ };
2063
+ const isPrevDisabled = currentPage <= 1 || isDisabled;
2064
+ const isNextDisabled = currentPage >= totalPages || isDisabled;
2065
+ const selectSize = size === "small" ? "small" : size === "large" ? "large" : "medium";
2066
+ // Map pagination size to button size
2067
+ const buttonSize = size === "small" ? "xsmall" : size === "large" ? "large" : "small";
2068
+ // Generate page numbers to display
2069
+ const getPageNumbers = () => {
2070
+ if (totalPages <= maxPageButtons) {
2071
+ // Show all pages if total is less than max
2072
+ return Array.from({ length: totalPages }, (_, i) => i + 1);
2073
+ }
2074
+ // Determine page status if not provided
2075
+ const effectivePageStatus = pageStatus ||
2076
+ (currentPage <= 2
2077
+ ? "first"
2078
+ : currentPage >= totalPages - 1
2079
+ ? "last"
2080
+ : "middle");
2081
+ if (effectivePageStatus === "first") {
2082
+ // Show first pages: 1, 2, 3, 4, 5
2083
+ return Array.from({ length: Math.min(maxPageButtons, totalPages) }, (_, i) => i + 1);
2084
+ }
2085
+ else if (effectivePageStatus === "last") {
2086
+ // Show last pages
2087
+ const start = totalPages - maxPageButtons + 1;
2088
+ return Array.from({ length: maxPageButtons }, (_, i) => start + i);
2089
+ }
2090
+ else if (effectivePageStatus === "middle") {
2091
+ // Show pages around current page
2092
+ const halfMax = Math.floor(maxPageButtons / 2);
2093
+ let start = currentPage - halfMax;
2094
+ let end = currentPage + halfMax;
2095
+ // Adjust if at boundaries
2096
+ if (start < 1) {
2097
+ end += 1 - start;
2098
+ start = 1;
2099
+ }
2100
+ if (end > totalPages) {
2101
+ start -= end - totalPages;
2102
+ end = totalPages;
2103
+ }
2104
+ start = Math.max(1, start);
2105
+ end = Math.min(totalPages, end);
2106
+ return Array.from({ length: end - start + 1 }, (_, i) => start + i);
2107
+ }
2108
+ // pageStatus === "none" - don't show any page numbers
2109
+ return [];
2110
+ };
2111
+ const pageNumbers = getPageNumbers();
2112
+ return (jsxs("div", { ref: ref, className: cn(paginationVariants({ size }), className), ...props, children: [showRowsPerPage && (jsxs("div", { className: "flex items-center gap-3", children: [jsx("span", { className: "text-surface-ink-neutral-muted whitespace-nowrap", children: rowsPerPageLabel }), jsx("div", { className: "w-[80px]", children: jsx(Select, { value: rowsPerPage.toString(), options: rowsPerPageSelectOptions, onChange: handleRowsPerPageChange, size: selectSize, isDisabled: isDisabled, menuWidth: "auto" }) })] })), jsxs("div", { className: "flex items-center gap-3 ml-auto", children: [showPrevNext && (jsx(Button, { variant: "tertiary", color: "neutral", size: buttonSize, onClick: handlePrevPage, isDisabled: isPrevDisabled, leadingIcon: jsx(ChevronLeft, { className: "w-4 h-4" }), "aria-label": "Previous page", children: prevLabel })), showPageJumper ? (
2113
+ // Show page dropdown selector
2114
+ jsxs("div", { className: "flex items-center gap-3", children: [jsx("div", { className: "w-[80px]", children: jsx(Select, { value: currentPage.toString(), options: pageOptions, onChange: (value) => handlePageChange(typeof value === "string" ? parseInt(value) : value), size: selectSize, isDisabled: isDisabled, menuWidth: "auto" }) }), jsxs("span", { className: "text-surface-ink-neutral-muted whitespace-nowrap", children: [ofLabel, " ", totalPages] })] })) : (
2115
+ // Show numbered page buttons
2116
+ showPageNumber &&
2117
+ pageNumbers.length > 0 && (jsx(ButtonGroup, { variant: "separated", size: buttonSize, isDisabled: isDisabled, value: currentPage, onChange: (value) => handlePageChange(value), children: pageNumbers.map((pageNum) => (jsx(Button, { value: pageNum, variant: "tertiary", color: "primary", "aria-label": `Page ${pageNum}`, "aria-current": pageNum === currentPage ? "page" : undefined, children: pageNum }, pageNum))) }))), showPrevNext && (jsx(Button, { variant: "tertiary", color: "neutral", size: buttonSize, onClick: handleNextPage, isDisabled: isNextDisabled, trailingIcon: jsx(ChevronRight, { className: "w-4 h-4" }), "aria-label": "Next page", children: nextLabel }))] })] }));
2118
+ });
2119
+ Pagination.displayName = "Pagination";
2120
+
1544
2121
  const radioVariants = cva("relative inline-flex items-center justify-center shrink-0 border transition-all cursor-pointer rounded-full", {
1545
2122
  variants: {
1546
2123
  size: {
@@ -1815,13 +2392,30 @@ const defaultFilter = (item, query) => {
1815
2392
  return (item.title.toLowerCase().includes(searchQuery) ||
1816
2393
  (item.description?.toLowerCase().includes(searchQuery) ?? false));
1817
2394
  };
1818
- const SearchableDropdown = React.forwardRef(({ className, items = [], sectionHeading, isLoading = false, emptyTitle = "No Search Results Found", emptyDescription = "Add description of what the user can search for here.", emptyLinkText = "Link to support site", onEmptyLinkClick, primaryButtonText = "Primary", secondaryButtonText = "Secondary", onPrimaryClick, onSecondaryClick, dropdownWidth = "full", showChevron = false, emptyIcon, disableFooter = false, onSearchChange, onItemSelect, filterFunction = defaultFilter, searchValue: controlledSearchValue, defaultSearchValue = "", dropdownClassName, minSearchLength = 0, showOnFocus = true, containerClassName, ...textFieldProps }, ref) => {
2395
+ const SearchableDropdown = React.forwardRef(({ className, items = [], sectionHeading, isLoading = false, emptyTitle = "No Search Results Found", emptyDescription = "Add description of what the user can search for here.", emptyLinkText = "Link to support site", onEmptyLinkClick, primaryButtonText = "Primary", secondaryButtonText = "Secondary", onPrimaryClick, onSecondaryClick, dropdownWidth = "full", showChevron = false, emptyIcon, disableFooter = false, footerLayout = "horizontal", onSearchChange, onItemSelect, filterFunction = defaultFilter, searchValue: controlledSearchValue, defaultSearchValue = "", dropdownClassName, minSearchLength = 0, showOnFocus = true, containerClassName, ...textFieldProps }, ref) => {
1819
2396
  const [uncontrolledSearchValue, setUncontrolledSearchValue] = React.useState(defaultSearchValue);
1820
2397
  const [isOpen, setIsOpen] = React.useState(false);
1821
2398
  const [focusedIndex, setFocusedIndex] = React.useState(-1);
1822
2399
  const dropdownRef = React.useRef(null);
1823
2400
  const inputRef = React.useRef(null);
2401
+ const menuRef = React.useRef(null);
2402
+ const [position, setPosition] = React.useState({
2403
+ top: 0,
2404
+ left: 0,
2405
+ width: 0,
2406
+ });
1824
2407
  React.useImperativeHandle(ref, () => inputRef.current);
2408
+ // Update position when dropdown opens or window resizes
2409
+ React.useEffect(() => {
2410
+ if (isOpen && dropdownRef.current) {
2411
+ const rect = dropdownRef.current.getBoundingClientRect();
2412
+ setPosition({
2413
+ top: rect.bottom + window.scrollY,
2414
+ left: rect.left + window.scrollX,
2415
+ width: rect.width,
2416
+ });
2417
+ }
2418
+ }, [isOpen]);
1825
2419
  const searchValue = controlledSearchValue !== undefined
1826
2420
  ? controlledSearchValue
1827
2421
  : uncontrolledSearchValue;
@@ -1847,7 +2441,7 @@ const SearchableDropdown = React.forwardRef(({ className, items = [], sectionHea
1847
2441
  const handleItemSelect = (item) => {
1848
2442
  onItemSelect?.(item);
1849
2443
  if (controlledSearchValue === undefined) {
1850
- setUncontrolledSearchValue(item.value || item.title);
2444
+ setUncontrolledSearchValue(item.title);
1851
2445
  }
1852
2446
  setIsOpen(false);
1853
2447
  inputRef.current?.focus();
@@ -1862,7 +2456,9 @@ const SearchableDropdown = React.forwardRef(({ className, items = [], sectionHea
1862
2456
  React.useEffect(() => {
1863
2457
  const handleClickOutside = (event) => {
1864
2458
  if (dropdownRef.current &&
1865
- !dropdownRef.current.contains(event.target)) {
2459
+ !dropdownRef.current.contains(event.target) &&
2460
+ menuRef.current &&
2461
+ !menuRef.current.contains(event.target)) {
1866
2462
  setIsOpen(false);
1867
2463
  }
1868
2464
  };
@@ -1910,7 +2506,16 @@ const SearchableDropdown = React.forwardRef(({ className, items = [], sectionHea
1910
2506
  onClick: () => handleItemSelect(item),
1911
2507
  }));
1912
2508
  const showDropdown = isOpen && searchValue.length >= minSearchLength;
1913
- return (jsxs("div", { ref: dropdownRef, className: cn("relative", containerClassName), children: [jsx(TextField, { ref: inputRef, value: searchValue, onChange: handleSearchChange, onFocus: handleFocus, onKeyDown: handleKeyDown, containerClassName: "mb-0", ...textFieldProps }), showDropdown && (jsx(DropdownMenu, { items: itemsWithHandlers, sectionHeading: sectionHeading, isLoading: isLoading, isEmpty: filteredItems.length === 0, emptyTitle: emptyTitle, emptyDescription: emptyDescription, emptyLinkText: emptyLinkText, onEmptyLinkClick: onEmptyLinkClick, primaryButtonText: primaryButtonText, secondaryButtonText: secondaryButtonText, onPrimaryClick: onPrimaryClick, onSecondaryClick: onSecondaryClick, showChevron: showChevron, emptyIcon: emptyIcon, disableFooter: disableFooter, onClose: () => setIsOpen(false), focusedIndex: focusedIndex, className: cn("absolute z-50 mt-2", dropdownClassName), width: dropdownWidth === "full" ? "full" : "auto" }))] }));
2509
+ const dropdownMenu = showDropdown && (jsx("div", { ref: menuRef, style: {
2510
+ position: "absolute",
2511
+ top: `${position.top + 8}px`,
2512
+ left: `${position.left}px`,
2513
+ width: dropdownWidth === "full" ? `${position.width}px` : "auto",
2514
+ zIndex: 9999,
2515
+ }, children: jsx(DropdownMenu, { items: itemsWithHandlers, sectionHeading: sectionHeading, isLoading: isLoading, isEmpty: filteredItems.length === 0, emptyTitle: emptyTitle, emptyDescription: emptyDescription, emptyLinkText: emptyLinkText, onEmptyLinkClick: onEmptyLinkClick, primaryButtonText: primaryButtonText, secondaryButtonText: secondaryButtonText, onPrimaryClick: onPrimaryClick, onSecondaryClick: onSecondaryClick, showChevron: showChevron, emptyIcon: emptyIcon, disableFooter: disableFooter, footerLayout: footerLayout, onClose: () => setIsOpen(false), focusedIndex: focusedIndex, className: dropdownClassName, width: dropdownWidth === "full" ? "full" : "auto" }) }));
2516
+ return (jsxs(Fragment, { children: [jsx("div", { ref: dropdownRef, className: cn("relative", containerClassName), children: jsx(TextField, { ref: inputRef, value: searchValue, onChange: handleSearchChange, onFocus: handleFocus, onKeyDown: handleKeyDown, containerClassName: "mb-0", ...textFieldProps }) }), typeof document !== "undefined" &&
2517
+ dropdownMenu &&
2518
+ createPortal(dropdownMenu, document.body)] }));
1914
2519
  });
1915
2520
  SearchableDropdown.displayName = "SearchableDropdown";
1916
2521
 
@@ -2054,6 +2659,99 @@ const Switch = React.forwardRef(({ label, size = "medium", isDisabled = false, c
2054
2659
  });
2055
2660
  Switch.displayName = "Switch";
2056
2661
 
2662
+ const tableVariants = cva("w-full border-collapse", {
2663
+ variants: {
2664
+ variant: {
2665
+ default: "border-separate border-spacing-0",
2666
+ bordered: "border-separate border-spacing-0",
2667
+ striped: "border-separate border-spacing-0",
2668
+ },
2669
+ size: {
2670
+ small: "text-body-small-medium",
2671
+ medium: "text-body-medium-medium",
2672
+ large: "text-body-large-medium",
2673
+ },
2674
+ },
2675
+ defaultVariants: {
2676
+ variant: "default",
2677
+ size: "medium",
2678
+ },
2679
+ });
2680
+ const tableHeaderVariants = cva("text-body-medium-regular text-left text-surface-ink-neutral-normal border-b border-surface-outline-neutral-muted", {
2681
+ variants: {
2682
+ size: {
2683
+ small: "px-3 py-2 h-[32px]",
2684
+ medium: "px-4 py-3 h-[40px]",
2685
+ large: "px-6 py-4 h-[48px]",
2686
+ },
2687
+ },
2688
+ defaultVariants: {
2689
+ size: "medium",
2690
+ },
2691
+ });
2692
+ const tableCellVariants = cva("text-body-medium-regular border-b border-surface-outline-neutral-muted text-surface-ink-neutral-normal", {
2693
+ variants: {
2694
+ size: {
2695
+ small: "px-3 py-2 h-[40px]",
2696
+ medium: "px-4 py-3 h-[48px]",
2697
+ large: "px-6 py-4 h-[56px]",
2698
+ },
2699
+ },
2700
+ defaultVariants: {
2701
+ size: "medium",
2702
+ },
2703
+ });
2704
+ function TableComponent({ className, wrapperClassName, containerClassName, variant, size, table, enableRowSelection = false, enableSelectAll = false, isLoading = false, loadingComponent, emptyComponent, enableHorizontalScroll = false, stickyHeader = false, maxHeight, showRowHover = true, onRowClick, rowClassName, headerClassName, cellClassName, showHeaderBackground = true, ...props }, ref) {
2705
+ const hasData = table.getRowModel().rows?.length > 0;
2706
+ const getRowClassName = (row) => {
2707
+ if (typeof rowClassName === "function") {
2708
+ return rowClassName(row);
2709
+ }
2710
+ return rowClassName || "";
2711
+ };
2712
+ const renderLoadingState = () => {
2713
+ if (loadingComponent) {
2714
+ return loadingComponent;
2715
+ }
2716
+ return (jsx("tr", { children: jsx("td", { colSpan: table.getAllColumns().length, className: "text-center py-12 text-surface-ink-neutral-muted", children: jsxs("div", { className: "flex items-center justify-center gap-2", children: [jsx("div", { className: "animate-spin rounded-full h-6 w-6 border-b-2 border-action-fill-primary-default" }), jsx("span", { children: "Loading..." })] }) }) }));
2717
+ };
2718
+ const renderEmptyState = () => {
2719
+ if (emptyComponent) {
2720
+ return emptyComponent;
2721
+ }
2722
+ return (jsx("tr", { children: jsx("td", { colSpan: table.getAllColumns().length, className: "text-center py-12 text-surface-ink-neutral-muted", children: "No data available" }) }));
2723
+ };
2724
+ return (jsx("div", { ref: ref, className: cn("w-full", wrapperClassName), ...props, children: jsx("div", { className: cn("relative", enableHorizontalScroll && "overflow-x-auto", maxHeight && "overflow-y-auto", containerClassName), style: maxHeight ? { maxHeight } : undefined, children: jsxs("table", { className: cn(tableVariants({ variant, size }), className), children: [jsx("thead", { className: cn(showHeaderBackground && "bg-surface-fill-neutral-moderate", stickyHeader && "sticky top-0 z-10"), children: table.getHeaderGroups().map((headerGroup) => (jsxs("tr", { children: [enableRowSelection && enableSelectAll && (jsx("th", { className: cn(tableHeaderVariants({ size }), showHeaderBackground &&
2725
+ "bg-surface-fill-neutral-moderate", "w-10 rounded-tl-xlarge rounded-bl-xlarge", headerClassName), children: jsx(Checkbox, { checked: table.getIsAllRowsSelected(), isIndeterminate: table.getIsSomeRowsSelected(), onChange: table.getToggleAllRowsSelectedHandler(), "aria-label": "Select all rows" }) })), headerGroup.headers.map((header, index) => (jsx("th", { className: cn(tableHeaderVariants({ size }), showHeaderBackground &&
2726
+ "bg-surface-fill-neutral-moderate border-none",
2727
+ // Apply rounded corners to first and last headers
2728
+ !enableRowSelection &&
2729
+ index === 0 &&
2730
+ "rounded-tl-xlarge rounded-bl-xlarge", index === headerGroup.headers.length - 1 &&
2731
+ "rounded-tr-xlarge rounded-br-xlarge", header.column.columnDef.meta?.headerClassName, headerClassName), style: {
2732
+ width: header.getSize(),
2733
+ minWidth: header.column.columnDef.minSize,
2734
+ maxWidth: header.column.columnDef.maxSize,
2735
+ }, children: header.isPlaceholder ? null : (jsxs("div", { className: cn("flex items-center gap-2", header.column.getCanSort() &&
2736
+ "cursor-pointer select-none"), onClick: header.column.getToggleSortingHandler(), children: [flexRender(header.column.columnDef.header, header.getContext()), header.column.getCanSort() && (jsx("span", { className: "text-surface-ink-neutral-muted", children: {
2737
+ asc: "↑",
2738
+ desc: "↓",
2739
+ }[header.column.getIsSorted()] ?? "↕" }))] })) }, header.id)))] }, headerGroup.id))) }), jsx("tbody", { className: "bg-surface-fill-neutral-intense", children: isLoading
2740
+ ? renderLoadingState()
2741
+ : !hasData
2742
+ ? renderEmptyState()
2743
+ : table.getRowModel().rows.map((row) => (jsxs("tr", { className: cn(variant === "striped" &&
2744
+ row.index % 2 === 1 &&
2745
+ "bg-surface-fill-neutral-moderate", showRowHover && "hover:bg-surface-fill-neutral-moderate", onRowClick && "cursor-pointer", getRowClassName(row.original)), onClick: () => onRowClick?.(row.original), children: [enableRowSelection && (jsx("td", { className: cn(tableCellVariants({ size }), "w-10", cellClassName), children: jsx(Checkbox, { checked: row.getIsSelected(), isIndeterminate: row.getIsSomeSelected(), onChange: row.getToggleSelectedHandler(), onClick: (e) => e.stopPropagation(), "aria-label": `Select row ${row.id}` }) })), row.getVisibleCells().map((cell) => (jsx("td", { className: cn(tableCellVariants({ size }), cell.column.columnDef.meta?.cellClassName, cellClassName), style: {
2746
+ width: cell.column.getSize(),
2747
+ minWidth: cell.column.columnDef.minSize,
2748
+ maxWidth: cell.column.columnDef.maxSize,
2749
+ }, children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id)))] }, row.id))) })] }) }) }));
2750
+ }
2751
+ // Use React.forwardRef with generic type
2752
+ const Table = React.forwardRef(TableComponent);
2753
+ Table.displayName = "Table";
2754
+
2057
2755
  const tabItemVariants = cva("inline-flex items-center justify-center gap-2 whitespace-nowrap transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 relative cursor-pointer", {
2058
2756
  variants: {
2059
2757
  variant: {
@@ -2256,5 +2954,5 @@ const TextArea = React.forwardRef(({ label, helperText, errorText, successText,
2256
2954
  });
2257
2955
  TextArea.displayName = "TextArea";
2258
2956
 
2259
- export { Badge, Button, Checkbox, Counter, Divider, Dropdown, DropdownMenu, FormFooter, FormHeader, Icon, Link, ListItem, Radio, SearchableDropdown, Switch, TabItem, Tabs, Text, TextArea, TextField, Tooltip, badgeVariants, buttonVariants, checkboxVariants, cn, counterVariants, dropdownVariants, getAvailableIcons, hasIcon, iconRegistry, linkVariants, listItemVariants, radioVariants, switchVariants, textAreaVariants, textFieldVariants, tooltipVariants };
2957
+ export { Avatar, Badge, Button, ButtonGroup, Checkbox, Counter, Divider, Dropdown, DropdownMenu, FormFooter, FormHeader, Icon, Link, ListItem, Modal, Pagination, Radio, SearchableDropdown, Select, Switch, TabItem, Table, Tabs, Text, TextArea, TextField, Tooltip, avatarVariants, badgeVariants, buttonGroupVariants, buttonVariants, checkboxVariants, cn, counterVariants, dropdownVariants, getAvailableIcons, hasIcon, iconRegistry, linkVariants, listItemVariants, paginationVariants, radioVariants, selectVariants, switchVariants, tableCellVariants, tableHeaderVariants, tableVariants, textAreaVariants, textFieldVariants, tooltipVariants };
2260
2958
  //# sourceMappingURL=index.esm.js.map