@oxyhq/bloom 0.3.5 → 0.3.6
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/lib/commonjs/button/Button.js +68 -30
- package/lib/commonjs/button/Button.js.map +1 -1
- package/lib/module/button/Button.js +70 -32
- package/lib/module/button/Button.js.map +1 -1
- package/lib/typescript/commonjs/button/Button.d.ts.map +1 -1
- package/lib/typescript/commonjs/button/types.d.ts +11 -0
- package/lib/typescript/commonjs/button/types.d.ts.map +1 -1
- package/lib/typescript/module/button/Button.d.ts.map +1 -1
- package/lib/typescript/module/button/types.d.ts +11 -0
- package/lib/typescript/module/button/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/Button.test.tsx +35 -0
- package/src/button/Button.tsx +80 -30
- package/src/button/types.ts +12 -0
|
@@ -48,6 +48,8 @@ const ButtonComponent = ({
|
|
|
48
48
|
textStyle,
|
|
49
49
|
icon,
|
|
50
50
|
iconPosition = 'left',
|
|
51
|
+
loading = false,
|
|
52
|
+
loadingColor,
|
|
51
53
|
accessibilityLabel,
|
|
52
54
|
accessibilityHint,
|
|
53
55
|
hitSlop,
|
|
@@ -57,11 +59,12 @@ const ButtonComponent = ({
|
|
|
57
59
|
}) => {
|
|
58
60
|
const theme = (0, _useTheme.useTheme)();
|
|
59
61
|
const hasScaleFeedback = SCALE_VARIANTS.has(variant);
|
|
62
|
+
const isInteractionBlocked = disabled || loading;
|
|
60
63
|
const {
|
|
61
64
|
scaleAnim,
|
|
62
65
|
onPressIn,
|
|
63
66
|
onPressOut
|
|
64
|
-
} = (0, _usePressAnimation.usePressAnimation)(hasScaleFeedback ? PRESS_SCALE : undefined);
|
|
67
|
+
} = (0, _usePressAnimation.usePressAnimation)(hasScaleFeedback && !isInteractionBlocked ? PRESS_SCALE : undefined);
|
|
65
68
|
const baseStyles = (0, _react.useMemo)(() => {
|
|
66
69
|
const sizeConfig = SIZE_CONFIG[size];
|
|
67
70
|
const styles = {
|
|
@@ -108,68 +111,103 @@ const ButtonComponent = ({
|
|
|
108
111
|
}
|
|
109
112
|
return styles;
|
|
110
113
|
}, [variant, size, theme]);
|
|
111
|
-
const
|
|
112
|
-
const sizeConfig = SIZE_CONFIG[size];
|
|
113
|
-
const styles = {
|
|
114
|
-
fontSize: sizeConfig.fontSize,
|
|
115
|
-
fontWeight: _reactNative.Platform.OS === 'web' ? 'bold' : '600'
|
|
116
|
-
};
|
|
114
|
+
const resolvedTextColor = (0, _react.useMemo)(() => {
|
|
117
115
|
switch (variant) {
|
|
118
116
|
case 'primary':
|
|
119
|
-
|
|
120
|
-
break;
|
|
117
|
+
return theme.colors.card;
|
|
121
118
|
case 'secondary':
|
|
122
|
-
|
|
123
|
-
break;
|
|
119
|
+
return theme.colors.text;
|
|
124
120
|
case 'inverse':
|
|
125
|
-
|
|
126
|
-
break;
|
|
121
|
+
return '#000000';
|
|
127
122
|
case 'ghost':
|
|
128
123
|
case 'text':
|
|
129
|
-
|
|
130
|
-
|
|
124
|
+
return theme.colors.primary;
|
|
125
|
+
case 'icon':
|
|
126
|
+
default:
|
|
127
|
+
return theme.colors.text;
|
|
131
128
|
}
|
|
132
|
-
|
|
133
|
-
|
|
129
|
+
}, [variant, theme]);
|
|
130
|
+
const computedTextStyle = (0, _react.useMemo)(() => {
|
|
131
|
+
const sizeConfig = SIZE_CONFIG[size];
|
|
132
|
+
return {
|
|
133
|
+
fontSize: sizeConfig.fontSize,
|
|
134
|
+
fontWeight: _reactNative.Platform.OS === 'web' ? 'bold' : '600',
|
|
135
|
+
color: resolvedTextColor
|
|
136
|
+
};
|
|
137
|
+
}, [size, resolvedTextColor]);
|
|
134
138
|
const defaultHitSlop = variant === 'icon' ? ICON_HIT_SLOP : undefined;
|
|
135
139
|
const resolvedActiveOpacity = activeOpacity ?? (variant === 'icon' ? 0.7 : 0.8);
|
|
136
140
|
const resolvedClassName = className ?? (variant === 'icon' ? 'bg-background border border-border' : undefined);
|
|
141
|
+
const content = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
142
|
+
children: [iconPosition === 'left' && icon, children != null && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
143
|
+
style: [computedTextStyle, textStyle],
|
|
144
|
+
children: children
|
|
145
|
+
}), iconPosition === 'right' && icon]
|
|
146
|
+
});
|
|
137
147
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
|
|
138
148
|
style: hasScaleFeedback ? {
|
|
139
149
|
transform: [{
|
|
140
150
|
scale: scaleAnim
|
|
141
151
|
}]
|
|
142
152
|
} : undefined,
|
|
143
|
-
children: /*#__PURE__*/(0, _jsxRuntime.
|
|
153
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
|
|
144
154
|
...(resolvedClassName ? {
|
|
145
155
|
className: resolvedClassName
|
|
146
156
|
} : {}),
|
|
147
157
|
style: ({
|
|
148
158
|
pressed
|
|
149
|
-
}) => [baseStyles, disabled && {
|
|
159
|
+
}) => [baseStyles, disabled && !loading && {
|
|
150
160
|
opacity: 0.5
|
|
151
|
-
}, pressed && !hasScaleFeedback && {
|
|
161
|
+
}, pressed && !hasScaleFeedback && !isInteractionBlocked && {
|
|
152
162
|
opacity: resolvedActiveOpacity
|
|
153
163
|
}, style],
|
|
154
|
-
onPress: onPress,
|
|
155
|
-
onPressIn: onPressIn,
|
|
156
|
-
onPressOut: onPressOut,
|
|
157
|
-
disabled:
|
|
164
|
+
onPress: isInteractionBlocked ? undefined : onPress,
|
|
165
|
+
onPressIn: isInteractionBlocked ? undefined : onPressIn,
|
|
166
|
+
onPressOut: isInteractionBlocked ? undefined : onPressOut,
|
|
167
|
+
disabled: isInteractionBlocked,
|
|
158
168
|
hitSlop: hitSlop ?? defaultHitSlop,
|
|
159
169
|
accessibilityLabel: accessibilityLabel,
|
|
160
170
|
accessibilityHint: accessibilityHint,
|
|
161
171
|
accessibilityRole: "button",
|
|
162
|
-
accessibilityState: {
|
|
163
|
-
disabled
|
|
172
|
+
accessibilityState: loading ? {
|
|
173
|
+
disabled: isInteractionBlocked,
|
|
174
|
+
busy: true
|
|
175
|
+
} : {
|
|
176
|
+
disabled: isInteractionBlocked
|
|
164
177
|
},
|
|
165
178
|
testID: testID,
|
|
166
|
-
children:
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
179
|
+
children: loading ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
180
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
181
|
+
style: styles.loadingHiddenContent,
|
|
182
|
+
importantForAccessibility: "no-hide-descendants",
|
|
183
|
+
accessibilityElementsHidden: true,
|
|
184
|
+
children: content
|
|
185
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
186
|
+
style: styles.loadingOverlay,
|
|
187
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
|
|
188
|
+
size: "small",
|
|
189
|
+
color: loadingColor ?? resolvedTextColor
|
|
190
|
+
})
|
|
191
|
+
})]
|
|
192
|
+
}) : content
|
|
170
193
|
})
|
|
171
194
|
});
|
|
172
195
|
};
|
|
196
|
+
const styles = _reactNative.StyleSheet.create({
|
|
197
|
+
loadingHiddenContent: {
|
|
198
|
+
flexDirection: 'row',
|
|
199
|
+
alignItems: 'center',
|
|
200
|
+
justifyContent: 'center',
|
|
201
|
+
opacity: 0,
|
|
202
|
+
pointerEvents: 'none'
|
|
203
|
+
},
|
|
204
|
+
loadingOverlay: {
|
|
205
|
+
..._reactNative.StyleSheet.absoluteFillObject,
|
|
206
|
+
alignItems: 'center',
|
|
207
|
+
justifyContent: 'center',
|
|
208
|
+
pointerEvents: 'none'
|
|
209
|
+
}
|
|
210
|
+
});
|
|
173
211
|
const Button = exports.Button = /*#__PURE__*/(0, _react.memo)(ButtonComponent);
|
|
174
212
|
Button.displayName = 'Button';
|
|
175
213
|
const PrimaryButton = exports.PrimaryButton = /*#__PURE__*/(0, _react.memo)(props => /*#__PURE__*/(0, _jsxRuntime.jsx)(Button, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_useTheme","_usePressAnimation","_jsxRuntime","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","SIZE_CONFIG","small","paddingVertical","paddingHorizontal","fontSize","minHeight","medium","large","ICON_HIT_SLOP","top","bottom","left","right","PRESS_SCALE","SCALE_VARIANTS","Set","ButtonComponent","onPress","children","disabled","variant","size","style","textStyle","icon","iconPosition","accessibilityLabel","accessibilityHint","hitSlop","activeOpacity","testID","className","theme","useTheme","hasScaleFeedback","scaleAnim","onPressIn","onPressOut","usePressAnimation","undefined","baseStyles","useMemo","sizeConfig","styles","alignItems","justifyContent","flexDirection","overflow","backgroundColor","colors","primary","borderRadius","borderWidth","borderColor","border","padding","width","height","computedTextStyle","fontWeight","Platform","OS","color","
|
|
1
|
+
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_useTheme","_usePressAnimation","_jsxRuntime","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","SIZE_CONFIG","small","paddingVertical","paddingHorizontal","fontSize","minHeight","medium","large","ICON_HIT_SLOP","top","bottom","left","right","PRESS_SCALE","SCALE_VARIANTS","Set","ButtonComponent","onPress","children","disabled","variant","size","style","textStyle","icon","iconPosition","loading","loadingColor","accessibilityLabel","accessibilityHint","hitSlop","activeOpacity","testID","className","theme","useTheme","hasScaleFeedback","isInteractionBlocked","scaleAnim","onPressIn","onPressOut","usePressAnimation","undefined","baseStyles","useMemo","sizeConfig","styles","alignItems","justifyContent","flexDirection","overflow","backgroundColor","colors","primary","borderRadius","borderWidth","borderColor","border","padding","width","height","resolvedTextColor","card","text","computedTextStyle","fontWeight","Platform","OS","color","defaultHitSlop","resolvedActiveOpacity","resolvedClassName","content","jsxs","Fragment","jsx","Text","Animated","View","transform","scale","Pressable","pressed","opacity","accessibilityRole","accessibilityState","busy","loadingHiddenContent","importantForAccessibility","accessibilityElementsHidden","loadingOverlay","ActivityIndicator","StyleSheet","create","pointerEvents","absoluteFillObject","Button","exports","memo","displayName","PrimaryButton","props","SecondaryButton","IconButton","GhostButton","InverseButton","TextButton"],"sourceRoot":"../../../src","sources":["button/Button.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import React, { useMemo, memo } from 'react';
|
|
4
|
-
import {
|
|
4
|
+
import { ActivityIndicator, Animated, Platform, Pressable, StyleSheet, Text, View } from 'react-native';
|
|
5
5
|
import { useTheme } from "../theme/use-theme.js";
|
|
6
6
|
import { usePressAnimation } from "../hooks/usePressAnimation.js";
|
|
7
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
8
|
const SIZE_CONFIG = {
|
|
9
9
|
small: {
|
|
10
10
|
paddingVertical: 6,
|
|
@@ -43,6 +43,8 @@ const ButtonComponent = ({
|
|
|
43
43
|
textStyle,
|
|
44
44
|
icon,
|
|
45
45
|
iconPosition = 'left',
|
|
46
|
+
loading = false,
|
|
47
|
+
loadingColor,
|
|
46
48
|
accessibilityLabel,
|
|
47
49
|
accessibilityHint,
|
|
48
50
|
hitSlop,
|
|
@@ -52,11 +54,12 @@ const ButtonComponent = ({
|
|
|
52
54
|
}) => {
|
|
53
55
|
const theme = useTheme();
|
|
54
56
|
const hasScaleFeedback = SCALE_VARIANTS.has(variant);
|
|
57
|
+
const isInteractionBlocked = disabled || loading;
|
|
55
58
|
const {
|
|
56
59
|
scaleAnim,
|
|
57
60
|
onPressIn,
|
|
58
61
|
onPressOut
|
|
59
|
-
} = usePressAnimation(hasScaleFeedback ? PRESS_SCALE : undefined);
|
|
62
|
+
} = usePressAnimation(hasScaleFeedback && !isInteractionBlocked ? PRESS_SCALE : undefined);
|
|
60
63
|
const baseStyles = useMemo(() => {
|
|
61
64
|
const sizeConfig = SIZE_CONFIG[size];
|
|
62
65
|
const styles = {
|
|
@@ -103,68 +106,103 @@ const ButtonComponent = ({
|
|
|
103
106
|
}
|
|
104
107
|
return styles;
|
|
105
108
|
}, [variant, size, theme]);
|
|
106
|
-
const
|
|
107
|
-
const sizeConfig = SIZE_CONFIG[size];
|
|
108
|
-
const styles = {
|
|
109
|
-
fontSize: sizeConfig.fontSize,
|
|
110
|
-
fontWeight: Platform.OS === 'web' ? 'bold' : '600'
|
|
111
|
-
};
|
|
109
|
+
const resolvedTextColor = useMemo(() => {
|
|
112
110
|
switch (variant) {
|
|
113
111
|
case 'primary':
|
|
114
|
-
|
|
115
|
-
break;
|
|
112
|
+
return theme.colors.card;
|
|
116
113
|
case 'secondary':
|
|
117
|
-
|
|
118
|
-
break;
|
|
114
|
+
return theme.colors.text;
|
|
119
115
|
case 'inverse':
|
|
120
|
-
|
|
121
|
-
break;
|
|
116
|
+
return '#000000';
|
|
122
117
|
case 'ghost':
|
|
123
118
|
case 'text':
|
|
124
|
-
|
|
125
|
-
|
|
119
|
+
return theme.colors.primary;
|
|
120
|
+
case 'icon':
|
|
121
|
+
default:
|
|
122
|
+
return theme.colors.text;
|
|
126
123
|
}
|
|
127
|
-
|
|
128
|
-
|
|
124
|
+
}, [variant, theme]);
|
|
125
|
+
const computedTextStyle = useMemo(() => {
|
|
126
|
+
const sizeConfig = SIZE_CONFIG[size];
|
|
127
|
+
return {
|
|
128
|
+
fontSize: sizeConfig.fontSize,
|
|
129
|
+
fontWeight: Platform.OS === 'web' ? 'bold' : '600',
|
|
130
|
+
color: resolvedTextColor
|
|
131
|
+
};
|
|
132
|
+
}, [size, resolvedTextColor]);
|
|
129
133
|
const defaultHitSlop = variant === 'icon' ? ICON_HIT_SLOP : undefined;
|
|
130
134
|
const resolvedActiveOpacity = activeOpacity ?? (variant === 'icon' ? 0.7 : 0.8);
|
|
131
135
|
const resolvedClassName = className ?? (variant === 'icon' ? 'bg-background border border-border' : undefined);
|
|
136
|
+
const content = /*#__PURE__*/_jsxs(_Fragment, {
|
|
137
|
+
children: [iconPosition === 'left' && icon, children != null && /*#__PURE__*/_jsx(Text, {
|
|
138
|
+
style: [computedTextStyle, textStyle],
|
|
139
|
+
children: children
|
|
140
|
+
}), iconPosition === 'right' && icon]
|
|
141
|
+
});
|
|
132
142
|
return /*#__PURE__*/_jsx(Animated.View, {
|
|
133
143
|
style: hasScaleFeedback ? {
|
|
134
144
|
transform: [{
|
|
135
145
|
scale: scaleAnim
|
|
136
146
|
}]
|
|
137
147
|
} : undefined,
|
|
138
|
-
children: /*#__PURE__*/
|
|
148
|
+
children: /*#__PURE__*/_jsx(Pressable, {
|
|
139
149
|
...(resolvedClassName ? {
|
|
140
150
|
className: resolvedClassName
|
|
141
151
|
} : {}),
|
|
142
152
|
style: ({
|
|
143
153
|
pressed
|
|
144
|
-
}) => [baseStyles, disabled && {
|
|
154
|
+
}) => [baseStyles, disabled && !loading && {
|
|
145
155
|
opacity: 0.5
|
|
146
|
-
}, pressed && !hasScaleFeedback && {
|
|
156
|
+
}, pressed && !hasScaleFeedback && !isInteractionBlocked && {
|
|
147
157
|
opacity: resolvedActiveOpacity
|
|
148
158
|
}, style],
|
|
149
|
-
onPress: onPress,
|
|
150
|
-
onPressIn: onPressIn,
|
|
151
|
-
onPressOut: onPressOut,
|
|
152
|
-
disabled:
|
|
159
|
+
onPress: isInteractionBlocked ? undefined : onPress,
|
|
160
|
+
onPressIn: isInteractionBlocked ? undefined : onPressIn,
|
|
161
|
+
onPressOut: isInteractionBlocked ? undefined : onPressOut,
|
|
162
|
+
disabled: isInteractionBlocked,
|
|
153
163
|
hitSlop: hitSlop ?? defaultHitSlop,
|
|
154
164
|
accessibilityLabel: accessibilityLabel,
|
|
155
165
|
accessibilityHint: accessibilityHint,
|
|
156
166
|
accessibilityRole: "button",
|
|
157
|
-
accessibilityState: {
|
|
158
|
-
disabled
|
|
167
|
+
accessibilityState: loading ? {
|
|
168
|
+
disabled: isInteractionBlocked,
|
|
169
|
+
busy: true
|
|
170
|
+
} : {
|
|
171
|
+
disabled: isInteractionBlocked
|
|
159
172
|
},
|
|
160
173
|
testID: testID,
|
|
161
|
-
children:
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
174
|
+
children: loading ? /*#__PURE__*/_jsxs(_Fragment, {
|
|
175
|
+
children: [/*#__PURE__*/_jsx(View, {
|
|
176
|
+
style: styles.loadingHiddenContent,
|
|
177
|
+
importantForAccessibility: "no-hide-descendants",
|
|
178
|
+
accessibilityElementsHidden: true,
|
|
179
|
+
children: content
|
|
180
|
+
}), /*#__PURE__*/_jsx(View, {
|
|
181
|
+
style: styles.loadingOverlay,
|
|
182
|
+
children: /*#__PURE__*/_jsx(ActivityIndicator, {
|
|
183
|
+
size: "small",
|
|
184
|
+
color: loadingColor ?? resolvedTextColor
|
|
185
|
+
})
|
|
186
|
+
})]
|
|
187
|
+
}) : content
|
|
165
188
|
})
|
|
166
189
|
});
|
|
167
190
|
};
|
|
191
|
+
const styles = StyleSheet.create({
|
|
192
|
+
loadingHiddenContent: {
|
|
193
|
+
flexDirection: 'row',
|
|
194
|
+
alignItems: 'center',
|
|
195
|
+
justifyContent: 'center',
|
|
196
|
+
opacity: 0,
|
|
197
|
+
pointerEvents: 'none'
|
|
198
|
+
},
|
|
199
|
+
loadingOverlay: {
|
|
200
|
+
...StyleSheet.absoluteFillObject,
|
|
201
|
+
alignItems: 'center',
|
|
202
|
+
justifyContent: 'center',
|
|
203
|
+
pointerEvents: 'none'
|
|
204
|
+
}
|
|
205
|
+
});
|
|
168
206
|
export const Button = /*#__PURE__*/memo(ButtonComponent);
|
|
169
207
|
Button.displayName = 'Button';
|
|
170
208
|
export const PrimaryButton = /*#__PURE__*/memo(props => /*#__PURE__*/_jsx(Button, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","useMemo","memo","
|
|
1
|
+
{"version":3,"names":["React","useMemo","memo","ActivityIndicator","Animated","Platform","Pressable","StyleSheet","Text","View","useTheme","usePressAnimation","jsx","_jsx","Fragment","_Fragment","jsxs","_jsxs","SIZE_CONFIG","small","paddingVertical","paddingHorizontal","fontSize","minHeight","medium","large","ICON_HIT_SLOP","top","bottom","left","right","PRESS_SCALE","SCALE_VARIANTS","Set","ButtonComponent","onPress","children","disabled","variant","size","style","textStyle","icon","iconPosition","loading","loadingColor","accessibilityLabel","accessibilityHint","hitSlop","activeOpacity","testID","className","theme","hasScaleFeedback","has","isInteractionBlocked","scaleAnim","onPressIn","onPressOut","undefined","baseStyles","sizeConfig","styles","alignItems","justifyContent","flexDirection","overflow","backgroundColor","colors","primary","borderRadius","borderWidth","borderColor","border","padding","width","height","resolvedTextColor","card","text","computedTextStyle","fontWeight","OS","color","defaultHitSlop","resolvedActiveOpacity","resolvedClassName","content","transform","scale","pressed","opacity","accessibilityRole","accessibilityState","busy","loadingHiddenContent","importantForAccessibility","accessibilityElementsHidden","loadingOverlay","create","pointerEvents","absoluteFillObject","Button","displayName","PrimaryButton","props","SecondaryButton","IconButton","GhostButton","InverseButton","TextButton"],"sourceRoot":"../../../src","sources":["button/Button.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../../../../src/button/Button.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAwB,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../../../../src/button/Button.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAwB,MAAM,OAAO,CAAC;AAe7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AA0MtE,eAAO,MAAM,MAAM,yCAAwB,CAAC;AAG5C,eAAO,MAAM,aAAa,oCAAgB,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,6CAEpE,CAAC;AAGH,eAAO,MAAM,eAAe,oCAAgB,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,6CAEtE,CAAC;AAGH,eAAO,MAAM,UAAU,oCAAgB,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,6CAEjE,CAAC;AAGH,eAAO,MAAM,WAAW,oCAAgB,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,6CAElE,CAAC;AAGH,eAAO,MAAM,aAAa,oCAAgB,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,6CAEpE,CAAC;AAGH,eAAO,MAAM,UAAU,oCAAgB,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,6CAEjE,CAAC"}
|
|
@@ -11,6 +11,17 @@ export interface ButtonProps {
|
|
|
11
11
|
textStyle?: StyleProp<TextStyle>;
|
|
12
12
|
icon?: React.ReactNode;
|
|
13
13
|
iconPosition?: 'left' | 'right';
|
|
14
|
+
/**
|
|
15
|
+
* When true, displays a centered loading spinner overlay and prevents
|
|
16
|
+
* presses. Children remain in the layout (but visually hidden) so the
|
|
17
|
+
* button preserves its width. Use this for async actions like submit.
|
|
18
|
+
*/
|
|
19
|
+
loading?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Optional color override for the loading spinner. Defaults to the
|
|
22
|
+
* resolved button text color.
|
|
23
|
+
*/
|
|
24
|
+
loadingColor?: string;
|
|
14
25
|
accessibilityLabel?: string;
|
|
15
26
|
accessibilityHint?: string;
|
|
16
27
|
hitSlop?: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/button/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpE,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAE5F,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEtD,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAEjC,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAEhC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACvE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/button/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpE,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAE5F,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEtD,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAEjC,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAEhC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACvE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../../../../src/button/Button.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAwB,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../../../../src/button/Button.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAwB,MAAM,OAAO,CAAC;AAe7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AA0MtE,eAAO,MAAM,MAAM,yCAAwB,CAAC;AAG5C,eAAO,MAAM,aAAa,oCAAgB,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,6CAEpE,CAAC;AAGH,eAAO,MAAM,eAAe,oCAAgB,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,6CAEtE,CAAC;AAGH,eAAO,MAAM,UAAU,oCAAgB,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,6CAEjE,CAAC;AAGH,eAAO,MAAM,WAAW,oCAAgB,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,6CAElE,CAAC;AAGH,eAAO,MAAM,aAAa,oCAAgB,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,6CAEpE,CAAC;AAGH,eAAO,MAAM,UAAU,oCAAgB,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,6CAEjE,CAAC"}
|
|
@@ -11,6 +11,17 @@ export interface ButtonProps {
|
|
|
11
11
|
textStyle?: StyleProp<TextStyle>;
|
|
12
12
|
icon?: React.ReactNode;
|
|
13
13
|
iconPosition?: 'left' | 'right';
|
|
14
|
+
/**
|
|
15
|
+
* When true, displays a centered loading spinner overlay and prevents
|
|
16
|
+
* presses. Children remain in the layout (but visually hidden) so the
|
|
17
|
+
* button preserves its width. Use this for async actions like submit.
|
|
18
|
+
*/
|
|
19
|
+
loading?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Optional color override for the loading spinner. Defaults to the
|
|
22
|
+
* resolved button text color.
|
|
23
|
+
*/
|
|
24
|
+
loadingColor?: string;
|
|
14
25
|
accessibilityLabel?: string;
|
|
15
26
|
accessibilityHint?: string;
|
|
16
27
|
hitSlop?: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/button/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpE,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAE5F,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEtD,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAEjC,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAEhC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACvE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/button/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpE,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAE5F,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEtD,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAEjC,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAEhC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACvE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
|
package/package.json
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { Text } from 'react-native';
|
|
2
3
|
import { render, fireEvent } from '@testing-library/react-native';
|
|
3
4
|
|
|
4
5
|
import { BloomThemeProvider } from '../theme/BloomThemeProvider';
|
|
@@ -58,6 +59,40 @@ describe('Button', () => {
|
|
|
58
59
|
const btn = getByTestId('dis-btn');
|
|
59
60
|
expect(btn.props.accessibilityState).toEqual({ disabled: true });
|
|
60
61
|
});
|
|
62
|
+
|
|
63
|
+
it('keeps children mounted when loading so width is preserved', () => {
|
|
64
|
+
const { UNSAFE_queryAllByType } = renderWithTheme(
|
|
65
|
+
<Button loading>Submit</Button>,
|
|
66
|
+
);
|
|
67
|
+
// The text node remains in the tree even though it is visually hidden.
|
|
68
|
+
const texts = UNSAFE_queryAllByType(Text);
|
|
69
|
+
const hasSubmit = texts.some((node) =>
|
|
70
|
+
Array.isArray(node.props.children)
|
|
71
|
+
? node.props.children.includes('Submit')
|
|
72
|
+
: node.props.children === 'Submit',
|
|
73
|
+
);
|
|
74
|
+
expect(hasSubmit).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('disables the underlying Pressable when loading', () => {
|
|
78
|
+
const { getByTestId } = renderWithTheme(
|
|
79
|
+
<Button testID="loading-btn" loading>
|
|
80
|
+
Submit
|
|
81
|
+
</Button>,
|
|
82
|
+
);
|
|
83
|
+
const btn = getByTestId('loading-btn');
|
|
84
|
+
expect(btn.props.disabled).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('marks loading state via accessibilityState busy + disabled', () => {
|
|
88
|
+
const { getByTestId } = renderWithTheme(
|
|
89
|
+
<Button testID="busy-btn" loading>
|
|
90
|
+
Submit
|
|
91
|
+
</Button>,
|
|
92
|
+
);
|
|
93
|
+
const btn = getByTestId('busy-btn');
|
|
94
|
+
expect(btn.props.accessibilityState).toEqual({ disabled: true, busy: true });
|
|
95
|
+
});
|
|
61
96
|
});
|
|
62
97
|
|
|
63
98
|
describe('Button variants', () => {
|
package/src/button/Button.tsx
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import React, { useMemo, memo } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
ActivityIndicator,
|
|
4
|
+
Animated,
|
|
5
|
+
Platform,
|
|
6
|
+
Pressable,
|
|
7
|
+
StyleSheet,
|
|
8
|
+
Text,
|
|
9
|
+
View,
|
|
10
|
+
type TextStyle,
|
|
11
|
+
type ViewStyle,
|
|
12
|
+
} from 'react-native';
|
|
3
13
|
|
|
4
14
|
import { useTheme } from '../theme/use-theme';
|
|
5
15
|
import { usePressAnimation } from '../hooks/usePressAnimation';
|
|
@@ -43,6 +53,8 @@ const ButtonComponent: React.FC<ButtonProps> = ({
|
|
|
43
53
|
textStyle,
|
|
44
54
|
icon,
|
|
45
55
|
iconPosition = 'left',
|
|
56
|
+
loading = false,
|
|
57
|
+
loadingColor,
|
|
46
58
|
accessibilityLabel,
|
|
47
59
|
accessibilityHint,
|
|
48
60
|
hitSlop,
|
|
@@ -52,8 +64,9 @@ const ButtonComponent: React.FC<ButtonProps> = ({
|
|
|
52
64
|
}) => {
|
|
53
65
|
const theme = useTheme();
|
|
54
66
|
const hasScaleFeedback = SCALE_VARIANTS.has(variant);
|
|
67
|
+
const isInteractionBlocked = disabled || loading;
|
|
55
68
|
const { scaleAnim, onPressIn, onPressOut } = usePressAnimation(
|
|
56
|
-
hasScaleFeedback ? PRESS_SCALE : undefined,
|
|
69
|
+
hasScaleFeedback && !isInteractionBlocked ? PRESS_SCALE : undefined,
|
|
57
70
|
);
|
|
58
71
|
|
|
59
72
|
const baseStyles = useMemo((): ViewStyle => {
|
|
@@ -106,67 +119,104 @@ const ButtonComponent: React.FC<ButtonProps> = ({
|
|
|
106
119
|
return styles;
|
|
107
120
|
}, [variant, size, theme]);
|
|
108
121
|
|
|
109
|
-
const
|
|
110
|
-
const sizeConfig = SIZE_CONFIG[size];
|
|
111
|
-
const styles: TextStyle = {
|
|
112
|
-
fontSize: sizeConfig.fontSize,
|
|
113
|
-
fontWeight: Platform.OS === 'web' ? 'bold' : '600',
|
|
114
|
-
};
|
|
115
|
-
|
|
122
|
+
const resolvedTextColor = useMemo((): string => {
|
|
116
123
|
switch (variant) {
|
|
117
124
|
case 'primary':
|
|
118
|
-
|
|
119
|
-
break;
|
|
125
|
+
return theme.colors.card;
|
|
120
126
|
case 'secondary':
|
|
121
|
-
|
|
122
|
-
break;
|
|
127
|
+
return theme.colors.text;
|
|
123
128
|
case 'inverse':
|
|
124
|
-
|
|
125
|
-
break;
|
|
129
|
+
return '#000000';
|
|
126
130
|
case 'ghost':
|
|
127
131
|
case 'text':
|
|
128
|
-
|
|
129
|
-
|
|
132
|
+
return theme.colors.primary;
|
|
133
|
+
case 'icon':
|
|
134
|
+
default:
|
|
135
|
+
return theme.colors.text;
|
|
130
136
|
}
|
|
137
|
+
}, [variant, theme]);
|
|
131
138
|
|
|
132
|
-
|
|
133
|
-
|
|
139
|
+
const computedTextStyle = useMemo((): TextStyle => {
|
|
140
|
+
const sizeConfig = SIZE_CONFIG[size];
|
|
141
|
+
return {
|
|
142
|
+
fontSize: sizeConfig.fontSize,
|
|
143
|
+
fontWeight: Platform.OS === 'web' ? 'bold' : '600',
|
|
144
|
+
color: resolvedTextColor,
|
|
145
|
+
};
|
|
146
|
+
}, [size, resolvedTextColor]);
|
|
134
147
|
|
|
135
148
|
const defaultHitSlop = variant === 'icon' ? ICON_HIT_SLOP : undefined;
|
|
136
149
|
const resolvedActiveOpacity = activeOpacity ?? (variant === 'icon' ? 0.7 : 0.8);
|
|
137
150
|
const resolvedClassName = className ?? (variant === 'icon' ? 'bg-background border border-border' : undefined);
|
|
138
151
|
|
|
152
|
+
const content = (
|
|
153
|
+
<>
|
|
154
|
+
{iconPosition === 'left' && icon}
|
|
155
|
+
{children != null && (
|
|
156
|
+
<Text style={[computedTextStyle, textStyle]}>{children}</Text>
|
|
157
|
+
)}
|
|
158
|
+
{iconPosition === 'right' && icon}
|
|
159
|
+
</>
|
|
160
|
+
);
|
|
161
|
+
|
|
139
162
|
return (
|
|
140
163
|
<Animated.View style={hasScaleFeedback ? { transform: [{ scale: scaleAnim }] } : undefined}>
|
|
141
164
|
<Pressable
|
|
142
165
|
{...(resolvedClassName ? { className: resolvedClassName } as Record<string, string> : {})}
|
|
143
166
|
style={({ pressed }) => [
|
|
144
167
|
baseStyles,
|
|
145
|
-
disabled && { opacity: 0.5 },
|
|
146
|
-
pressed && !hasScaleFeedback && { opacity: resolvedActiveOpacity },
|
|
168
|
+
disabled && !loading && { opacity: 0.5 },
|
|
169
|
+
pressed && !hasScaleFeedback && !isInteractionBlocked && { opacity: resolvedActiveOpacity },
|
|
147
170
|
style,
|
|
148
171
|
]}
|
|
149
|
-
onPress={onPress}
|
|
150
|
-
onPressIn={onPressIn}
|
|
151
|
-
onPressOut={onPressOut}
|
|
152
|
-
disabled={
|
|
172
|
+
onPress={isInteractionBlocked ? undefined : onPress}
|
|
173
|
+
onPressIn={isInteractionBlocked ? undefined : onPressIn}
|
|
174
|
+
onPressOut={isInteractionBlocked ? undefined : onPressOut}
|
|
175
|
+
disabled={isInteractionBlocked}
|
|
153
176
|
hitSlop={hitSlop ?? defaultHitSlop}
|
|
154
177
|
accessibilityLabel={accessibilityLabel}
|
|
155
178
|
accessibilityHint={accessibilityHint}
|
|
156
179
|
accessibilityRole="button"
|
|
157
|
-
accessibilityState={{ disabled }}
|
|
180
|
+
accessibilityState={loading ? { disabled: isInteractionBlocked, busy: true } : { disabled: isInteractionBlocked }}
|
|
158
181
|
testID={testID}
|
|
159
182
|
>
|
|
160
|
-
{
|
|
161
|
-
|
|
162
|
-
|
|
183
|
+
{loading ? (
|
|
184
|
+
<>
|
|
185
|
+
<View
|
|
186
|
+
style={styles.loadingHiddenContent}
|
|
187
|
+
importantForAccessibility="no-hide-descendants"
|
|
188
|
+
accessibilityElementsHidden
|
|
189
|
+
>
|
|
190
|
+
{content}
|
|
191
|
+
</View>
|
|
192
|
+
<View style={styles.loadingOverlay}>
|
|
193
|
+
<ActivityIndicator size="small" color={loadingColor ?? resolvedTextColor} />
|
|
194
|
+
</View>
|
|
195
|
+
</>
|
|
196
|
+
) : (
|
|
197
|
+
content
|
|
163
198
|
)}
|
|
164
|
-
{iconPosition === 'right' && icon}
|
|
165
199
|
</Pressable>
|
|
166
200
|
</Animated.View>
|
|
167
201
|
);
|
|
168
202
|
};
|
|
169
203
|
|
|
204
|
+
const styles = StyleSheet.create({
|
|
205
|
+
loadingHiddenContent: {
|
|
206
|
+
flexDirection: 'row',
|
|
207
|
+
alignItems: 'center',
|
|
208
|
+
justifyContent: 'center',
|
|
209
|
+
opacity: 0,
|
|
210
|
+
pointerEvents: 'none',
|
|
211
|
+
},
|
|
212
|
+
loadingOverlay: {
|
|
213
|
+
...StyleSheet.absoluteFillObject,
|
|
214
|
+
alignItems: 'center',
|
|
215
|
+
justifyContent: 'center',
|
|
216
|
+
pointerEvents: 'none',
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
|
|
170
220
|
export const Button = memo(ButtonComponent);
|
|
171
221
|
Button.displayName = 'Button';
|
|
172
222
|
|
package/src/button/types.ts
CHANGED
|
@@ -17,6 +17,18 @@ export interface ButtonProps {
|
|
|
17
17
|
icon?: React.ReactNode;
|
|
18
18
|
iconPosition?: 'left' | 'right';
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* When true, displays a centered loading spinner overlay and prevents
|
|
22
|
+
* presses. Children remain in the layout (but visually hidden) so the
|
|
23
|
+
* button preserves its width. Use this for async actions like submit.
|
|
24
|
+
*/
|
|
25
|
+
loading?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Optional color override for the loading spinner. Defaults to the
|
|
28
|
+
* resolved button text color.
|
|
29
|
+
*/
|
|
30
|
+
loadingColor?: string;
|
|
31
|
+
|
|
20
32
|
accessibilityLabel?: string;
|
|
21
33
|
accessibilityHint?: string;
|
|
22
34
|
hitSlop?: { top: number; bottom: number; left: number; right: number };
|