@xsolla/xui-button 0.79.0 → 0.81.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/native/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/Button.tsx
2
- import { useState } from "react";
2
+ import React4, { useState } from "react";
3
3
 
4
4
  // ../primitives-native/src/Box.tsx
5
5
  import {
@@ -501,6 +501,17 @@ TextAreaPrimitive.displayName = "TextAreaPrimitive";
501
501
  // src/Button.tsx
502
502
  import { useDesignSystem } from "@xsolla/xui-core";
503
503
  import { jsx as jsx8, jsxs } from "react/jsx-runtime";
504
+ var cloneIconWithDefaults = (icon, defaultSize, defaultColor) => {
505
+ if (!React4.isValidElement(icon)) return icon;
506
+ const iconElement = icon;
507
+ const existingProps = iconElement.props || {};
508
+ return React4.cloneElement(iconElement, {
509
+ ...existingProps,
510
+ // Preserve existing props (including accessibility attributes)
511
+ size: existingProps.size ?? defaultSize,
512
+ color: existingProps.color ?? defaultColor
513
+ });
514
+ };
504
515
  var Button = ({
505
516
  variant = "primary",
506
517
  tone = "brand",
@@ -511,6 +522,11 @@ var Button = ({
511
522
  onPress,
512
523
  iconLeft,
513
524
  iconRight,
525
+ divider,
526
+ sublabel,
527
+ labelAlignment = "center",
528
+ labelIcon,
529
+ customContent,
514
530
  "aria-label": ariaLabel,
515
531
  "aria-describedby": ariaDescribedBy,
516
532
  "aria-expanded": ariaExpanded,
@@ -526,9 +542,17 @@ var Button = ({
526
542
  const [isKeyboardPressed, setIsKeyboardPressed] = useState(false);
527
543
  const isDisabled = disabled || loading;
528
544
  const sizeStyles = theme.sizing.button(size);
529
- const variantStyles = theme?.colors?.control?.[tone]?.[variant] || theme?.colors?.control?.brand?.primary || {
545
+ const controlTone = theme?.colors?.control?.[tone];
546
+ const variantStyles = controlTone?.[variant] || theme?.colors?.control?.brand?.primary || {
530
547
  bg: "transparent",
531
- text: { primary: "#000" }
548
+ bgHover: "transparent",
549
+ bgPress: "transparent",
550
+ bgDisable: "transparent",
551
+ border: "transparent",
552
+ borderHover: "transparent",
553
+ borderPress: "transparent",
554
+ borderDisable: "transparent",
555
+ text: { primary: "#000", secondary: "#000", disable: "#666" }
532
556
  };
533
557
  const handlePress = () => {
534
558
  if (!isDisabled && onPress) {
@@ -552,17 +576,19 @@ var Button = ({
552
576
  }
553
577
  }
554
578
  };
555
- const styles = variantStyles;
556
- let backgroundColor = styles.bg;
579
+ let backgroundColor = variantStyles.bg;
557
580
  if (disabled) {
558
- backgroundColor = styles.bgDisable || styles.bg;
581
+ backgroundColor = variantStyles.bgDisable || variantStyles.bg;
559
582
  } else if (isKeyboardPressed) {
560
- backgroundColor = styles.bgPress || styles.bg;
583
+ backgroundColor = variantStyles.bgPress || variantStyles.bg;
561
584
  }
562
- const borderColor = disabled ? styles.borderDisable || styles.border : styles.border;
563
- const textColor = disabled ? styles.text?.disable || styles.text?.primary : styles.text?.primary;
564
- const isDarkText = textColor === "#000000" || textColor === "black" || textColor.startsWith("rgba(0, 0, 0");
565
- const dividerColor = isDarkText ? "rgba(0, 0, 0, 0.2)" : "rgba(255, 255, 255, 0.2)";
585
+ const borderColor = disabled ? variantStyles.borderDisable || variantStyles.border : variantStyles.border;
586
+ const textColor = disabled ? variantStyles.text?.disable || variantStyles.text?.primary : variantStyles.text?.primary;
587
+ const textColorStr = typeof textColor === "string" ? textColor : "";
588
+ const isDarkText = textColorStr === "#000000" || textColorStr === "black" || textColorStr.startsWith("rgba(0, 0, 0");
589
+ const dividerLineColor = isDarkText ? "rgba(0, 0, 0, 0.2)" : "rgba(255, 255, 255, 0.2)";
590
+ const hasIcon = Boolean(iconLeft || iconRight);
591
+ const showDivider = divider !== void 0 ? divider : hasIcon;
566
592
  const computedAriaLabel = ariaLabel;
567
593
  return /* @__PURE__ */ jsxs(
568
594
  Box,
@@ -586,7 +612,7 @@ var Button = ({
586
612
  backgroundColor,
587
613
  borderColor,
588
614
  borderWidth: borderColor !== "transparent" && borderColor !== "rgba(255, 255, 255, 0)" ? 1 : 0,
589
- borderRadius: theme.radius.button,
615
+ borderRadius: sizeStyles.borderRadius,
590
616
  height: sizeStyles.height,
591
617
  width: fullWidth ? "100%" : void 0,
592
618
  padding: 0,
@@ -609,72 +635,110 @@ var Button = ({
609
635
  outlineStyle: "solid"
610
636
  },
611
637
  children: [
612
- !loading && iconLeft && /* @__PURE__ */ jsxs(
638
+ loading && /* @__PURE__ */ jsx8(
639
+ Box,
640
+ {
641
+ position: "absolute",
642
+ top: 0,
643
+ left: 0,
644
+ right: 0,
645
+ bottom: 0,
646
+ alignItems: "center",
647
+ justifyContent: "center",
648
+ zIndex: 1,
649
+ children: /* @__PURE__ */ jsx8(
650
+ Spinner,
651
+ {
652
+ color: textColor,
653
+ size: sizeStyles.spinnerSize,
654
+ "aria-hidden": true
655
+ }
656
+ )
657
+ }
658
+ ),
659
+ iconLeft && /* @__PURE__ */ jsxs(
613
660
  Box,
614
661
  {
615
662
  height: "100%",
616
663
  flexDirection: "row",
617
664
  alignItems: "center",
618
- justifyContent: "center",
619
665
  "aria-hidden": true,
666
+ style: {
667
+ opacity: loading ? 0 : 1,
668
+ pointerEvents: loading ? "none" : "auto"
669
+ },
620
670
  children: [
621
671
  /* @__PURE__ */ jsx8(
622
672
  Box,
623
673
  {
674
+ width: sizeStyles.iconContainerSize,
675
+ height: sizeStyles.iconContainerSize,
624
676
  alignItems: "center",
625
677
  justifyContent: "center",
626
- paddingHorizontal: sizeStyles.iconPadding,
627
- children: /* @__PURE__ */ jsx8(Icon, { size: sizeStyles.iconSize, color: textColor, children: iconLeft })
678
+ children: cloneIconWithDefaults(iconLeft, sizeStyles.iconSize, textColor)
628
679
  }
629
680
  ),
630
- /* @__PURE__ */ jsx8(Divider, { vertical: true, color: dividerColor, height: "100%" })
681
+ showDivider && /* @__PURE__ */ jsx8(Divider, { vertical: true, color: dividerLineColor, height: "100%" })
631
682
  ]
632
683
  }
633
684
  ),
634
- /* @__PURE__ */ jsx8(
685
+ /* @__PURE__ */ jsxs(
635
686
  Box,
636
687
  {
637
688
  flex: fullWidth ? 1 : void 0,
638
689
  flexDirection: "row",
639
690
  alignItems: "center",
640
- justifyContent: "center",
641
- paddingHorizontal: loading ? sizeStyles.loadingPadding : sizeStyles.padding,
691
+ justifyContent: labelAlignment === "left" ? "flex-start" : "center",
692
+ paddingHorizontal: sizeStyles.padding,
642
693
  height: "100%",
643
- children: loading ? /* @__PURE__ */ jsx8(
644
- Spinner,
645
- {
646
- color: textColor,
647
- size: sizeStyles.spinnerSize,
648
- "aria-hidden": true
649
- }
650
- ) : /* @__PURE__ */ jsx8(
651
- Text,
652
- {
653
- color: textColor,
654
- fontSize: sizeStyles.fontSize,
655
- fontWeight: "500",
656
- children
657
- }
658
- )
694
+ gap: sizeStyles.labelIconGap,
695
+ style: {
696
+ opacity: loading ? 0 : 1,
697
+ pointerEvents: loading ? "none" : "auto"
698
+ },
699
+ "aria-hidden": loading ? true : void 0,
700
+ children: [
701
+ labelIcon && /* @__PURE__ */ jsx8(Box, { "aria-hidden": true, children: cloneIconWithDefaults(
702
+ labelIcon,
703
+ sizeStyles.labelIconSize,
704
+ textColor
705
+ ) }),
706
+ /* @__PURE__ */ jsx8(Text, { color: textColor, fontSize: sizeStyles.fontSize, fontWeight: "500", children }),
707
+ sublabel && /* @__PURE__ */ jsx8(
708
+ Text,
709
+ {
710
+ color: textColor,
711
+ fontSize: sizeStyles.fontSize,
712
+ fontWeight: "500",
713
+ style: { opacity: 0.4 },
714
+ children: sublabel
715
+ }
716
+ ),
717
+ customContent && /* @__PURE__ */ jsx8(Box, { "aria-hidden": true, children: customContent })
718
+ ]
659
719
  }
660
720
  ),
661
- !loading && iconRight && /* @__PURE__ */ jsxs(
721
+ iconRight && /* @__PURE__ */ jsxs(
662
722
  Box,
663
723
  {
664
724
  height: "100%",
665
725
  flexDirection: "row",
666
726
  alignItems: "center",
667
- justifyContent: "center",
668
727
  "aria-hidden": true,
728
+ style: {
729
+ opacity: loading ? 0 : 1,
730
+ pointerEvents: loading ? "none" : "auto"
731
+ },
669
732
  children: [
670
- /* @__PURE__ */ jsx8(Divider, { vertical: true, color: dividerColor, height: "100%" }),
733
+ showDivider && /* @__PURE__ */ jsx8(Divider, { vertical: true, color: dividerLineColor, height: "100%" }),
671
734
  /* @__PURE__ */ jsx8(
672
735
  Box,
673
736
  {
737
+ width: sizeStyles.iconContainerSize,
738
+ height: sizeStyles.iconContainerSize,
674
739
  alignItems: "center",
675
740
  justifyContent: "center",
676
- paddingHorizontal: sizeStyles.iconPadding,
677
- children: /* @__PURE__ */ jsx8(Icon, { size: sizeStyles.iconSize, color: textColor, children: iconRight })
741
+ children: cloneIconWithDefaults(iconRight, sizeStyles.iconSize, textColor)
678
742
  }
679
743
  )
680
744
  ]
@@ -687,9 +751,20 @@ var Button = ({
687
751
  Button.displayName = "Button";
688
752
 
689
753
  // src/IconButton.tsx
690
- import { useState as useState2 } from "react";
754
+ import React5, { useState as useState2 } from "react";
691
755
  import { useDesignSystem as useDesignSystem2 } from "@xsolla/xui-core";
692
- import { jsx as jsx9 } from "react/jsx-runtime";
756
+ import { jsx as jsx9, jsxs as jsxs2 } from "react/jsx-runtime";
757
+ var cloneIconWithDefaults2 = (icon, defaultSize, defaultColor) => {
758
+ if (!React5.isValidElement(icon)) return icon;
759
+ const iconElement = icon;
760
+ const existingProps = iconElement.props || {};
761
+ return React5.cloneElement(iconElement, {
762
+ ...existingProps,
763
+ // Preserve existing props (including accessibility attributes)
764
+ size: existingProps.size ?? defaultSize,
765
+ color: existingProps.color ?? defaultColor
766
+ });
767
+ };
693
768
  var IconButton = ({
694
769
  variant = "primary",
695
770
  tone = "brand",
@@ -712,9 +787,17 @@ var IconButton = ({
712
787
  const [isKeyboardPressed, setIsKeyboardPressed] = useState2(false);
713
788
  const isDisabled = disabled || loading;
714
789
  const sizeStyles = theme.sizing.button(size);
715
- const variantStyles = theme?.colors?.control?.[tone]?.[variant] || theme?.colors?.control?.brand?.primary || {
790
+ const controlTone = theme?.colors?.control?.[tone];
791
+ const variantStyles = controlTone?.[variant] || theme?.colors?.control?.brand?.primary || {
716
792
  bg: "transparent",
717
- text: { primary: "#000" }
793
+ bgHover: "transparent",
794
+ bgPress: "transparent",
795
+ bgDisable: "transparent",
796
+ border: "transparent",
797
+ borderHover: "transparent",
798
+ borderPress: "transparent",
799
+ borderDisable: "transparent",
800
+ text: { primary: "#000", secondary: "#000", disable: "#666" }
718
801
  };
719
802
  const handlePress = () => {
720
803
  if (!isDisabled && onPress) {
@@ -738,16 +821,15 @@ var IconButton = ({
738
821
  }
739
822
  }
740
823
  };
741
- const styles = variantStyles;
742
- let backgroundColor = styles.bg;
824
+ let backgroundColor = variantStyles.bg;
743
825
  if (disabled) {
744
- backgroundColor = styles.bgDisable || styles.bg;
826
+ backgroundColor = variantStyles.bgDisable || variantStyles.bg;
745
827
  } else if (isKeyboardPressed) {
746
- backgroundColor = styles.bgPress || styles.bg;
828
+ backgroundColor = variantStyles.bgPress || variantStyles.bg;
747
829
  }
748
- const borderColor = disabled ? styles.borderDisable || styles.border : styles.border;
749
- const textColor = disabled ? styles.text?.disable || styles.text?.primary : styles.text?.primary;
750
- return /* @__PURE__ */ jsx9(
830
+ const borderColor = disabled ? variantStyles.borderDisable || variantStyles.border : variantStyles.border;
831
+ const textColor = disabled ? variantStyles.text?.disable || variantStyles.text?.primary : variantStyles.text?.primary;
832
+ return /* @__PURE__ */ jsxs2(
751
833
  Box,
752
834
  {
753
835
  as: "button",
@@ -769,7 +851,7 @@ var IconButton = ({
769
851
  backgroundColor,
770
852
  borderColor,
771
853
  borderWidth: borderColor !== "transparent" && borderColor !== "rgba(255, 255, 255, 0)" ? 1 : 0,
772
- borderRadius: theme.radius.button,
854
+ borderRadius: sizeStyles.borderRadius,
773
855
  height: sizeStyles.height,
774
856
  width: sizeStyles.height,
775
857
  padding: 0,
@@ -780,10 +862,10 @@ var IconButton = ({
780
862
  cursor: disabled ? "not-allowed" : loading ? "wait" : "pointer",
781
863
  opacity: disabled ? 0.6 : 1,
782
864
  hoverStyle: !isDisabled ? {
783
- backgroundColor: styles.bgHover
865
+ backgroundColor: variantStyles.bgHover
784
866
  } : void 0,
785
867
  pressStyle: !isDisabled ? {
786
- backgroundColor: styles.bgPress
868
+ backgroundColor: variantStyles.bgPress
787
869
  } : void 0,
788
870
  focusStyle: {
789
871
  outlineColor: theme.colors.border.brand,
@@ -791,14 +873,40 @@ var IconButton = ({
791
873
  outlineOffset: 2,
792
874
  outlineStyle: "solid"
793
875
  },
794
- children: loading ? /* @__PURE__ */ jsx9(
795
- Spinner,
796
- {
797
- color: textColor,
798
- size: sizeStyles.spinnerSize,
799
- "aria-hidden": true
800
- }
801
- ) : /* @__PURE__ */ jsx9(Icon, { size: sizeStyles.iconSize, color: textColor, "aria-hidden": true, children: icon })
876
+ children: [
877
+ loading && /* @__PURE__ */ jsx9(
878
+ Box,
879
+ {
880
+ position: "absolute",
881
+ top: 0,
882
+ left: 0,
883
+ right: 0,
884
+ bottom: 0,
885
+ alignItems: "center",
886
+ justifyContent: "center",
887
+ zIndex: 1,
888
+ children: /* @__PURE__ */ jsx9(
889
+ Spinner,
890
+ {
891
+ color: textColor,
892
+ size: sizeStyles.spinnerSize,
893
+ "aria-hidden": true
894
+ }
895
+ )
896
+ }
897
+ ),
898
+ /* @__PURE__ */ jsx9(
899
+ Box,
900
+ {
901
+ "aria-hidden": true,
902
+ style: {
903
+ opacity: loading ? 0 : 1,
904
+ pointerEvents: loading ? "none" : "auto"
905
+ },
906
+ children: cloneIconWithDefaults2(icon, sizeStyles.iconSize, textColor)
907
+ }
908
+ )
909
+ ]
802
910
  }
803
911
  );
804
912
  };
@@ -807,7 +915,7 @@ IconButton.displayName = "IconButton";
807
915
  // src/FlexButton.tsx
808
916
  import { useRef, useState as useState3 } from "react";
809
917
  import { useDesignSystem as useDesignSystem3 } from "@xsolla/xui-core";
810
- import { Fragment, jsx as jsx10, jsxs as jsxs2 } from "react/jsx-runtime";
918
+ import { Fragment, jsx as jsx10, jsxs as jsxs3 } from "react/jsx-runtime";
811
919
  var ICON_SIZES = {
812
920
  xs: 12,
813
921
  sm: 14,
@@ -1129,7 +1237,7 @@ var FlexButton = ({
1129
1237
  tabIndex,
1130
1238
  style: buttonStyle,
1131
1239
  "data-testid": testID || "flex-button",
1132
- children: /* @__PURE__ */ jsx10("span", { style: contentStyle, children: loading ? /* @__PURE__ */ jsx10("span", { style: spinnerStyle, children: /* @__PURE__ */ jsx10(Spinner, { size: spinnerSize, color: spinnerColor }) }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
1240
+ children: /* @__PURE__ */ jsx10("span", { style: contentStyle, children: loading ? /* @__PURE__ */ jsx10("span", { style: spinnerStyle, children: /* @__PURE__ */ jsx10(Spinner, { size: spinnerSize, color: spinnerColor }) }) : /* @__PURE__ */ jsxs3(Fragment, { children: [
1133
1241
  iconLeft && /* @__PURE__ */ jsx10(Icon, { size: iconSize, color: colors.text, children: iconLeft }),
1134
1242
  /* @__PURE__ */ jsx10("span", { children }),
1135
1243
  iconRight && /* @__PURE__ */ jsx10(Icon, { size: iconSize, color: colors.text, children: iconRight })
@@ -1142,7 +1250,7 @@ FlexButton.displayName = "FlexButton";
1142
1250
  // src/ButtonGroup.tsx
1143
1251
  import React6 from "react";
1144
1252
  import { useDesignSystem as useDesignSystem4 } from "@xsolla/xui-core";
1145
- import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs3 } from "react/jsx-runtime";
1253
+ import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs4 } from "react/jsx-runtime";
1146
1254
  var ButtonGroup = ({
1147
1255
  orientation = "horizontal",
1148
1256
  size = "md",
@@ -1191,7 +1299,7 @@ var ButtonGroup = ({
1191
1299
  const computedAriaDescribedBy = [
1192
1300
  ariaDescribedBy,
1193
1301
  error && errorId ? errorId : void 0,
1194
- description && !error && descriptionId ? descriptionId : void 0
1302
+ description && descriptionId ? descriptionId : void 0
1195
1303
  ].filter(Boolean).join(" ") || void 0;
1196
1304
  const processChildren = (childrenToProcess) => {
1197
1305
  if (orientation === "vertical") {
@@ -1213,7 +1321,7 @@ var ButtonGroup = ({
1213
1321
  if (useSpaceBetween) {
1214
1322
  const firstChild = processedChildren[0];
1215
1323
  const restChildren = processedChildren.slice(1);
1216
- return /* @__PURE__ */ jsxs3(Fragment2, { children: [
1324
+ return /* @__PURE__ */ jsxs4(Fragment2, { children: [
1217
1325
  firstChild,
1218
1326
  /* @__PURE__ */ jsx11(Box, { flexDirection: "row", gap: computedGap, children: restChildren })
1219
1327
  ] });
@@ -1223,7 +1331,7 @@ var ButtonGroup = ({
1223
1331
  }
1224
1332
  return children;
1225
1333
  };
1226
- return /* @__PURE__ */ jsxs3(Box, { flexDirection: "column", width: "100%", gap: 8, children: [
1334
+ return /* @__PURE__ */ jsxs4(Box, { flexDirection: "column", width: "100%", gap: 8, children: [
1227
1335
  /* @__PURE__ */ jsx11(
1228
1336
  Box,
1229
1337
  {
@@ -1254,7 +1362,7 @@ var ButtonGroup = ({
1254
1362
  children: error
1255
1363
  }
1256
1364
  ) }),
1257
- description && !error && /* @__PURE__ */ jsx11(Box, { marginTop: 4, children: /* @__PURE__ */ jsx11(
1365
+ description && /* @__PURE__ */ jsx11(Box, { marginTop: 4, children: /* @__PURE__ */ jsx11(
1258
1366
  Text,
1259
1367
  {
1260
1368
  id: descriptionId,