@oxyhq/bloom 0.3.4 → 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/commonjs/theme/set-color-scheme-safe.js +34 -12
- package/lib/commonjs/theme/set-color-scheme-safe.js.map +1 -1
- package/lib/module/button/Button.js +70 -32
- package/lib/module/button/Button.js.map +1 -1
- package/lib/module/theme/set-color-scheme-safe.js +34 -12
- package/lib/module/theme/set-color-scheme-safe.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/commonjs/theme/set-color-scheme-safe.d.ts +27 -9
- package/lib/typescript/commonjs/theme/set-color-scheme-safe.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/lib/typescript/module/theme/set-color-scheme-safe.d.ts +27 -9
- package/lib/typescript/module/theme/set-color-scheme-safe.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
- package/src/theme/set-color-scheme-safe.ts +36 -12
|
@@ -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":[]}
|
|
@@ -7,15 +7,33 @@ exports.setColorSchemeSafe = setColorSchemeSafe;
|
|
|
7
7
|
var _reactNative = require("react-native");
|
|
8
8
|
/**
|
|
9
9
|
* Safely set the color scheme via Appearance API.
|
|
10
|
-
* On Android (RN 0.83+), Appearance.setColorScheme has a Kotlin non-null
|
|
11
|
-
* annotation on `style`. Passing null for 'system' crashes.
|
|
12
|
-
* Workaround: resolve the system preference and pass 'light'/'dark' instead.
|
|
13
10
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
11
|
+
* Behavior by mode:
|
|
12
|
+
* - 'light' / 'dark': set the explicit override.
|
|
13
|
+
* - 'system' / 'adaptive': leave the OS in control. We must NOT call
|
|
14
|
+
* Appearance.setColorScheme(resolved) here — doing so installs an
|
|
15
|
+
* app-level override that masks the OS preference. Once that override
|
|
16
|
+
* is set, useColorScheme() / Appearance.getColorScheme() return the
|
|
17
|
+
* frozen override instead of the live OS value, and the app stops
|
|
18
|
+
* following dark↔light OS toggles until a cold restart.
|
|
19
|
+
*
|
|
20
|
+
* On iOS we additionally pass 'unspecified' to clear any prior
|
|
21
|
+
* override that may have been installed by a previous explicit mode.
|
|
22
|
+
* 'unspecified' is the documented sentinel that tells the native
|
|
23
|
+
* Appearance module to fall back to the OS preference. (RN's JS
|
|
24
|
+
* implementation forwards this straight through to the native
|
|
25
|
+
* bridge; on iOS this clears the override.)
|
|
26
|
+
*
|
|
27
|
+
* On Android (RN 0.83+) the native Kotlin signature has @NonNull on
|
|
28
|
+
* `style` and rejects null, and 'unspecified' is not honored as a
|
|
29
|
+
* clear-override sentinel on Android either. As a result, if a user
|
|
30
|
+
* previously selected 'light' or 'dark' and then switches back to
|
|
31
|
+
* 'system' on Android, the override remains until the next cold
|
|
32
|
+
* restart. Users who never explicitly overrode are unaffected because
|
|
33
|
+
* we never install an override in system mode in the first place.
|
|
34
|
+
*
|
|
35
|
+
* On react-native-web, Appearance.setColorScheme is not implemented at
|
|
36
|
+
* all; the browser controls the color scheme, so we bail out on web.
|
|
19
37
|
*/
|
|
20
38
|
function setColorSchemeSafe(mode) {
|
|
21
39
|
if (_reactNative.Platform.OS === 'web') {
|
|
@@ -23,10 +41,14 @@ function setColorSchemeSafe(mode) {
|
|
|
23
41
|
}
|
|
24
42
|
const effectiveMode = mode === 'adaptive' ? 'system' : mode;
|
|
25
43
|
if (effectiveMode === 'system') {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
_reactNative.
|
|
44
|
+
// Clear any prior app-level override so useColorScheme() tracks the
|
|
45
|
+
// OS. iOS honors 'unspecified' as a sentinel to fall back to the
|
|
46
|
+
// system preference; Android does not (see note above).
|
|
47
|
+
if (_reactNative.Platform.OS === 'ios') {
|
|
48
|
+
_reactNative.Appearance.setColorScheme('unspecified');
|
|
49
|
+
}
|
|
50
|
+
return;
|
|
30
51
|
}
|
|
52
|
+
_reactNative.Appearance.setColorScheme(effectiveMode);
|
|
31
53
|
}
|
|
32
54
|
//# sourceMappingURL=set-color-scheme-safe.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_reactNative","require","setColorSchemeSafe","mode","Platform","OS","effectiveMode","
|
|
1
|
+
{"version":3,"names":["_reactNative","require","setColorSchemeSafe","mode","Platform","OS","effectiveMode","Appearance","setColorScheme"],"sourceRoot":"../../../src","sources":["theme/set-color-scheme-safe.ts"],"mappings":";;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASC,kBAAkBA,CAACC,IAAe,EAAE;EAClD,IAAIC,qBAAQ,CAACC,EAAE,KAAK,KAAK,EAAE;IACzB;EACF;EAEA,MAAMC,aAAa,GAAGH,IAAI,KAAK,UAAU,GAAG,QAAQ,GAAGA,IAAI;EAE3D,IAAIG,aAAa,KAAK,QAAQ,EAAE;IAC9B;IACA;IACA;IACA,IAAIF,qBAAQ,CAACC,EAAE,KAAK,KAAK,EAAE;MACzBE,uBAAU,CAACC,cAAc,CAAC,aAAa,CAAC;IAC1C;IACA;EACF;EAEAD,uBAAU,CAACC,cAAc,CAACF,aAAa,CAAC;AAC1C","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":[]}
|
|
@@ -3,15 +3,33 @@
|
|
|
3
3
|
import { Appearance, Platform } from 'react-native';
|
|
4
4
|
/**
|
|
5
5
|
* Safely set the color scheme via Appearance API.
|
|
6
|
-
* On Android (RN 0.83+), Appearance.setColorScheme has a Kotlin non-null
|
|
7
|
-
* annotation on `style`. Passing null for 'system' crashes.
|
|
8
|
-
* Workaround: resolve the system preference and pass 'light'/'dark' instead.
|
|
9
6
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
7
|
+
* Behavior by mode:
|
|
8
|
+
* - 'light' / 'dark': set the explicit override.
|
|
9
|
+
* - 'system' / 'adaptive': leave the OS in control. We must NOT call
|
|
10
|
+
* Appearance.setColorScheme(resolved) here — doing so installs an
|
|
11
|
+
* app-level override that masks the OS preference. Once that override
|
|
12
|
+
* is set, useColorScheme() / Appearance.getColorScheme() return the
|
|
13
|
+
* frozen override instead of the live OS value, and the app stops
|
|
14
|
+
* following dark↔light OS toggles until a cold restart.
|
|
15
|
+
*
|
|
16
|
+
* On iOS we additionally pass 'unspecified' to clear any prior
|
|
17
|
+
* override that may have been installed by a previous explicit mode.
|
|
18
|
+
* 'unspecified' is the documented sentinel that tells the native
|
|
19
|
+
* Appearance module to fall back to the OS preference. (RN's JS
|
|
20
|
+
* implementation forwards this straight through to the native
|
|
21
|
+
* bridge; on iOS this clears the override.)
|
|
22
|
+
*
|
|
23
|
+
* On Android (RN 0.83+) the native Kotlin signature has @NonNull on
|
|
24
|
+
* `style` and rejects null, and 'unspecified' is not honored as a
|
|
25
|
+
* clear-override sentinel on Android either. As a result, if a user
|
|
26
|
+
* previously selected 'light' or 'dark' and then switches back to
|
|
27
|
+
* 'system' on Android, the override remains until the next cold
|
|
28
|
+
* restart. Users who never explicitly overrode are unaffected because
|
|
29
|
+
* we never install an override in system mode in the first place.
|
|
30
|
+
*
|
|
31
|
+
* On react-native-web, Appearance.setColorScheme is not implemented at
|
|
32
|
+
* all; the browser controls the color scheme, so we bail out on web.
|
|
15
33
|
*/
|
|
16
34
|
export function setColorSchemeSafe(mode) {
|
|
17
35
|
if (Platform.OS === 'web') {
|
|
@@ -19,10 +37,14 @@ export function setColorSchemeSafe(mode) {
|
|
|
19
37
|
}
|
|
20
38
|
const effectiveMode = mode === 'adaptive' ? 'system' : mode;
|
|
21
39
|
if (effectiveMode === 'system') {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
40
|
+
// Clear any prior app-level override so useColorScheme() tracks the
|
|
41
|
+
// OS. iOS honors 'unspecified' as a sentinel to fall back to the
|
|
42
|
+
// system preference; Android does not (see note above).
|
|
43
|
+
if (Platform.OS === 'ios') {
|
|
44
|
+
Appearance.setColorScheme('unspecified');
|
|
45
|
+
}
|
|
46
|
+
return;
|
|
26
47
|
}
|
|
48
|
+
Appearance.setColorScheme(effectiveMode);
|
|
27
49
|
}
|
|
28
50
|
//# sourceMappingURL=set-color-scheme-safe.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["Appearance","Platform","setColorSchemeSafe","mode","OS","effectiveMode","
|
|
1
|
+
{"version":3,"names":["Appearance","Platform","setColorSchemeSafe","mode","OS","effectiveMode","setColorScheme"],"sourceRoot":"../../../src","sources":["theme/set-color-scheme-safe.ts"],"mappings":";;AAAA,SAASA,UAAU,EAAEC,QAAQ,QAAQ,cAAc;AAGnD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,kBAAkBA,CAACC,IAAe,EAAE;EAClD,IAAIF,QAAQ,CAACG,EAAE,KAAK,KAAK,EAAE;IACzB;EACF;EAEA,MAAMC,aAAa,GAAGF,IAAI,KAAK,UAAU,GAAG,QAAQ,GAAGA,IAAI;EAE3D,IAAIE,aAAa,KAAK,QAAQ,EAAE;IAC9B;IACA;IACA;IACA,IAAIJ,QAAQ,CAACG,EAAE,KAAK,KAAK,EAAE;MACzBJ,UAAU,CAACM,cAAc,CAAC,aAAa,CAAC;IAC1C;IACA;EACF;EAEAN,UAAU,CAACM,cAAc,CAACD,aAAa,CAAC;AAC1C","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,15 +1,33 @@
|
|
|
1
1
|
import type { ThemeMode } from './types';
|
|
2
2
|
/**
|
|
3
3
|
* Safely set the color scheme via Appearance API.
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* Appearance.getColorScheme()
|
|
11
|
-
*
|
|
12
|
-
*
|
|
4
|
+
*
|
|
5
|
+
* Behavior by mode:
|
|
6
|
+
* - 'light' / 'dark': set the explicit override.
|
|
7
|
+
* - 'system' / 'adaptive': leave the OS in control. We must NOT call
|
|
8
|
+
* Appearance.setColorScheme(resolved) here — doing so installs an
|
|
9
|
+
* app-level override that masks the OS preference. Once that override
|
|
10
|
+
* is set, useColorScheme() / Appearance.getColorScheme() return the
|
|
11
|
+
* frozen override instead of the live OS value, and the app stops
|
|
12
|
+
* following dark↔light OS toggles until a cold restart.
|
|
13
|
+
*
|
|
14
|
+
* On iOS we additionally pass 'unspecified' to clear any prior
|
|
15
|
+
* override that may have been installed by a previous explicit mode.
|
|
16
|
+
* 'unspecified' is the documented sentinel that tells the native
|
|
17
|
+
* Appearance module to fall back to the OS preference. (RN's JS
|
|
18
|
+
* implementation forwards this straight through to the native
|
|
19
|
+
* bridge; on iOS this clears the override.)
|
|
20
|
+
*
|
|
21
|
+
* On Android (RN 0.83+) the native Kotlin signature has @NonNull on
|
|
22
|
+
* `style` and rejects null, and 'unspecified' is not honored as a
|
|
23
|
+
* clear-override sentinel on Android either. As a result, if a user
|
|
24
|
+
* previously selected 'light' or 'dark' and then switches back to
|
|
25
|
+
* 'system' on Android, the override remains until the next cold
|
|
26
|
+
* restart. Users who never explicitly overrode are unaffected because
|
|
27
|
+
* we never install an override in system mode in the first place.
|
|
28
|
+
*
|
|
29
|
+
* On react-native-web, Appearance.setColorScheme is not implemented at
|
|
30
|
+
* all; the browser controls the color scheme, so we bail out on web.
|
|
13
31
|
*/
|
|
14
32
|
export declare function setColorSchemeSafe(mode: ThemeMode): void;
|
|
15
33
|
//# sourceMappingURL=set-color-scheme-safe.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"set-color-scheme-safe.d.ts","sourceRoot":"","sources":["../../../../src/theme/set-color-scheme-safe.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC
|
|
1
|
+
{"version":3,"file":"set-color-scheme-safe.d.ts","sourceRoot":"","sources":["../../../../src/theme/set-color-scheme-safe.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,QAkBjD"}
|
|
@@ -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,15 +1,33 @@
|
|
|
1
1
|
import type { ThemeMode } from './types';
|
|
2
2
|
/**
|
|
3
3
|
* Safely set the color scheme via Appearance API.
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* Appearance.getColorScheme()
|
|
11
|
-
*
|
|
12
|
-
*
|
|
4
|
+
*
|
|
5
|
+
* Behavior by mode:
|
|
6
|
+
* - 'light' / 'dark': set the explicit override.
|
|
7
|
+
* - 'system' / 'adaptive': leave the OS in control. We must NOT call
|
|
8
|
+
* Appearance.setColorScheme(resolved) here — doing so installs an
|
|
9
|
+
* app-level override that masks the OS preference. Once that override
|
|
10
|
+
* is set, useColorScheme() / Appearance.getColorScheme() return the
|
|
11
|
+
* frozen override instead of the live OS value, and the app stops
|
|
12
|
+
* following dark↔light OS toggles until a cold restart.
|
|
13
|
+
*
|
|
14
|
+
* On iOS we additionally pass 'unspecified' to clear any prior
|
|
15
|
+
* override that may have been installed by a previous explicit mode.
|
|
16
|
+
* 'unspecified' is the documented sentinel that tells the native
|
|
17
|
+
* Appearance module to fall back to the OS preference. (RN's JS
|
|
18
|
+
* implementation forwards this straight through to the native
|
|
19
|
+
* bridge; on iOS this clears the override.)
|
|
20
|
+
*
|
|
21
|
+
* On Android (RN 0.83+) the native Kotlin signature has @NonNull on
|
|
22
|
+
* `style` and rejects null, and 'unspecified' is not honored as a
|
|
23
|
+
* clear-override sentinel on Android either. As a result, if a user
|
|
24
|
+
* previously selected 'light' or 'dark' and then switches back to
|
|
25
|
+
* 'system' on Android, the override remains until the next cold
|
|
26
|
+
* restart. Users who never explicitly overrode are unaffected because
|
|
27
|
+
* we never install an override in system mode in the first place.
|
|
28
|
+
*
|
|
29
|
+
* On react-native-web, Appearance.setColorScheme is not implemented at
|
|
30
|
+
* all; the browser controls the color scheme, so we bail out on web.
|
|
13
31
|
*/
|
|
14
32
|
export declare function setColorSchemeSafe(mode: ThemeMode): void;
|
|
15
33
|
//# sourceMappingURL=set-color-scheme-safe.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"set-color-scheme-safe.d.ts","sourceRoot":"","sources":["../../../../src/theme/set-color-scheme-safe.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC
|
|
1
|
+
{"version":3,"file":"set-color-scheme-safe.d.ts","sourceRoot":"","sources":["../../../../src/theme/set-color-scheme-safe.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,QAkBjD"}
|
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 };
|
|
@@ -3,15 +3,33 @@ import type { ThemeMode } from './types';
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Safely set the color scheme via Appearance API.
|
|
6
|
-
* On Android (RN 0.83+), Appearance.setColorScheme has a Kotlin non-null
|
|
7
|
-
* annotation on `style`. Passing null for 'system' crashes.
|
|
8
|
-
* Workaround: resolve the system preference and pass 'light'/'dark' instead.
|
|
9
6
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
7
|
+
* Behavior by mode:
|
|
8
|
+
* - 'light' / 'dark': set the explicit override.
|
|
9
|
+
* - 'system' / 'adaptive': leave the OS in control. We must NOT call
|
|
10
|
+
* Appearance.setColorScheme(resolved) here — doing so installs an
|
|
11
|
+
* app-level override that masks the OS preference. Once that override
|
|
12
|
+
* is set, useColorScheme() / Appearance.getColorScheme() return the
|
|
13
|
+
* frozen override instead of the live OS value, and the app stops
|
|
14
|
+
* following dark↔light OS toggles until a cold restart.
|
|
15
|
+
*
|
|
16
|
+
* On iOS we additionally pass 'unspecified' to clear any prior
|
|
17
|
+
* override that may have been installed by a previous explicit mode.
|
|
18
|
+
* 'unspecified' is the documented sentinel that tells the native
|
|
19
|
+
* Appearance module to fall back to the OS preference. (RN's JS
|
|
20
|
+
* implementation forwards this straight through to the native
|
|
21
|
+
* bridge; on iOS this clears the override.)
|
|
22
|
+
*
|
|
23
|
+
* On Android (RN 0.83+) the native Kotlin signature has @NonNull on
|
|
24
|
+
* `style` and rejects null, and 'unspecified' is not honored as a
|
|
25
|
+
* clear-override sentinel on Android either. As a result, if a user
|
|
26
|
+
* previously selected 'light' or 'dark' and then switches back to
|
|
27
|
+
* 'system' on Android, the override remains until the next cold
|
|
28
|
+
* restart. Users who never explicitly overrode are unaffected because
|
|
29
|
+
* we never install an override in system mode in the first place.
|
|
30
|
+
*
|
|
31
|
+
* On react-native-web, Appearance.setColorScheme is not implemented at
|
|
32
|
+
* all; the browser controls the color scheme, so we bail out on web.
|
|
15
33
|
*/
|
|
16
34
|
export function setColorSchemeSafe(mode: ThemeMode) {
|
|
17
35
|
if (Platform.OS === 'web') {
|
|
@@ -19,10 +37,16 @@ export function setColorSchemeSafe(mode: ThemeMode) {
|
|
|
19
37
|
}
|
|
20
38
|
|
|
21
39
|
const effectiveMode = mode === 'adaptive' ? 'system' : mode;
|
|
40
|
+
|
|
22
41
|
if (effectiveMode === 'system') {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
42
|
+
// Clear any prior app-level override so useColorScheme() tracks the
|
|
43
|
+
// OS. iOS honors 'unspecified' as a sentinel to fall back to the
|
|
44
|
+
// system preference; Android does not (see note above).
|
|
45
|
+
if (Platform.OS === 'ios') {
|
|
46
|
+
Appearance.setColorScheme('unspecified');
|
|
47
|
+
}
|
|
48
|
+
return;
|
|
27
49
|
}
|
|
50
|
+
|
|
51
|
+
Appearance.setColorScheme(effectiveMode);
|
|
28
52
|
}
|