@yahoo/uds-mobile 2.21.1 → 2.22.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.
Files changed (53) hide show
  1. package/README.md +2 -0
  2. package/dist/bin/automated-config/dist/generated/generatedConfigs.mjs +67 -51
  3. package/dist/components/Button/buttonTheme.cjs +69 -0
  4. package/dist/components/Button/buttonTheme.d.cts +24 -0
  5. package/dist/components/Button/buttonTheme.d.cts.map +1 -0
  6. package/dist/components/Button/buttonTheme.d.ts +24 -0
  7. package/dist/components/Button/buttonTheme.d.ts.map +1 -0
  8. package/dist/components/Button/buttonTheme.js +68 -0
  9. package/dist/components/Button/buttonTheme.js.map +1 -0
  10. package/dist/components/Button.cjs +7 -1
  11. package/dist/components/Button.d.cts.map +1 -1
  12. package/dist/components/Button.d.ts.map +1 -1
  13. package/dist/components/Button.js +7 -1
  14. package/dist/components/Button.js.map +1 -1
  15. package/dist/components/IconButton.cjs +33 -21
  16. package/dist/components/IconButton.d.cts.map +1 -1
  17. package/dist/components/IconButton.d.ts.map +1 -1
  18. package/dist/components/IconButton.js +34 -22
  19. package/dist/components/IconButton.js.map +1 -1
  20. package/dist/components/Switch.cjs +34 -12
  21. package/dist/components/Switch.d.cts.map +1 -1
  22. package/dist/components/Switch.d.ts.map +1 -1
  23. package/dist/components/Switch.js +36 -14
  24. package/dist/components/Switch.js.map +1 -1
  25. package/dist/components/UDSGestureProvider.cjs +4 -0
  26. package/dist/components/UDSGestureProvider.d.cts +3 -0
  27. package/dist/components/UDSGestureProvider.d.ts +3 -0
  28. package/dist/components/UDSGestureProvider.js +3 -0
  29. package/dist/components/UDSProvider.cjs +10 -5
  30. package/dist/components/UDSProvider.d.cts +15 -7
  31. package/dist/components/UDSProvider.d.cts.map +1 -1
  32. package/dist/components/UDSProvider.d.ts +15 -7
  33. package/dist/components/UDSProvider.d.ts.map +1 -1
  34. package/dist/components/UDSProvider.js +10 -6
  35. package/dist/components/UDSProvider.js.map +1 -1
  36. package/dist/components/internal/Overlay/OverlayPortal.js.map +1 -1
  37. package/dist/jest/mocks/styles.cjs +21 -9
  38. package/dist/jest/mocks/styles.d.cts.map +1 -1
  39. package/dist/jest/mocks/styles.d.ts.map +1 -1
  40. package/dist/jest/mocks/styles.js +21 -9
  41. package/dist/jest/mocks/styles.js.map +1 -1
  42. package/dist/jest/mocks/unistyles.cjs +16 -1
  43. package/dist/jest/mocks/unistyles.d.cts +56 -2
  44. package/dist/jest/mocks/unistyles.d.cts.map +1 -1
  45. package/dist/jest/mocks/unistyles.d.ts +56 -2
  46. package/dist/jest/mocks/unistyles.d.ts.map +1 -1
  47. package/dist/jest/mocks/unistyles.js +16 -1
  48. package/dist/jest/mocks/unistyles.js.map +1 -1
  49. package/dist/portal.cjs +1 -1
  50. package/dist/portal.js +1 -1
  51. package/dist/portal.js.map +1 -1
  52. package/generated/styles.d.ts +1 -1
  53. package/package.json +11 -1
package/README.md CHANGED
@@ -681,6 +681,7 @@ import type { HStackProps } from '@yahoo/uds-mobile/HStack';
681
681
  import type { ScreenProps } from '@yahoo/uds-mobile/Screen';
682
682
  import type { PressableProps } from '@yahoo/uds-mobile/Pressable';
683
683
  import type { IconSlotProps, IconSlotType } from '@yahoo/uds-mobile/IconSlot';
684
+ import type { UDSGestureProviderProps } from '@yahoo/uds-mobile/UDSGestureProvider';
684
685
  ```
685
686
 
686
687
  ### Exports
@@ -708,6 +709,7 @@ import { RadioGroup } from '@yahoo/uds-mobile/RadioGroup';
708
709
  import { Screen } from '@yahoo/uds-mobile/Screen';
709
710
  import { Switch } from '@yahoo/uds-mobile/Switch';
710
711
  import { Text } from '@yahoo/uds-mobile/Text';
712
+ import { UDSGestureProvider } from '@yahoo/uds-mobile/UDSGestureProvider';
711
713
  import { VStack } from '@yahoo/uds-mobile/VStack';
712
714
 
713
715
  // Sub-exports
@@ -18617,7 +18617,7 @@ const IconButtonConfig = {
18617
18617
  label: "icon",
18618
18618
  properties: { size: {
18619
18619
  defaults: {
18620
- lg: "md",
18620
+ lg: "sm",
18621
18621
  md: "sm",
18622
18622
  sm: "sm",
18623
18623
  xl: "lg",
@@ -18636,57 +18636,73 @@ const IconButtonConfig = {
18636
18636
  },
18637
18637
  root: {
18638
18638
  label: "root",
18639
- properties: { spacing: {
18640
- defaults: {
18641
- lg: "3.5",
18642
- md: "3",
18643
- sm: "2",
18644
- xl: "4",
18645
- xs: "2"
18639
+ properties: {
18640
+ controlHeight: {
18641
+ defaults: {
18642
+ lg: 44,
18643
+ md: 32,
18644
+ sm: 24,
18645
+ xl: 64,
18646
+ xs: 28
18647
+ },
18648
+ label: "Control height",
18649
+ name: "controlHeight",
18650
+ optionalInDefaultSchema: true,
18651
+ typeOfFixture: ["positiveIntegers"],
18652
+ values: []
18646
18653
  },
18647
- label: "Spacing",
18648
- name: "spacing",
18649
- typeOfFixture: ["spacingAliases"],
18650
- values: [[
18651
- "0",
18652
- "px",
18653
- "0.5",
18654
- "1",
18655
- "1.5",
18656
- "2",
18657
- "2.5",
18658
- "3",
18659
- "3.5",
18660
- "4",
18661
- "4.5",
18662
- "5",
18663
- "5.5",
18664
- "6",
18665
- "7",
18666
- "8",
18667
- "9",
18668
- "10",
18669
- "11",
18670
- "12",
18671
- "14",
18672
- "16",
18673
- "20",
18674
- "24",
18675
- "28",
18676
- "32",
18677
- "36",
18678
- "40",
18679
- "44",
18680
- "48",
18681
- "52",
18682
- "56",
18683
- "60",
18684
- "64",
18685
- "72",
18686
- "80",
18687
- "96"
18688
- ]]
18689
- } }
18654
+ spacing: {
18655
+ defaults: {
18656
+ lg: "3.5",
18657
+ md: "3",
18658
+ sm: "2",
18659
+ xl: "4",
18660
+ xs: "2"
18661
+ },
18662
+ label: "Spacing",
18663
+ name: "spacing",
18664
+ typeOfFixture: ["spacingAliases"],
18665
+ values: [[
18666
+ "0",
18667
+ "px",
18668
+ "0.5",
18669
+ "1",
18670
+ "1.5",
18671
+ "2",
18672
+ "2.5",
18673
+ "3",
18674
+ "3.5",
18675
+ "4",
18676
+ "4.5",
18677
+ "5",
18678
+ "5.5",
18679
+ "6",
18680
+ "7",
18681
+ "8",
18682
+ "9",
18683
+ "10",
18684
+ "11",
18685
+ "12",
18686
+ "14",
18687
+ "16",
18688
+ "20",
18689
+ "24",
18690
+ "28",
18691
+ "32",
18692
+ "36",
18693
+ "40",
18694
+ "44",
18695
+ "48",
18696
+ "52",
18697
+ "56",
18698
+ "60",
18699
+ "64",
18700
+ "72",
18701
+ "80",
18702
+ "96"
18703
+ ]]
18704
+ }
18705
+ }
18690
18706
  }
18691
18707
  },
18692
18708
  options: [
@@ -0,0 +1,69 @@
1
+ /*! © 2026 Yahoo, Inc. UDS Mobile v0.0.0-development */
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ //#region src/components/Button/buttonTheme.ts
4
+ const SHARED_BUTTON_ICONBUTTON_SIZES = new Set([
5
+ "xs",
6
+ "sm",
7
+ "md",
8
+ "lg"
9
+ ]);
10
+ function buttonSizePath(size, layer) {
11
+ return `button/size/${size}/${layer}/rest`;
12
+ }
13
+ function iconButtonSizePath(size, layer) {
14
+ return `iconButton/size/${size}/${layer}/rest`;
15
+ }
16
+ function getLayerStyle(theme, path) {
17
+ const style = theme.components[path];
18
+ return style && typeof style === "object" ? style : {};
19
+ }
20
+ function getIconSize(iconStyle) {
21
+ return (typeof iconStyle.fontSize === "number" ? iconStyle.fontSize : void 0) ?? (typeof iconStyle.lineHeight === "number" ? iconStyle.lineHeight : void 0) ?? 16;
22
+ }
23
+ function getVerticalPadding(rootStyle) {
24
+ return (typeof rootStyle.paddingVertical === "number" ? rootStyle.paddingVertical : void 0) ?? (typeof rootStyle.padding === "number" ? rootStyle.padding : void 0) ?? 0;
25
+ }
26
+ function getButtonContentSize(iconSize, textStyle) {
27
+ const lineHeight = typeof textStyle.lineHeight === "number" ? textStyle.lineHeight : void 0;
28
+ const fontSize = typeof textStyle.fontSize === "number" ? textStyle.fontSize : iconSize;
29
+ return Math.max(iconSize, lineHeight ?? fontSize);
30
+ }
31
+ /** Control height from vertical padding plus max(icon size, text line-height), matching web. */
32
+ function getButtonControlMetrics(theme, size) {
33
+ const rootStyle = getLayerStyle(theme, buttonSizePath(size, "root"));
34
+ const iconStyle = getLayerStyle(theme, buttonSizePath(size, "icon"));
35
+ const textStyle = getLayerStyle(theme, buttonSizePath(size, "rootText"));
36
+ const paddingVertical = getVerticalPadding(rootStyle);
37
+ const iconSize = getIconSize(iconStyle);
38
+ const contentSize = getButtonContentSize(iconSize, textStyle);
39
+ return {
40
+ controlHeight: Math.ceil(paddingVertical * 2 + contentSize),
41
+ contentLineHeight: Math.ceil(contentSize),
42
+ iconSize
43
+ };
44
+ }
45
+ /** IconButton uses explicit control height only when CSS sets height (match-button-height opt-in). */
46
+ function getIconButtonControlMetrics(theme, size) {
47
+ const rootStyle = getLayerStyle(theme, iconButtonSizePath(size, "root"));
48
+ const iconSize = getIconSize(getLayerStyle(theme, iconButtonSizePath(size, "icon")));
49
+ const explicitHeight = typeof rootStyle.height === "number" ? rootStyle.height : void 0;
50
+ if (SHARED_BUTTON_ICONBUTTON_SIZES.has(size) && explicitHeight !== void 0) return {
51
+ controlHeight: explicitHeight,
52
+ iconSize
53
+ };
54
+ return {
55
+ controlHeight: 0,
56
+ iconSize
57
+ };
58
+ }
59
+ /** Label line-height matches content box (max of icon size and line-height). */
60
+ function getButtonTextStyleForControlHeight(textStyle, contentLineHeight) {
61
+ return {
62
+ ...textStyle,
63
+ lineHeight: contentLineHeight
64
+ };
65
+ }
66
+ //#endregion
67
+ exports.getButtonControlMetrics = getButtonControlMetrics;
68
+ exports.getButtonTextStyleForControlHeight = getButtonTextStyleForControlHeight;
69
+ exports.getIconButtonControlMetrics = getIconButtonControlMetrics;
@@ -0,0 +1,24 @@
1
+
2
+ import { ButtonSize, IconButtonSize } from "../../types/dist/index.cjs";
3
+ import { TextStyle } from "react-native";
4
+
5
+ //#region src/components/Button/buttonTheme.d.ts
6
+ type ButtonTheme = {
7
+ components: Record<string, any>;
8
+ };
9
+ /** Control height from vertical padding plus max(icon size, text line-height), matching web. */
10
+ declare function getButtonControlMetrics(theme: ButtonTheme, size: ButtonSize): {
11
+ controlHeight: number;
12
+ contentLineHeight: number;
13
+ iconSize: number;
14
+ };
15
+ /** IconButton uses explicit control height only when CSS sets height (match-button-height opt-in). */
16
+ declare function getIconButtonControlMetrics(theme: ButtonTheme, size: IconButtonSize): {
17
+ controlHeight: number;
18
+ iconSize: number;
19
+ };
20
+ /** Label line-height matches content box (max of icon size and line-height). */
21
+ declare function getButtonTextStyleForControlHeight(textStyle: TextStyle, contentLineHeight: number): TextStyle;
22
+ //#endregion
23
+ export { getButtonControlMetrics, getButtonTextStyleForControlHeight, getIconButtonControlMetrics };
24
+ //# sourceMappingURL=buttonTheme.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buttonTheme.d.cts","names":[],"sources":["../../../src/components/Button/buttonTheme.ts"],"mappings":";;;;;KAGK,WAAA;EAEH,UAAA,EAAY,MAAA;AAAA;;iBAyCE,uBAAA,CACd,KAAA,EAAO,WAAA,EACP,IAAA,EAAM,UAAA;EACH,aAAA;EAAuB,iBAAA;EAA2B,QAAA;AAAA;;iBAgBvC,2BAAA,CACd,KAAA,EAAO,WAAA,EACP,IAAA,EAAM,cAAA;EACH,aAAA;EAAuB,QAAA;AAAA;;iBAcZ,kCAAA,CACd,SAAA,EAAW,SAAA,EACX,iBAAA,WACC,SAAA"}
@@ -0,0 +1,24 @@
1
+
2
+ import { ButtonSize, IconButtonSize } from "../../types/dist/index.js";
3
+ import { TextStyle } from "react-native";
4
+
5
+ //#region src/components/Button/buttonTheme.d.ts
6
+ type ButtonTheme = {
7
+ components: Record<string, any>;
8
+ };
9
+ /** Control height from vertical padding plus max(icon size, text line-height), matching web. */
10
+ declare function getButtonControlMetrics(theme: ButtonTheme, size: ButtonSize): {
11
+ controlHeight: number;
12
+ contentLineHeight: number;
13
+ iconSize: number;
14
+ };
15
+ /** IconButton uses explicit control height only when CSS sets height (match-button-height opt-in). */
16
+ declare function getIconButtonControlMetrics(theme: ButtonTheme, size: IconButtonSize): {
17
+ controlHeight: number;
18
+ iconSize: number;
19
+ };
20
+ /** Label line-height matches content box (max of icon size and line-height). */
21
+ declare function getButtonTextStyleForControlHeight(textStyle: TextStyle, contentLineHeight: number): TextStyle;
22
+ //#endregion
23
+ export { getButtonControlMetrics, getButtonTextStyleForControlHeight, getIconButtonControlMetrics };
24
+ //# sourceMappingURL=buttonTheme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buttonTheme.d.ts","names":[],"sources":["../../../src/components/Button/buttonTheme.ts"],"mappings":";;;;;KAGK,WAAA;EAEH,UAAA,EAAY,MAAA;AAAA;;iBAyCE,uBAAA,CACd,KAAA,EAAO,WAAA,EACP,IAAA,EAAM,UAAA;EACH,aAAA;EAAuB,iBAAA;EAA2B,QAAA;AAAA;;iBAgBvC,2BAAA,CACd,KAAA,EAAO,WAAA,EACP,IAAA,EAAM,cAAA;EACH,aAAA;EAAuB,QAAA;AAAA;;iBAcZ,kCAAA,CACd,SAAA,EAAW,SAAA,EACX,iBAAA,WACC,SAAA"}
@@ -0,0 +1,68 @@
1
+ /*! © 2026 Yahoo, Inc. UDS Mobile v0.0.0-development */
2
+ //#region src/components/Button/buttonTheme.ts
3
+ const SHARED_BUTTON_ICONBUTTON_SIZES = new Set([
4
+ "xs",
5
+ "sm",
6
+ "md",
7
+ "lg"
8
+ ]);
9
+ function buttonSizePath(size, layer) {
10
+ return `button/size/${size}/${layer}/rest`;
11
+ }
12
+ function iconButtonSizePath(size, layer) {
13
+ return `iconButton/size/${size}/${layer}/rest`;
14
+ }
15
+ function getLayerStyle(theme, path) {
16
+ const style = theme.components[path];
17
+ return style && typeof style === "object" ? style : {};
18
+ }
19
+ function getIconSize(iconStyle) {
20
+ return (typeof iconStyle.fontSize === "number" ? iconStyle.fontSize : void 0) ?? (typeof iconStyle.lineHeight === "number" ? iconStyle.lineHeight : void 0) ?? 16;
21
+ }
22
+ function getVerticalPadding(rootStyle) {
23
+ return (typeof rootStyle.paddingVertical === "number" ? rootStyle.paddingVertical : void 0) ?? (typeof rootStyle.padding === "number" ? rootStyle.padding : void 0) ?? 0;
24
+ }
25
+ function getButtonContentSize(iconSize, textStyle) {
26
+ const lineHeight = typeof textStyle.lineHeight === "number" ? textStyle.lineHeight : void 0;
27
+ const fontSize = typeof textStyle.fontSize === "number" ? textStyle.fontSize : iconSize;
28
+ return Math.max(iconSize, lineHeight ?? fontSize);
29
+ }
30
+ /** Control height from vertical padding plus max(icon size, text line-height), matching web. */
31
+ function getButtonControlMetrics(theme, size) {
32
+ const rootStyle = getLayerStyle(theme, buttonSizePath(size, "root"));
33
+ const iconStyle = getLayerStyle(theme, buttonSizePath(size, "icon"));
34
+ const textStyle = getLayerStyle(theme, buttonSizePath(size, "rootText"));
35
+ const paddingVertical = getVerticalPadding(rootStyle);
36
+ const iconSize = getIconSize(iconStyle);
37
+ const contentSize = getButtonContentSize(iconSize, textStyle);
38
+ return {
39
+ controlHeight: Math.ceil(paddingVertical * 2 + contentSize),
40
+ contentLineHeight: Math.ceil(contentSize),
41
+ iconSize
42
+ };
43
+ }
44
+ /** IconButton uses explicit control height only when CSS sets height (match-button-height opt-in). */
45
+ function getIconButtonControlMetrics(theme, size) {
46
+ const rootStyle = getLayerStyle(theme, iconButtonSizePath(size, "root"));
47
+ const iconSize = getIconSize(getLayerStyle(theme, iconButtonSizePath(size, "icon")));
48
+ const explicitHeight = typeof rootStyle.height === "number" ? rootStyle.height : void 0;
49
+ if (SHARED_BUTTON_ICONBUTTON_SIZES.has(size) && explicitHeight !== void 0) return {
50
+ controlHeight: explicitHeight,
51
+ iconSize
52
+ };
53
+ return {
54
+ controlHeight: 0,
55
+ iconSize
56
+ };
57
+ }
58
+ /** Label line-height matches content box (max of icon size and line-height). */
59
+ function getButtonTextStyleForControlHeight(textStyle, contentLineHeight) {
60
+ return {
61
+ ...textStyle,
62
+ lineHeight: contentLineHeight
63
+ };
64
+ }
65
+ //#endregion
66
+ export { getButtonControlMetrics, getButtonTextStyleForControlHeight, getIconButtonControlMetrics };
67
+
68
+ //# sourceMappingURL=buttonTheme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buttonTheme.js","names":[],"sources":["../../../src/components/Button/buttonTheme.ts"],"sourcesContent":["import type { ButtonSize, IconButtonSize } from '@yahoo/uds-types';\nimport type { TextStyle, ViewStyle } from 'react-native';\n\ntype ButtonTheme = {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n components: Record<string, any>;\n};\n\nconst SHARED_BUTTON_ICONBUTTON_SIZES = new Set<IconButtonSize>(['xs', 'sm', 'md', 'lg']);\n\nfunction buttonSizePath(size: ButtonSize, layer: 'root' | 'icon' | 'rootText'): string {\n return `button/size/${size}/${layer}/rest`;\n}\n\nfunction iconButtonSizePath(size: IconButtonSize, layer: 'root' | 'icon'): string {\n return `iconButton/size/${size}/${layer}/rest`;\n}\n\nfunction getLayerStyle(theme: ButtonTheme, path: string): ViewStyle | TextStyle {\n const style = theme.components[path];\n return style && typeof style === 'object' ? style : {};\n}\n\nfunction getIconSize(iconStyle: TextStyle): number {\n return (\n (typeof iconStyle.fontSize === 'number' ? iconStyle.fontSize : undefined) ??\n (typeof iconStyle.lineHeight === 'number' ? iconStyle.lineHeight : undefined) ??\n 16\n );\n}\n\nfunction getVerticalPadding(rootStyle: ViewStyle): number {\n return (\n (typeof rootStyle.paddingVertical === 'number' ? rootStyle.paddingVertical : undefined) ??\n (typeof rootStyle.padding === 'number' ? rootStyle.padding : undefined) ??\n 0\n );\n}\n\nfunction getButtonContentSize(iconSize: number, textStyle: TextStyle): number {\n const lineHeight = typeof textStyle.lineHeight === 'number' ? textStyle.lineHeight : undefined;\n const fontSize = typeof textStyle.fontSize === 'number' ? textStyle.fontSize : iconSize;\n return Math.max(iconSize, lineHeight ?? fontSize);\n}\n\n/** Control height from vertical padding plus max(icon size, text line-height), matching web. */\nexport function getButtonControlMetrics(\n theme: ButtonTheme,\n size: ButtonSize,\n): { controlHeight: number; contentLineHeight: number; iconSize: number } {\n const rootStyle = getLayerStyle(theme, buttonSizePath(size, 'root')) as ViewStyle;\n const iconStyle = getLayerStyle(theme, buttonSizePath(size, 'icon')) as TextStyle;\n const textStyle = getLayerStyle(theme, buttonSizePath(size, 'rootText')) as TextStyle;\n const paddingVertical = getVerticalPadding(rootStyle);\n const iconSize = getIconSize(iconStyle);\n const contentSize = getButtonContentSize(iconSize, textStyle);\n\n return {\n controlHeight: Math.ceil(paddingVertical * 2 + contentSize),\n contentLineHeight: Math.ceil(contentSize),\n iconSize,\n };\n}\n\n/** IconButton uses explicit control height only when CSS sets height (match-button-height opt-in). */\nexport function getIconButtonControlMetrics(\n theme: ButtonTheme,\n size: IconButtonSize,\n): { controlHeight: number; iconSize: number } {\n const rootStyle = getLayerStyle(theme, iconButtonSizePath(size, 'root')) as ViewStyle;\n const iconStyle = getLayerStyle(theme, iconButtonSizePath(size, 'icon')) as TextStyle;\n const iconSize = getIconSize(iconStyle);\n const explicitHeight = typeof rootStyle.height === 'number' ? rootStyle.height : undefined;\n\n if (SHARED_BUTTON_ICONBUTTON_SIZES.has(size) && explicitHeight !== undefined) {\n return { controlHeight: explicitHeight, iconSize };\n }\n\n return { controlHeight: 0, iconSize };\n}\n\n/** Label line-height matches content box (max of icon size and line-height). */\nexport function getButtonTextStyleForControlHeight(\n textStyle: TextStyle,\n contentLineHeight: number,\n): TextStyle {\n return {\n ...textStyle,\n lineHeight: contentLineHeight,\n };\n}\n"],"mappings":";;AAQA,MAAM,iCAAiC,IAAI,IAAoB;CAAC;CAAM;CAAM;CAAM;CAAK,CAAC;AAExF,SAAS,eAAe,MAAkB,OAA6C;CACrF,OAAO,eAAe,KAAK,GAAG,MAAM;;AAGtC,SAAS,mBAAmB,MAAsB,OAAgC;CAChF,OAAO,mBAAmB,KAAK,GAAG,MAAM;;AAG1C,SAAS,cAAc,OAAoB,MAAqC;CAC9E,MAAM,QAAQ,MAAM,WAAW;CAC/B,OAAO,SAAS,OAAO,UAAU,WAAW,QAAQ,EAAE;;AAGxD,SAAS,YAAY,WAA8B;CACjD,QACG,OAAO,UAAU,aAAa,WAAW,UAAU,WAAW,KAAA,OAC9D,OAAO,UAAU,eAAe,WAAW,UAAU,aAAa,KAAA,MACnE;;AAIJ,SAAS,mBAAmB,WAA8B;CACxD,QACG,OAAO,UAAU,oBAAoB,WAAW,UAAU,kBAAkB,KAAA,OAC5E,OAAO,UAAU,YAAY,WAAW,UAAU,UAAU,KAAA,MAC7D;;AAIJ,SAAS,qBAAqB,UAAkB,WAA8B;CAC5E,MAAM,aAAa,OAAO,UAAU,eAAe,WAAW,UAAU,aAAa,KAAA;CACrF,MAAM,WAAW,OAAO,UAAU,aAAa,WAAW,UAAU,WAAW;CAC/E,OAAO,KAAK,IAAI,UAAU,cAAc,SAAS;;;AAInD,SAAgB,wBACd,OACA,MACwE;CACxE,MAAM,YAAY,cAAc,OAAO,eAAe,MAAM,OAAO,CAAC;CACpE,MAAM,YAAY,cAAc,OAAO,eAAe,MAAM,OAAO,CAAC;CACpE,MAAM,YAAY,cAAc,OAAO,eAAe,MAAM,WAAW,CAAC;CACxE,MAAM,kBAAkB,mBAAmB,UAAU;CACrD,MAAM,WAAW,YAAY,UAAU;CACvC,MAAM,cAAc,qBAAqB,UAAU,UAAU;CAE7D,OAAO;EACL,eAAe,KAAK,KAAK,kBAAkB,IAAI,YAAY;EAC3D,mBAAmB,KAAK,KAAK,YAAY;EACzC;EACD;;;AAIH,SAAgB,4BACd,OACA,MAC6C;CAC7C,MAAM,YAAY,cAAc,OAAO,mBAAmB,MAAM,OAAO,CAAC;CAExE,MAAM,WAAW,YADC,cAAc,OAAO,mBAAmB,MAAM,OAAO,CACjC,CAAC;CACvC,MAAM,iBAAiB,OAAO,UAAU,WAAW,WAAW,UAAU,SAAS,KAAA;CAEjF,IAAI,+BAA+B,IAAI,KAAK,IAAI,mBAAmB,KAAA,GACjE,OAAO;EAAE,eAAe;EAAgB;EAAU;CAGpD,OAAO;EAAE,eAAe;EAAG;EAAU;;;AAIvC,SAAgB,mCACd,WACA,mBACW;CACX,OAAO;EACL,GAAG;EACH,YAAY;EACb"}
@@ -5,11 +5,13 @@ const require_index = require("../motion-tokens/dist/index.cjs");
5
5
  const require_motion = require("../motion.cjs");
6
6
  const require_components_IconSlot = require("./IconSlot.cjs");
7
7
  const require_components_Text = require("./Text.cjs");
8
+ const require_components_Button_buttonTheme = require("./Button/buttonTheme.cjs");
8
9
  const require_components_Pressable = require("./Pressable.cjs");
9
10
  let react = require("react");
10
11
  let react_native = require("react-native");
11
12
  let react_jsx_runtime = require("react/jsx-runtime");
12
13
  let generated_styles = require("../../generated/styles");
14
+ let react_native_unistyles = require("react-native-unistyles");
13
15
  let react_native_reanimated = require("react-native-reanimated");
14
16
  react_native_reanimated = require_runtime.__toESM(react_native_reanimated);
15
17
  let react_native_unistyles_reanimated = require("react-native-unistyles/reanimated");
@@ -82,6 +84,8 @@ function AnimatedIconSlot({ children, visible, iconSize, gap }) {
82
84
  */
83
85
  const Button = (0, react.memo)(function Button({ variant = "primary", size = "md", iconVariant = "outline", startIcon, endIcon, loading, disabled, width: _width, children, style, accessibilityLabel, accessibilityHint, disableEffects = false, onPressIn, onPressOut, ref, ...props }) {
84
86
  const shouldAnimate = !disableEffects;
87
+ const { theme } = (0, react_native_unistyles.useUnistyles)();
88
+ const { controlHeight, contentLineHeight } = (0, react.useMemo)(() => require_components_Button_buttonTheme.getButtonControlMetrics(theme, size), [theme, size]);
85
89
  const [pressed, setPressed] = (0, react.useState)(false);
86
90
  generated_styles.buttonStyles.useVariants({
87
91
  size,
@@ -114,7 +118,7 @@ const Button = (0, react.memo)(function Button({ variant = "primary", size = "md
114
118
  const childrenNode = children && ((0, react.isValidElement)(children) ? children : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_Text.Text, {
115
119
  numberOfLines: 1,
116
120
  textAlign: "center",
117
- style: generated_styles.buttonStyles.text,
121
+ style: require_components_Button_buttonTheme.getButtonTextStyleForControlHeight(generated_styles.buttonStyles.text, contentLineHeight),
118
122
  children
119
123
  }));
120
124
  const a11yState = (0, react.useMemo)(() => ({
@@ -163,10 +167,12 @@ const Button = (0, react.memo)(function Button({ variant = "primary", size = "md
163
167
  });
164
168
  const rootStyles = (0, react.useMemo)(() => [
165
169
  generated_styles.buttonStyles.root,
170
+ { height: controlHeight },
166
171
  animatedStyles,
167
172
  typeof style === "function" ? style({ pressed }) : style
168
173
  ], [
169
174
  generated_styles.buttonStyles.root,
175
+ controlHeight,
170
176
  animatedStyles,
171
177
  style,
172
178
  pressed
@@ -1 +1 @@
1
- {"version":3,"file":"Button.d.cts","names":[],"sources":["../../src/components/Button.tsx"],"mappings":";;;;;;;;;UA8GU,WAAA,SAAoB,IAAA,CAAK,gBAAA;;EAEjC,OAAA,GAAU,iBAAA;EAFF;EAIR,IAAA,GAAO,UAAA;;EAEP,WAAA,GAAc,WAAA;EAJJ;EAMV,SAAA,GAAY,YAAA;EAFE;EAId,OAAA,GAAU,YAAA;EAAA;EAEV,OAAA;EAWU;EATV,QAAA;EAd4B;EAgB5B,QAAA,GAAW,KAAA,CAAM,SAAA;EAhBe;;;;EAqBhC,cAAA;EAjBA;EAmBA,GAAA,GAAM,GAAA,CAAI,IAAA;AAAA;;;;;;;;;;;;;;;;;AAAI;;;;;;;;;;;;;;;;cAuCV,MAAA,EAAM,OAAA,CAAA,oBAAA,CAAA,WAAA"}
1
+ {"version":3,"file":"Button.d.cts","names":[],"sources":["../../src/components/Button.tsx"],"mappings":";;;;;;;;;UAiHU,WAAA,SAAoB,IAAA,CAAK,gBAAA;;EAEjC,OAAA,GAAU,iBAAA;EAFF;EAIR,IAAA,GAAO,UAAA;;EAEP,WAAA,GAAc,WAAA;EAJJ;EAMV,SAAA,GAAY,YAAA;EAFE;EAId,OAAA,GAAU,YAAA;EAAA;EAEV,OAAA;EAWU;EATV,QAAA;EAd4B;EAgB5B,QAAA,GAAW,KAAA,CAAM,SAAA;EAhBe;;;;EAqBhC,cAAA;EAjBA;EAmBA,GAAA,GAAM,GAAA,CAAI,IAAA;AAAA;;;;;;;;;;;;;;;;;AAAI;;;;;;;;;;;;;;;;cAuCV,MAAA,EAAM,OAAA,CAAA,oBAAA,CAAA,WAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"Button.d.ts","names":[],"sources":["../../src/components/Button.tsx"],"mappings":";;;;;;;;;UA8GU,WAAA,SAAoB,IAAA,CAAK,gBAAA;;EAEjC,OAAA,GAAU,iBAAA;EAFF;EAIR,IAAA,GAAO,UAAA;;EAEP,WAAA,GAAc,WAAA;EAJJ;EAMV,SAAA,GAAY,YAAA;EAFE;EAId,OAAA,GAAU,YAAA;EAAA;EAEV,OAAA;EAWU;EATV,QAAA;EAd4B;EAgB5B,QAAA,GAAW,KAAA,CAAM,SAAA;EAhBe;;;;EAqBhC,cAAA;EAjBA;EAmBA,GAAA,GAAM,GAAA,CAAI,IAAA;AAAA;;;;;;;;;;;;;;;;;AAAI;;;;;;;;;;;;;;;;cAuCV,MAAA,EAAM,OAAA,CAAA,oBAAA,CAAA,WAAA"}
1
+ {"version":3,"file":"Button.d.ts","names":[],"sources":["../../src/components/Button.tsx"],"mappings":";;;;;;;;;UAiHU,WAAA,SAAoB,IAAA,CAAK,gBAAA;;EAEjC,OAAA,GAAU,iBAAA;EAFF;EAIR,IAAA,GAAO,UAAA;;EAEP,WAAA,GAAc,WAAA;EAJJ;EAMV,SAAA,GAAY,YAAA;EAFE;EAId,OAAA,GAAU,YAAA;EAAA;EAEV,OAAA;EAWU;EATV,QAAA;EAd4B;EAgB5B,QAAA,GAAW,KAAA,CAAM,SAAA;EAhBe;;;;EAqBhC,cAAA;EAjBA;EAmBA,GAAA,GAAM,GAAA,CAAI,IAAA;AAAA;;;;;;;;;;;;;;;;;AAAI;;;;;;;;;;;;;;;;cAuCV,MAAA,EAAM,OAAA,CAAA,oBAAA,CAAA,WAAA"}
@@ -3,11 +3,13 @@ import { SCALE_EFFECTS } from "../motion-tokens/dist/index.js";
3
3
  import { BUTTON_SPRING_CONFIG } from "../motion.js";
4
4
  import { IconSlot } from "./IconSlot.js";
5
5
  import { Text as Text$1 } from "./Text.js";
6
+ import { getButtonControlMetrics, getButtonTextStyleForControlHeight } from "./Button/buttonTheme.js";
6
7
  import { AnimatedPressable } from "./Pressable.js";
7
8
  import { isValidElement, memo, useCallback, useMemo, useState } from "react";
8
9
  import { ActivityIndicator } from "react-native";
9
10
  import { jsx, jsxs } from "react/jsx-runtime";
10
11
  import { buttonStyles } from "../../generated/styles";
12
+ import { useUnistyles } from "react-native-unistyles";
11
13
  import Animated, { Easing, interpolate, useAnimatedStyle, useDerivedValue, useSharedValue, withSpring, withTiming } from "react-native-reanimated";
12
14
  import { useAnimatedTheme } from "react-native-unistyles/reanimated";
13
15
  //#region src/components/Button.tsx
@@ -79,6 +81,8 @@ function AnimatedIconSlot({ children, visible, iconSize, gap }) {
79
81
  */
80
82
  const Button = memo(function Button({ variant = "primary", size = "md", iconVariant = "outline", startIcon, endIcon, loading, disabled, width: _width, children, style, accessibilityLabel, accessibilityHint, disableEffects = false, onPressIn, onPressOut, ref, ...props }) {
81
83
  const shouldAnimate = !disableEffects;
84
+ const { theme } = useUnistyles();
85
+ const { controlHeight, contentLineHeight } = useMemo(() => getButtonControlMetrics(theme, size), [theme, size]);
82
86
  const [pressed, setPressed] = useState(false);
83
87
  buttonStyles.useVariants({
84
88
  size,
@@ -111,7 +115,7 @@ const Button = memo(function Button({ variant = "primary", size = "md", iconVari
111
115
  const childrenNode = children && (isValidElement(children) ? children : /* @__PURE__ */ jsx(Text$1, {
112
116
  numberOfLines: 1,
113
117
  textAlign: "center",
114
- style: buttonStyles.text,
118
+ style: getButtonTextStyleForControlHeight(buttonStyles.text, contentLineHeight),
115
119
  children
116
120
  }));
117
121
  const a11yState = useMemo(() => ({
@@ -160,10 +164,12 @@ const Button = memo(function Button({ variant = "primary", size = "md", iconVari
160
164
  });
161
165
  const rootStyles = useMemo(() => [
162
166
  buttonStyles.root,
167
+ { height: controlHeight },
163
168
  animatedStyles,
164
169
  typeof style === "function" ? style({ pressed }) : style
165
170
  ], [
166
171
  buttonStyles.root,
172
+ controlHeight,
167
173
  animatedStyles,
168
174
  style,
169
175
  pressed
@@ -1 +1 @@
1
- {"version":3,"file":"Button.js","names":["Text"],"sources":["../../src/components/Button.tsx"],"sourcesContent":["import type { ButtonSize, ButtonVariantFlat, IconSize, IconVariant } from '@yahoo/uds-types';\nimport type { Ref } from 'react';\nimport { isValidElement, memo, useCallback, useMemo, useState } from 'react';\nimport type { View } from 'react-native';\nimport { ActivityIndicator } from 'react-native';\nimport Animated, {\n Easing,\n interpolate,\n useAnimatedStyle,\n useDerivedValue,\n useSharedValue,\n withSpring,\n withTiming,\n} from 'react-native-reanimated';\nimport { useAnimatedTheme } from 'react-native-unistyles/reanimated';\n\nimport { buttonStyles } from '../../generated/styles';\nimport { BUTTON_SPRING_CONFIG, SCALE_EFFECTS } from '../motion';\nimport type { IconSlotType } from './IconSlot';\nimport { IconSlot } from './IconSlot';\nimport type { PressableProps } from './Pressable';\nimport { AnimatedPressable } from './Pressable';\nimport { Text } from './Text';\n\n/* -------------------------------------------------------------------------- */\n/* Animation Helpers */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Interpolates a boxShadow string by scaling the alpha of all colors.\n * This allows smooth fade-in/out of shadows.\n */\nfunction interpolateShadowAlpha(shadow: string | undefined, alpha: number): string {\n 'worklet';\n if (!shadow) {\n return '';\n }\n if (alpha >= 1) {\n return shadow;\n }\n if (alpha <= 0) {\n return '';\n }\n\n return shadow.replace(/rgba\\(([^,]+),\\s*([^,]+),\\s*([^,]+),\\s*([^)]+)\\)/g, (_, r, g, b, a) => {\n const newAlpha = parseFloat(a) * alpha;\n return `rgba(${r}, ${g}, ${b}, ${newAlpha.toFixed(3)})`;\n });\n}\n\n/* -------------------------------------------------------------------------- */\n/* Animated Icon Slot */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Animated wrapper for icon/loading content.\n * Matches web Button icon animation: scale 0.7→1, opacity 0→1, width 0→auto\n * Uses staggered animation: opacity waits until halfway through width animation.\n */\nfunction AnimatedIconSlot({\n children,\n visible,\n iconSize,\n gap,\n}: {\n children: React.ReactNode;\n visible: boolean;\n iconSize: number;\n gap: number;\n}) {\n // Use useDerivedValue instead of useEffect + useSharedValue\n // This is the idiomatic Reanimated pattern for deriving animated values from React state\n const progress = useDerivedValue(\n () => withSpring(visible ? 1 : 0, BUTTON_SPRING_CONFIG),\n [visible],\n );\n\n const animatedStyle = useAnimatedStyle(() => {\n // Total width includes icon + gap when visible\n const totalWidth = iconSize + gap;\n const width = interpolate(progress.value, [0, 1], [0, totalWidth]);\n\n // Staggered animation: opacity starts at 50% of width animation\n // On enter: width expands first, then icon fades in\n // On exit: icon fades out first, then width collapses\n const opacity = interpolate(progress.value, [0.5, 1], [0, 1], 'clamp');\n const scale = interpolate(progress.value, [0.5, 1], [0.7, 1], 'clamp');\n\n return {\n width,\n opacity,\n transform: [{ scale }],\n overflow: 'hidden' as const,\n };\n });\n\n return <Animated.View style={animatedStyle}>{children}</Animated.View>;\n}\n\n// function LoadingIcon({ size, variant }: { size: ButtonSize, variant: ButtonVariantFlat }) {\n// const { theme } = useUnistyles();\n// const themeKey = `buttonVariant${variantToCapitalMap[variant]}IconRest` as const;\n// const iconSize = theme.components.buttonSizeLgIconRest.fontSize;\n// return <ActivityIndicator size={iconSize} color={theme.colors.text.primary} />;\n// }\n\n/* -------------------------------------------------------------------------- */\n/* Button Props */\n/* -------------------------------------------------------------------------- */\n\ninterface ButtonProps extends Omit<PressableProps, 'children' | 'disabled'> {\n /** The visual style variant of the button @default 'primary' */\n variant?: ButtonVariantFlat;\n /** The size of the button @default 'md' */\n size?: ButtonSize;\n /** The icon style variant @default 'outline' */\n iconVariant?: IconVariant;\n /** Icon displayed before the button label */\n startIcon?: IconSlotType;\n /** Icon displayed after the button label */\n endIcon?: IconSlotType;\n /** Shows a loading spinner and disables the button */\n loading?: boolean;\n /** Whether the button is disabled */\n disabled?: boolean;\n /** Button label content */\n children?: React.ReactNode;\n /**\n * Disable motion effects (scale on press, icon animations)\n * @default false\n */\n disableEffects?: boolean;\n /** Ref to the underlying View */\n ref?: Ref<View>;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Button Component */\n/* -------------------------------------------------------------------------- */\n\n/**\n * **🖲️ A button element that can be used to trigger an action**\n *\n * @description\n * A button is a fundamental component used to trigger an action or event.\n * Buttons are interactive elements that users can click, tap, or otherwise\n * engage with to submit forms, open dialogues, or perform any other interaction.\n *\n * Features animated scale effect on press and smooth icon transitions matching\n * the web UDS Button behavior.\n *\n * @category Interactive\n * @platform mobile\n *\n * @example\n * ```tsx\n * import { Button } from '@yahoo/uds-mobile/Button';\n *\n * <Button onPress={() => console.log('pressed')}>Save</Button>\n * <Button variant=\"secondary\">Cancel</Button>\n * <Button startIcon=\"Add\" variant=\"brand\">Add Item</Button>\n * <Button loading>Saving...</Button>\n * ```\n *\n * @accessibility\n * - Sets `accessibilityRole=\"button\"` automatically\n * - Announces loading state to screen readers\n * - Use `accessibilityLabel` for icon-only buttons\n *\n * @see {@link IconButton} for icon-only buttons\n * @see {@link Link} for navigation actions\n */\nconst Button = memo(function Button({\n variant = 'primary',\n size = 'md',\n iconVariant = 'outline',\n startIcon,\n endIcon,\n loading,\n disabled,\n width: _width,\n children,\n style,\n accessibilityLabel,\n accessibilityHint,\n disableEffects = false,\n onPressIn,\n onPressOut,\n ref,\n ...props\n}: ButtonProps) {\n const shouldAnimate = !disableEffects;\n\n /* --------------------------------- State ---------------------------------- */\n const [pressed, setPressed] = useState(false);\n\n buttonStyles.useVariants({ size, variant, disabled, pressed });\n\n // Get gap and icon size from current variant styles\n const buttonGap = buttonStyles.root.gap;\n const iconSize = buttonStyles.icon.fontSize;\n\n // Get animated theme for boxShadow (useAnimatedVariantColor doesn't support non-color props)\n const animatedTheme = useAnimatedTheme();\n\n /* ------------------------------- Animation -------------------------------- */\n const scale = useSharedValue<number>(SCALE_EFFECTS.none);\n\n const handlePressIn = useCallback(\n (event: Parameters<NonNullable<PressableProps['onPressIn']>>[0]) => {\n setPressed(true);\n if (shouldAnimate) {\n scale.value = withSpring(SCALE_EFFECTS.down, BUTTON_SPRING_CONFIG);\n }\n onPressIn?.(event);\n },\n [shouldAnimate, scale, onPressIn],\n );\n\n const handlePressOut = useCallback(\n (event: Parameters<NonNullable<PressableProps['onPressOut']>>[0]) => {\n setPressed(false);\n if (shouldAnimate) {\n scale.value = withSpring(SCALE_EFFECTS.none, BUTTON_SPRING_CONFIG);\n }\n onPressOut?.(event);\n },\n [shouldAnimate, scale, onPressOut],\n );\n\n /* -------------------------------- Content --------------------------------- */\n const childrenNode =\n children &&\n (isValidElement(children) ? (\n children\n ) : (\n <Text numberOfLines={1} textAlign=\"center\" style={buttonStyles.text}>\n {children}\n </Text>\n ));\n\n const a11yState = useMemo(() => ({ disabled, busy: loading }), [disabled, loading]);\n\n /* --------------------------------- Styles --------------------------------- */\n // Animate pressed state for shadow (0 = rest, 1 = pressed)\n const pressProgress = useDerivedValue(\n () => withTiming(pressed ? 1 : 0, { duration: 220, easing: Easing.bezier(0, 0, 0.2, 1) }),\n [pressed],\n );\n\n // Animate using Unistyles' variant color system + boxShadow from theme\n const animatedStyles = useAnimatedStyle(() => {\n // Get boxShadow from theme using flattened path (no camelCase conversion needed!)\n const components = animatedTheme.value.components;\n const buttonVariantPath = `button/variant/${variant}/root/pressed` as const;\n const shadowPressed = components[buttonVariantPath]?.boxShadow;\n\n return {\n transform: [{ scale: scale.value }],\n // Only animate shadow if the theme defines one for this variant\n ...(shadowPressed && {\n boxShadow: interpolateShadowAlpha(shadowPressed, pressProgress.value),\n }),\n };\n });\n\n // Determine what should be visible in start slot\n const showLoading = !!loading;\n const showStartIcon = !!startIcon && !loading;\n const showEndIcon = !!endIcon && !loading;\n\n const iconSizeToken = (buttonStyles.icon.iconSizeToken as IconSize) ?? 'sm';\n\n // Start slot: either loading spinner or start icon\n const startContent = (\n <AnimatedIconSlot visible={showLoading || showStartIcon} iconSize={iconSize} gap={buttonGap}>\n {showLoading ? (\n <ActivityIndicator size={buttonStyles.icon.fontSize} color={buttonStyles.icon.color} />\n ) : (\n <IconSlot\n icon={startIcon}\n size={iconSizeToken}\n variant={iconVariant}\n style={buttonStyles.icon}\n />\n )}\n </AnimatedIconSlot>\n );\n\n // End slot: only end icon (no loading here)\n const endContent = (\n <AnimatedIconSlot visible={showEndIcon} iconSize={iconSize} gap={buttonGap}>\n <IconSlot\n icon={endIcon}\n size={iconSizeToken}\n variant={iconVariant}\n style={buttonStyles.icon}\n />\n </AnimatedIconSlot>\n );\n\n const rootStyles = useMemo(\n () => [\n buttonStyles.root,\n animatedStyles,\n typeof style === 'function' ? style({ pressed }) : style,\n ],\n [buttonStyles.root, animatedStyles, style, pressed],\n );\n\n /* --------------------------------- Render --------------------------------- */\n return (\n <AnimatedPressable\n ref={ref}\n disabled={disabled}\n onPressIn={handlePressIn}\n onPressOut={handlePressOut}\n flexDirection=\"row\"\n alignItems=\"center\"\n justifyContent=\"center\"\n overflow=\"hidden\"\n accessibilityLabel={loading ? `${accessibilityLabel ?? ''}, loading` : accessibilityLabel}\n accessibilityHint={accessibilityHint}\n accessibilityRole=\"button\"\n accessibilityState={a11yState}\n alignContent=\"center\"\n style={rootStyles}\n {...props}\n >\n {startContent}\n {childrenNode}\n {endContent}\n </AnimatedPressable>\n );\n});\n\nButton.displayName = 'Button';\n\nexport { Button, type ButtonProps };\n"],"mappings":"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"}
1
+ {"version":3,"file":"Button.js","names":["Text"],"sources":["../../src/components/Button.tsx"],"sourcesContent":["import type { ButtonSize, ButtonVariantFlat, IconSize, IconVariant } from '@yahoo/uds-types';\nimport type { Ref } from 'react';\nimport { isValidElement, memo, useCallback, useMemo, useState } from 'react';\nimport type { View } from 'react-native';\nimport { ActivityIndicator } from 'react-native';\nimport Animated, {\n Easing,\n interpolate,\n useAnimatedStyle,\n useDerivedValue,\n useSharedValue,\n withSpring,\n withTiming,\n} from 'react-native-reanimated';\n// eslint-disable-next-line uds/no-use-unistyles -- button control height from theme size layers\nimport { useUnistyles } from 'react-native-unistyles';\nimport { useAnimatedTheme } from 'react-native-unistyles/reanimated';\n\nimport { buttonStyles } from '../../generated/styles';\nimport { BUTTON_SPRING_CONFIG, SCALE_EFFECTS } from '../motion';\nimport { getButtonControlMetrics, getButtonTextStyleForControlHeight } from './Button/buttonTheme';\nimport type { IconSlotType } from './IconSlot';\nimport { IconSlot } from './IconSlot';\nimport type { PressableProps } from './Pressable';\nimport { AnimatedPressable } from './Pressable';\nimport { Text } from './Text';\n\n/* -------------------------------------------------------------------------- */\n/* Animation Helpers */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Interpolates a boxShadow string by scaling the alpha of all colors.\n * This allows smooth fade-in/out of shadows.\n */\nfunction interpolateShadowAlpha(shadow: string | undefined, alpha: number): string {\n 'worklet';\n if (!shadow) {\n return '';\n }\n if (alpha >= 1) {\n return shadow;\n }\n if (alpha <= 0) {\n return '';\n }\n\n return shadow.replace(/rgba\\(([^,]+),\\s*([^,]+),\\s*([^,]+),\\s*([^)]+)\\)/g, (_, r, g, b, a) => {\n const newAlpha = parseFloat(a) * alpha;\n return `rgba(${r}, ${g}, ${b}, ${newAlpha.toFixed(3)})`;\n });\n}\n\n/* -------------------------------------------------------------------------- */\n/* Animated Icon Slot */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Animated wrapper for icon/loading content.\n * Matches web Button icon animation: scale 0.7→1, opacity 0→1, width 0→auto\n * Uses staggered animation: opacity waits until halfway through width animation.\n */\nfunction AnimatedIconSlot({\n children,\n visible,\n iconSize,\n gap,\n}: {\n children: React.ReactNode;\n visible: boolean;\n iconSize: number;\n gap: number;\n}) {\n // Use useDerivedValue instead of useEffect + useSharedValue\n // This is the idiomatic Reanimated pattern for deriving animated values from React state\n const progress = useDerivedValue(\n () => withSpring(visible ? 1 : 0, BUTTON_SPRING_CONFIG),\n [visible],\n );\n\n const animatedStyle = useAnimatedStyle(() => {\n // Total width includes icon + gap when visible\n const totalWidth = iconSize + gap;\n const width = interpolate(progress.value, [0, 1], [0, totalWidth]);\n\n // Staggered animation: opacity starts at 50% of width animation\n // On enter: width expands first, then icon fades in\n // On exit: icon fades out first, then width collapses\n const opacity = interpolate(progress.value, [0.5, 1], [0, 1], 'clamp');\n const scale = interpolate(progress.value, [0.5, 1], [0.7, 1], 'clamp');\n\n return {\n width,\n opacity,\n transform: [{ scale }],\n overflow: 'hidden' as const,\n };\n });\n\n return <Animated.View style={animatedStyle}>{children}</Animated.View>;\n}\n\n// function LoadingIcon({ size, variant }: { size: ButtonSize, variant: ButtonVariantFlat }) {\n// const { theme } = useUnistyles();\n// const themeKey = `buttonVariant${variantToCapitalMap[variant]}IconRest` as const;\n// const iconSize = theme.components.buttonSizeLgIconRest.fontSize;\n// return <ActivityIndicator size={iconSize} color={theme.colors.text.primary} />;\n// }\n\n/* -------------------------------------------------------------------------- */\n/* Button Props */\n/* -------------------------------------------------------------------------- */\n\ninterface ButtonProps extends Omit<PressableProps, 'children' | 'disabled'> {\n /** The visual style variant of the button @default 'primary' */\n variant?: ButtonVariantFlat;\n /** The size of the button @default 'md' */\n size?: ButtonSize;\n /** The icon style variant @default 'outline' */\n iconVariant?: IconVariant;\n /** Icon displayed before the button label */\n startIcon?: IconSlotType;\n /** Icon displayed after the button label */\n endIcon?: IconSlotType;\n /** Shows a loading spinner and disables the button */\n loading?: boolean;\n /** Whether the button is disabled */\n disabled?: boolean;\n /** Button label content */\n children?: React.ReactNode;\n /**\n * Disable motion effects (scale on press, icon animations)\n * @default false\n */\n disableEffects?: boolean;\n /** Ref to the underlying View */\n ref?: Ref<View>;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Button Component */\n/* -------------------------------------------------------------------------- */\n\n/**\n * **🖲️ A button element that can be used to trigger an action**\n *\n * @description\n * A button is a fundamental component used to trigger an action or event.\n * Buttons are interactive elements that users can click, tap, or otherwise\n * engage with to submit forms, open dialogues, or perform any other interaction.\n *\n * Features animated scale effect on press and smooth icon transitions matching\n * the web UDS Button behavior.\n *\n * @category Interactive\n * @platform mobile\n *\n * @example\n * ```tsx\n * import { Button } from '@yahoo/uds-mobile/Button';\n *\n * <Button onPress={() => console.log('pressed')}>Save</Button>\n * <Button variant=\"secondary\">Cancel</Button>\n * <Button startIcon=\"Add\" variant=\"brand\">Add Item</Button>\n * <Button loading>Saving...</Button>\n * ```\n *\n * @accessibility\n * - Sets `accessibilityRole=\"button\"` automatically\n * - Announces loading state to screen readers\n * - Use `accessibilityLabel` for icon-only buttons\n *\n * @see {@link IconButton} for icon-only buttons\n * @see {@link Link} for navigation actions\n */\nconst Button = memo(function Button({\n variant = 'primary',\n size = 'md',\n iconVariant = 'outline',\n startIcon,\n endIcon,\n loading,\n disabled,\n width: _width,\n children,\n style,\n accessibilityLabel,\n accessibilityHint,\n disableEffects = false,\n onPressIn,\n onPressOut,\n ref,\n ...props\n}: ButtonProps) {\n const shouldAnimate = !disableEffects;\n\n const { theme } = useUnistyles();\n const { controlHeight, contentLineHeight } = useMemo(\n () => getButtonControlMetrics(theme, size),\n [theme, size],\n );\n\n /* --------------------------------- State ---------------------------------- */\n const [pressed, setPressed] = useState(false);\n\n buttonStyles.useVariants({ size, variant, disabled, pressed });\n\n // Get gap and icon size from current variant styles\n const buttonGap = buttonStyles.root.gap;\n const iconSize = buttonStyles.icon.fontSize;\n\n // Get animated theme for boxShadow (useAnimatedVariantColor doesn't support non-color props)\n const animatedTheme = useAnimatedTheme();\n\n /* ------------------------------- Animation -------------------------------- */\n const scale = useSharedValue<number>(SCALE_EFFECTS.none);\n\n const handlePressIn = useCallback(\n (event: Parameters<NonNullable<PressableProps['onPressIn']>>[0]) => {\n setPressed(true);\n if (shouldAnimate) {\n scale.value = withSpring(SCALE_EFFECTS.down, BUTTON_SPRING_CONFIG);\n }\n onPressIn?.(event);\n },\n [shouldAnimate, scale, onPressIn],\n );\n\n const handlePressOut = useCallback(\n (event: Parameters<NonNullable<PressableProps['onPressOut']>>[0]) => {\n setPressed(false);\n if (shouldAnimate) {\n scale.value = withSpring(SCALE_EFFECTS.none, BUTTON_SPRING_CONFIG);\n }\n onPressOut?.(event);\n },\n [shouldAnimate, scale, onPressOut],\n );\n\n /* -------------------------------- Content --------------------------------- */\n const childrenNode =\n children &&\n (isValidElement(children) ? (\n children\n ) : (\n <Text\n numberOfLines={1}\n textAlign=\"center\"\n style={getButtonTextStyleForControlHeight(buttonStyles.text, contentLineHeight)}\n >\n {children}\n </Text>\n ));\n\n const a11yState = useMemo(() => ({ disabled, busy: loading }), [disabled, loading]);\n\n /* --------------------------------- Styles --------------------------------- */\n // Animate pressed state for shadow (0 = rest, 1 = pressed)\n const pressProgress = useDerivedValue(\n () => withTiming(pressed ? 1 : 0, { duration: 220, easing: Easing.bezier(0, 0, 0.2, 1) }),\n [pressed],\n );\n\n // Animate using Unistyles' variant color system + boxShadow from theme\n const animatedStyles = useAnimatedStyle(() => {\n // Get boxShadow from theme using flattened path (no camelCase conversion needed!)\n const components = animatedTheme.value.components;\n const buttonVariantPath = `button/variant/${variant}/root/pressed` as const;\n const shadowPressed = components[buttonVariantPath]?.boxShadow;\n\n return {\n transform: [{ scale: scale.value }],\n // Only animate shadow if the theme defines one for this variant\n ...(shadowPressed && {\n boxShadow: interpolateShadowAlpha(shadowPressed, pressProgress.value),\n }),\n };\n });\n\n // Determine what should be visible in start slot\n const showLoading = !!loading;\n const showStartIcon = !!startIcon && !loading;\n const showEndIcon = !!endIcon && !loading;\n\n const iconSizeToken = (buttonStyles.icon.iconSizeToken as IconSize) ?? 'sm';\n\n // Start slot: either loading spinner or start icon\n const startContent = (\n <AnimatedIconSlot visible={showLoading || showStartIcon} iconSize={iconSize} gap={buttonGap}>\n {showLoading ? (\n <ActivityIndicator size={buttonStyles.icon.fontSize} color={buttonStyles.icon.color} />\n ) : (\n <IconSlot\n icon={startIcon}\n size={iconSizeToken}\n variant={iconVariant}\n style={buttonStyles.icon}\n />\n )}\n </AnimatedIconSlot>\n );\n\n // End slot: only end icon (no loading here)\n const endContent = (\n <AnimatedIconSlot visible={showEndIcon} iconSize={iconSize} gap={buttonGap}>\n <IconSlot\n icon={endIcon}\n size={iconSizeToken}\n variant={iconVariant}\n style={buttonStyles.icon}\n />\n </AnimatedIconSlot>\n );\n\n const rootStyles = useMemo(\n () => [\n buttonStyles.root,\n { height: controlHeight },\n animatedStyles,\n typeof style === 'function' ? style({ pressed }) : style,\n ],\n [buttonStyles.root, controlHeight, animatedStyles, style, pressed],\n );\n\n /* --------------------------------- Render --------------------------------- */\n return (\n <AnimatedPressable\n ref={ref}\n disabled={disabled}\n onPressIn={handlePressIn}\n onPressOut={handlePressOut}\n flexDirection=\"row\"\n alignItems=\"center\"\n justifyContent=\"center\"\n overflow=\"hidden\"\n accessibilityLabel={loading ? `${accessibilityLabel ?? ''}, loading` : accessibilityLabel}\n accessibilityHint={accessibilityHint}\n accessibilityRole=\"button\"\n accessibilityState={a11yState}\n alignContent=\"center\"\n style={rootStyles}\n {...props}\n >\n {startContent}\n {childrenNode}\n {endContent}\n </AnimatedPressable>\n );\n});\n\nButton.displayName = 'Button';\n\nexport { Button, type ButtonProps };\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAmCA,SAAS,uBAAuB,QAA4B,OAAuB;AACjF;CACA,IAAI,CAAC,QACH,OAAO;CAET,IAAI,SAAS,GACX,OAAO;CAET,IAAI,SAAS,GACX,OAAO;CAGT,OAAO,OAAO,QAAQ,sDAAsD,GAAG,GAAG,GAAG,GAAG,MAAM;EAE5F,OAAO,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,KADZ,WAAW,EAAE,GAAG,OACS,QAAQ,EAAE,CAAC;GACrD;;;;;;;AAYJ,SAAS,iBAAiB,EACxB,UACA,SACA,UACA,OAMC;CAGD,MAAM,WAAW,sBACT,WAAW,UAAU,IAAI,GAAG,qBAAqB,EACvD,CAAC,QAAQ,CACV;CAED,MAAM,gBAAgB,uBAAuB;EAE3C,MAAM,aAAa,WAAW;EAS9B,OAAO;GACL,OATY,YAAY,SAAS,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,WAAW,CAS1D;GACL,SALc,YAAY,SAAS,OAAO,CAAC,IAAK,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,QAKrD;GACP,WAAW,CAAC,EAAE,OALF,YAAY,SAAS,OAAO,CAAC,IAAK,EAAE,EAAE,CAAC,IAAK,EAAE,EAAE,QAKzC,EAAE,CAAC;GACtB,UAAU;GACX;GACD;CAEF,OAAO,oBAAC,SAAS,MAAV;EAAe,OAAO;EAAgB;EAAyB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4ExE,MAAM,SAAS,KAAK,SAAS,OAAO,EAClC,UAAU,WACV,OAAO,MACP,cAAc,WACd,WACA,SACA,SACA,UACA,OAAO,QACP,UACA,OACA,oBACA,mBACA,iBAAiB,OACjB,WACA,YACA,KACA,GAAG,SACW;CACd,MAAM,gBAAgB,CAAC;CAEvB,MAAM,EAAE,UAAU,cAAc;CAChC,MAAM,EAAE,eAAe,sBAAsB,cACrC,wBAAwB,OAAO,KAAK,EAC1C,CAAC,OAAO,KAAK,CACd;CAGD,MAAM,CAAC,SAAS,cAAc,SAAS,MAAM;CAE7C,aAAa,YAAY;EAAE;EAAM;EAAS;EAAU;EAAS,CAAC;CAG9D,MAAM,YAAY,aAAa,KAAK;CACpC,MAAM,WAAW,aAAa,KAAK;CAGnC,MAAM,gBAAgB,kBAAkB;CAGxC,MAAM,QAAQ,eAAuB,cAAc,KAAK;CAExD,MAAM,gBAAgB,aACnB,UAAmE;EAClE,WAAW,KAAK;EAChB,IAAI,eACF,MAAM,QAAQ,WAAW,cAAc,MAAM,qBAAqB;EAEpE,YAAY,MAAM;IAEpB;EAAC;EAAe;EAAO;EAAU,CAClC;CAED,MAAM,iBAAiB,aACpB,UAAoE;EACnE,WAAW,MAAM;EACjB,IAAI,eACF,MAAM,QAAQ,WAAW,cAAc,MAAM,qBAAqB;EAEpE,aAAa,MAAM;IAErB;EAAC;EAAe;EAAO;EAAW,CACnC;CAGD,MAAM,eACJ,aACC,eAAe,SAAS,GACvB,WAEA,oBAACA,QAAD;EACE,eAAe;EACf,WAAU;EACV,OAAO,mCAAmC,aAAa,MAAM,kBAAkB;EAE9E;EACI,CAAA;CAGX,MAAM,YAAY,eAAe;EAAE;EAAU,MAAM;EAAS,GAAG,CAAC,UAAU,QAAQ,CAAC;CAInF,MAAM,gBAAgB,sBACd,WAAW,UAAU,IAAI,GAAG;EAAE,UAAU;EAAK,QAAQ,OAAO,OAAO,GAAG,GAAG,IAAK,EAAE;EAAE,CAAC,EACzF,CAAC,QAAQ,CACV;CAGD,MAAM,iBAAiB,uBAAuB;EAI5C,MAAM,gBAFa,cAAc,MAAM,WAEN,kBADW,QAAQ,iBACC;EAErD,OAAO;GACL,WAAW,CAAC,EAAE,OAAO,MAAM,OAAO,CAAC;GAEnC,GAAI,iBAAiB,EACnB,WAAW,uBAAuB,eAAe,cAAc,MAAM,EACtE;GACF;GACD;CAGF,MAAM,cAAc,CAAC,CAAC;CACtB,MAAM,gBAAgB,CAAC,CAAC,aAAa,CAAC;CACtC,MAAM,cAAc,CAAC,CAAC,WAAW,CAAC;CAElC,MAAM,gBAAiB,aAAa,KAAK,iBAA8B;CAGvE,MAAM,eACJ,oBAAC,kBAAD;EAAkB,SAAS,eAAe;EAAyB;EAAU,KAAK;YAC/E,cACC,oBAAC,mBAAD;GAAmB,MAAM,aAAa,KAAK;GAAU,OAAO,aAAa,KAAK;GAAS,CAAA,GAEvF,oBAAC,UAAD;GACE,MAAM;GACN,MAAM;GACN,SAAS;GACT,OAAO,aAAa;GACpB,CAAA;EAEa,CAAA;CAIrB,MAAM,aACJ,oBAAC,kBAAD;EAAkB,SAAS;EAAuB;EAAU,KAAK;YAC/D,oBAAC,UAAD;GACE,MAAM;GACN,MAAM;GACN,SAAS;GACT,OAAO,aAAa;GACpB,CAAA;EACe,CAAA;CAGrB,MAAM,aAAa,cACX;EACJ,aAAa;EACb,EAAE,QAAQ,eAAe;EACzB;EACA,OAAO,UAAU,aAAa,MAAM,EAAE,SAAS,CAAC,GAAG;EACpD,EACD;EAAC,aAAa;EAAM;EAAe;EAAgB;EAAO;EAAQ,CACnE;CAGD,OACE,qBAAC,mBAAD;EACO;EACK;EACV,WAAW;EACX,YAAY;EACZ,eAAc;EACd,YAAW;EACX,gBAAe;EACf,UAAS;EACT,oBAAoB,UAAU,GAAG,sBAAsB,GAAG,aAAa;EACpD;EACnB,mBAAkB;EAClB,oBAAoB;EACpB,cAAa;EACb,OAAO;EACP,GAAI;YAfN;GAiBG;GACA;GACA;GACiB;;EAEtB;AAEF,OAAO,cAAc"}
@@ -4,11 +4,13 @@ require("../_virtual/_rolldown/runtime.cjs");
4
4
  const require_index = require("../motion-tokens/dist/index.cjs");
5
5
  const require_motion = require("../motion.cjs");
6
6
  const require_components_Icon = require("./Icon.cjs");
7
+ const require_components_Button_buttonTheme = require("./Button/buttonTheme.cjs");
7
8
  const require_components_Pressable = require("./Pressable.cjs");
8
9
  let react = require("react");
9
10
  let react_native = require("react-native");
10
11
  let react_jsx_runtime = require("react/jsx-runtime");
11
12
  let generated_styles = require("../../generated/styles");
13
+ let react_native_unistyles = require("react-native-unistyles");
12
14
  let react_native_reanimated = require("react-native-reanimated");
13
15
  let react_native_unistyles_reanimated = require("react-native-unistyles/reanimated");
14
16
  //#region src/components/IconButton.tsx
@@ -57,17 +59,24 @@ function interpolateShadowAlpha(shadow, alpha) {
57
59
  const IconButton = (0, react.memo)(function IconButton({ name, variant = "primary", size = "md", iconVariant = "outline", iconColor, loading, disabled, style, accessibilityLabel, accessibilityHint, disableEffects = false, onPressIn, onPressOut, ref, ...props }) {
58
60
  const isDisabled = disabled || loading;
59
61
  const shouldAnimate = !disableEffects && !isDisabled;
62
+ const shouldAnimateVariantColors = react_native.Platform.OS !== "web";
63
+ const { theme } = (0, react_native_unistyles.useUnistyles)();
64
+ const { controlHeight } = (0, react.useMemo)(() => require_components_Button_buttonTheme.getIconButtonControlMetrics(theme, size), [theme, size]);
65
+ const matchedControlDimensions = controlHeight > 0 ? {
66
+ height: controlHeight,
67
+ width: controlHeight
68
+ } : void 0;
60
69
  const [pressed, setPressed] = (0, react.useState)(false);
61
- generated_styles.iconButtonStyles.useVariants({ size });
62
- generated_styles.buttonStyles.useVariants({
70
+ const resolvedIconButtonStyles = generated_styles.iconButtonStyles.useVariants({ size }) ?? generated_styles.iconButtonStyles;
71
+ const resolvedButtonStyles = generated_styles.buttonStyles.useVariants({
63
72
  variant,
64
73
  disabled: isDisabled,
65
74
  pressed
66
- });
67
- generated_styles.styles.useVariants({ color: iconColor });
68
- const resolvedIconColor = iconColor ? generated_styles.styles.foundation.color : void 0;
69
- const backgroundColor = (0, react_native_unistyles_reanimated.useAnimatedVariantColor)(generated_styles.buttonStyles.root, "backgroundColor");
70
- const borderColor = (0, react_native_unistyles_reanimated.useAnimatedVariantColor)(generated_styles.buttonStyles.root, "borderColor");
75
+ }) ?? generated_styles.buttonStyles;
76
+ const resolvedFoundationStyles = generated_styles.styles.useVariants({ color: iconColor }) ?? generated_styles.styles;
77
+ const resolvedIconColor = iconColor ? resolvedFoundationStyles.foundation.color : void 0;
78
+ const backgroundColor = (0, react_native_unistyles_reanimated.useAnimatedVariantColor)(resolvedButtonStyles.root, "backgroundColor");
79
+ const borderColor = (0, react_native_unistyles_reanimated.useAnimatedVariantColor)(resolvedButtonStyles.root, "borderColor");
71
80
  const animatedTheme = (0, react_native_unistyles_reanimated.useAnimatedTheme)();
72
81
  const scale = (0, react_native_reanimated.useSharedValue)(require_index.SCALE_EFFECTS.none);
73
82
  const handlePressIn = (0, react.useCallback)((event) => {
@@ -100,14 +109,16 @@ const IconButton = (0, react.memo)(function IconButton({ name, variant = "primar
100
109
  const shadowPressed = animatedTheme.value.components[`button/variant/${variant}/root/pressed`]?.boxShadow;
101
110
  return {
102
111
  transform: [{ scale: scale.value }],
103
- backgroundColor: (0, react_native_reanimated.withTiming)(backgroundColor.value, {
104
- duration: 220,
105
- easing: react_native_reanimated.Easing.bezier(0, 0, .2, 1)
106
- }),
107
- borderColor: (0, react_native_reanimated.withTiming)(borderColor.value, {
108
- duration: 220,
109
- easing: react_native_reanimated.Easing.bezier(0, 0, .2, 1)
110
- }),
112
+ ...shouldAnimateVariantColors && {
113
+ backgroundColor: (0, react_native_reanimated.withTiming)(backgroundColor.value, {
114
+ duration: 220,
115
+ easing: react_native_reanimated.Easing.bezier(0, 0, .2, 1)
116
+ }),
117
+ borderColor: (0, react_native_reanimated.withTiming)(borderColor.value, {
118
+ duration: 220,
119
+ easing: react_native_reanimated.Easing.bezier(0, 0, .2, 1)
120
+ })
121
+ },
111
122
  ...shadowPressed && { boxShadow: interpolateShadowAlpha(shadowPressed, pressProgress.value) }
112
123
  };
113
124
  });
@@ -125,20 +136,21 @@ const IconButton = (0, react.memo)(function IconButton({ name, variant = "primar
125
136
  accessibilityRole: "button",
126
137
  accessibilityState: a11yState,
127
138
  style: [
128
- generated_styles.iconButtonStyles.root,
129
- generated_styles.buttonStyles.root,
130
- generated_styles.styles.foundation,
139
+ resolvedIconButtonStyles.root,
140
+ resolvedButtonStyles.root,
141
+ matchedControlDimensions,
142
+ resolvedFoundationStyles.foundation,
131
143
  animatedRootStyle,
132
144
  typeof style === "function" ? style({ pressed }) : style
133
145
  ],
134
146
  ...props,
135
147
  children: loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.ActivityIndicator, {
136
- size: generated_styles.iconButtonStyles.icon.fontSize,
137
- color: resolvedIconColor ?? generated_styles.buttonStyles.icon.color
148
+ size: resolvedIconButtonStyles.icon.fontSize,
149
+ color: resolvedIconColor ?? resolvedButtonStyles.icon.color
138
150
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_Icon.Icon, {
139
151
  name,
140
152
  variant: iconVariant,
141
- style: [generated_styles.iconButtonStyles.icon, generated_styles.buttonStyles.icon],
153
+ style: [resolvedIconButtonStyles.icon, resolvedButtonStyles.icon],
142
154
  dangerouslySetColor: resolvedIconColor
143
155
  })
144
156
  });