@yahoo/uds-mobile 2.21.0 → 2.21.2
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/CONTRIBUTING.md +8 -4
- package/README.md +2 -0
- package/dist/bin/automated-config/dist/generated/generatedConfigs.mjs +67 -51
- package/dist/components/Button/buttonTheme.cjs +69 -0
- package/dist/components/Button/buttonTheme.d.cts +24 -0
- package/dist/components/Button/buttonTheme.d.cts.map +1 -0
- package/dist/components/Button/buttonTheme.d.ts +24 -0
- package/dist/components/Button/buttonTheme.d.ts.map +1 -0
- package/dist/components/Button/buttonTheme.js +68 -0
- package/dist/components/Button/buttonTheme.js.map +1 -0
- package/dist/components/Button.cjs +7 -1
- package/dist/components/Button.d.cts.map +1 -1
- package/dist/components/Button.d.ts.map +1 -1
- package/dist/components/Button.js +7 -1
- package/dist/components/Button.js.map +1 -1
- package/dist/components/IconButton.cjs +9 -0
- package/dist/components/IconButton.d.cts.map +1 -1
- package/dist/components/IconButton.d.ts.map +1 -1
- package/dist/components/IconButton.js +9 -0
- package/dist/components/IconButton.js.map +1 -1
- package/fonts/index.cjs +228 -219
- package/fonts/index.mjs +228 -219
- package/generated/styles.d.ts +1 -1
- package/package.json +1 -1
package/CONTRIBUTING.md
CHANGED
|
@@ -563,7 +563,7 @@ The generation pipeline runs via `bun scripts/generate.ts` (or `bun run generate
|
|
|
563
563
|
│ generate.ts │
|
|
564
564
|
│ 1. generateTypes() → generated/unistyles.d.ts │
|
|
565
565
|
│ 2. generateStyles() → generated/styles.ts │
|
|
566
|
-
│ 3. generateFonts() → fonts/index.{cjs,mjs}
|
|
566
|
+
│ 3. generateFonts() → fonts/index.{cjs,mjs} + fonts/uds-icons.ttf│
|
|
567
567
|
└─────────────────────────────────────────────────────────────────────┘
|
|
568
568
|
```
|
|
569
569
|
|
|
@@ -603,9 +603,13 @@ The most complex generation step:
|
|
|
603
603
|
|
|
604
604
|
### Step 3: generateFonts.ts
|
|
605
605
|
|
|
606
|
-
1.
|
|
607
|
-
2.
|
|
608
|
-
3.
|
|
606
|
+
1. Syncs `fonts/uds-icons.ttf` from `packages/icons/generated/font/uds-icons.ttf`
|
|
607
|
+
2. Scans `fonts/` directory for `.otf` and `.ttf` files, skipping the vendored icon font
|
|
608
|
+
3. Uses `fontkit` to extract PostScript names from font files
|
|
609
|
+
4. Generates CommonJS and ESM manifests for Expo compatibility
|
|
610
|
+
5. Resolves the icon font from `@yahoo/uds-icons/generated/font/uds-icons.ttf`, matching the package that provides `glyphMap`
|
|
611
|
+
|
|
612
|
+
The synced `fonts/uds-icons.ttf` is a build artifact and is gitignored — it is regenerated from `@yahoo/uds-icons` on every build and before publish (`ci:publish`), so it can never drift from the glyph map. Use `bun run sync:icon-font` to refresh it manually.
|
|
609
613
|
|
|
610
614
|
## Theme Generation
|
|
611
615
|
|
package/README.md
CHANGED
|
@@ -68,6 +68,8 @@ expo install react-native-unistyles react-native-reanimated react-native-svg rea
|
|
|
68
68
|
expo install expo-font
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
+
If your app uses `Icon` or UDS icon font loading, install `@yahoo/uds-icons` with your package manager.
|
|
72
|
+
|
|
71
73
|
Using `expo install` ensures you get versions compatible with your Expo SDK. If you encounter version conflicts, check the `peerDependencies` in the package's `package.json` for the exact version ranges required.
|
|
72
74
|
|
|
73
75
|
### Expo Configuration
|
|
@@ -18617,7 +18617,7 @@ const IconButtonConfig = {
|
|
|
18617
18617
|
label: "icon",
|
|
18618
18618
|
properties: { size: {
|
|
18619
18619
|
defaults: {
|
|
18620
|
-
lg: "
|
|
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: {
|
|
18640
|
-
|
|
18641
|
-
|
|
18642
|
-
|
|
18643
|
-
|
|
18644
|
-
|
|
18645
|
-
|
|
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
|
-
|
|
18648
|
-
|
|
18649
|
-
|
|
18650
|
-
|
|
18651
|
-
|
|
18652
|
-
|
|
18653
|
-
|
|
18654
|
-
|
|
18655
|
-
"
|
|
18656
|
-
"
|
|
18657
|
-
"
|
|
18658
|
-
|
|
18659
|
-
|
|
18660
|
-
|
|
18661
|
-
|
|
18662
|
-
|
|
18663
|
-
|
|
18664
|
-
|
|
18665
|
-
|
|
18666
|
-
|
|
18667
|
-
|
|
18668
|
-
|
|
18669
|
-
|
|
18670
|
-
|
|
18671
|
-
|
|
18672
|
-
|
|
18673
|
-
|
|
18674
|
-
|
|
18675
|
-
|
|
18676
|
-
|
|
18677
|
-
|
|
18678
|
-
|
|
18679
|
-
|
|
18680
|
-
|
|
18681
|
-
|
|
18682
|
-
|
|
18683
|
-
|
|
18684
|
-
|
|
18685
|
-
|
|
18686
|
-
|
|
18687
|
-
|
|
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":";;;;;;;;;
|
|
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":";;;;;;;;;
|
|
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":"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"}
|
|
@@ -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,6 +59,12 @@ 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 { theme } = (0, react_native_unistyles.useUnistyles)();
|
|
63
|
+
const { controlHeight } = (0, react.useMemo)(() => require_components_Button_buttonTheme.getIconButtonControlMetrics(theme, size), [theme, size]);
|
|
64
|
+
const matchedControlDimensions = controlHeight > 0 ? {
|
|
65
|
+
height: controlHeight,
|
|
66
|
+
width: controlHeight
|
|
67
|
+
} : void 0;
|
|
60
68
|
const [pressed, setPressed] = (0, react.useState)(false);
|
|
61
69
|
generated_styles.iconButtonStyles.useVariants({ size });
|
|
62
70
|
generated_styles.buttonStyles.useVariants({
|
|
@@ -127,6 +135,7 @@ const IconButton = (0, react.memo)(function IconButton({ name, variant = "primar
|
|
|
127
135
|
style: [
|
|
128
136
|
generated_styles.iconButtonStyles.root,
|
|
129
137
|
generated_styles.buttonStyles.root,
|
|
138
|
+
matchedControlDimensions,
|
|
130
139
|
generated_styles.styles.foundation,
|
|
131
140
|
animatedRootStyle,
|
|
132
141
|
typeof style === "function" ? style({ pressed }) : style
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IconButton.d.cts","names":[],"sources":["../../src/components/IconButton.tsx"],"mappings":";;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"IconButton.d.cts","names":[],"sources":["../../src/components/IconButton.tsx"],"mappings":";;;;;;;;;;UAoDU,eAAA,SAAwB,IAAA,CAAK,gBAAA;;EAErC,IAAA,EAAM,QAAA;EAFE;EAIR,OAAA,GAAU,iBAAA;;EAEV,IAAA,GAAO,cAAA;EAJD;EAMN,WAAA,GAAc,WAAA;EAFP;EAIP,SAAA,GAAY,UAAA;EAAA;EAEZ,OAAA;EAOM;;;;EAFN,cAAA;EAjBqC;EAmBrC,GAAA,GAAM,GAAA,CAAI,IAAA;AAAA;;;;;;;;;;;;;;;;AAAI;;;;;;;;;;;;;;;;;;cAwCV,UAAA,EAAU,OAAA,CAAA,oBAAA,CAAA,eAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IconButton.d.ts","names":[],"sources":["../../src/components/IconButton.tsx"],"mappings":";;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"IconButton.d.ts","names":[],"sources":["../../src/components/IconButton.tsx"],"mappings":";;;;;;;;;;UAoDU,eAAA,SAAwB,IAAA,CAAK,gBAAA;;EAErC,IAAA,EAAM,QAAA;EAFE;EAIR,OAAA,GAAU,iBAAA;;EAEV,IAAA,GAAO,cAAA;EAJD;EAMN,WAAA,GAAc,WAAA;EAFP;EAIP,SAAA,GAAY,UAAA;EAAA;EAEZ,OAAA;EAOM;;;;EAFN,cAAA;EAjBqC;EAmBrC,GAAA,GAAM,GAAA,CAAI,IAAA;AAAA;;;;;;;;;;;;;;;;AAAI;;;;;;;;;;;;;;;;;;cAwCV,UAAA,EAAU,OAAA,CAAA,oBAAA,CAAA,eAAA"}
|