zkit-ui 1.0.1 → 1.0.3
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/dist/ui/Button/index.d.ts.map +1 -1
- package/dist/ui/Button/index.js +155 -18
- package/package.json +1 -1
- package/src/ui/Button/README.md +2 -2
- package/src/ui/Button/index.tsx +304 -20
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/Button/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAEV,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/Button/index.tsx"],"names":[],"mappings":"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"}
|
package/dist/ui/Button/index.js
CHANGED
|
@@ -53,6 +53,7 @@ const LOADING_TIMING = {
|
|
|
53
53
|
easing: react_native_reanimated_1.Easing.out(react_native_reanimated_1.Easing.cubic),
|
|
54
54
|
reduceMotion: react_native_reanimated_1.ReduceMotion.System,
|
|
55
55
|
};
|
|
56
|
+
const ANIMATED_EXIT_RESIDENCY_MS = LOADING_TIMING.duration + 40;
|
|
56
57
|
const DISABLED_OPACITY = 0.52;
|
|
57
58
|
const PRESSED_OPACITY = 0.82;
|
|
58
59
|
const PRESSED_SCALE = 0.985;
|
|
@@ -373,7 +374,7 @@ function resolvePressEffect(effect, interactionDisabled) {
|
|
|
373
374
|
return 'none';
|
|
374
375
|
if (effect !== 'auto')
|
|
375
376
|
return effect;
|
|
376
|
-
return '
|
|
377
|
+
return 'opacity';
|
|
377
378
|
}
|
|
378
379
|
function resolvePressEffectFlags(effect) {
|
|
379
380
|
return {
|
|
@@ -538,8 +539,16 @@ function resolveWebCursorStyle(disabled) {
|
|
|
538
539
|
return undefined;
|
|
539
540
|
return { cursor: disabled ? 'not-allowed' : 'pointer' };
|
|
540
541
|
}
|
|
541
|
-
function
|
|
542
|
-
return
|
|
542
|
+
function requiresAnimatedPressEffect(effect) {
|
|
543
|
+
return (effect === 'scale' ||
|
|
544
|
+
effect === 'scale-opacity' ||
|
|
545
|
+
effect === 'highlight' ||
|
|
546
|
+
effect === 'scale-highlight');
|
|
547
|
+
}
|
|
548
|
+
function shouldRequestAnimatedPressPath({ disabled, pressEffect, }) {
|
|
549
|
+
if (disabled)
|
|
550
|
+
return false;
|
|
551
|
+
return requiresAnimatedPressEffect(pressEffect);
|
|
543
552
|
}
|
|
544
553
|
function useButtonFrame({ variant = 'solid', tone = 'primary', size = 'md', shape = 'rounded', block = false, disabled = false, loading = false, icon, iconPlacement = 'start', iconOnly = false, color, colors, border, layout, gradient, shadow = 'none', children, textStyle, accessibilityLabel, }) {
|
|
545
554
|
var _a, _b;
|
|
@@ -670,7 +679,7 @@ function useButtonFrame({ variant = 'solid', tone = 'primary', size = 'md', shap
|
|
|
670
679
|
borderStyle,
|
|
671
680
|
};
|
|
672
681
|
}
|
|
673
|
-
function ButtonStaticImpl({ variant = 'solid', tone = 'primary', size = 'md', shape = 'rounded', block = false, disabled = false, loading = false, loadingMode: _loadingMode = 'inline', pressEffect
|
|
682
|
+
function ButtonStaticImpl({ variant = 'solid', tone = 'primary', size = 'md', shape = 'rounded', block = false, disabled = false, loading = false, loadingMode: _loadingMode = 'inline', pressEffect = 'auto', icon, iconPlacement = 'start', iconOnly = false, color, colors, border, layout, gradient, shadow = 'none', children, style, contentStyle, textStyle, accessibilityLabel, accessibilityState, hitSlop, onPress, testID, ...pressableProps }, ref) {
|
|
674
683
|
const frame = useButtonFrame({
|
|
675
684
|
accessibilityLabel,
|
|
676
685
|
block,
|
|
@@ -698,19 +707,30 @@ function ButtonStaticImpl({ variant = 'solid', tone = 'primary', size = 'md', sh
|
|
|
698
707
|
return;
|
|
699
708
|
onPress === null || onPress === void 0 ? void 0 : onPress(event);
|
|
700
709
|
}, [interactionDisabled, onPress]);
|
|
710
|
+
const pressOpacityEnabled = !interactionDisabled && (pressEffect === 'auto' || pressEffect === 'opacity');
|
|
711
|
+
const rootStyle = React.useCallback(({ pressed }) => [
|
|
712
|
+
styles.root,
|
|
713
|
+
rootSizeStyle,
|
|
714
|
+
radiusStyle,
|
|
715
|
+
shadowStyle,
|
|
716
|
+
visualDisabled ? styles.disabledRoot : null,
|
|
717
|
+
pressOpacityEnabled && pressed ? styles.pressedRoot : null,
|
|
718
|
+
webCursorStyle,
|
|
719
|
+
style,
|
|
720
|
+
], [
|
|
721
|
+
pressOpacityEnabled,
|
|
722
|
+
radiusStyle,
|
|
723
|
+
rootSizeStyle,
|
|
724
|
+
shadowStyle,
|
|
725
|
+
style,
|
|
726
|
+
visualDisabled,
|
|
727
|
+
webCursorStyle,
|
|
728
|
+
]);
|
|
701
729
|
return ((0, jsx_runtime_1.jsx)(react_native_1.Pressable, { ...pressableProps, ref: ref, accessibilityLabel: resolvedAccessibilityLabel, accessibilityRole: "button", accessibilityState: {
|
|
702
730
|
...accessibilityState,
|
|
703
731
|
busy: Boolean(loading || (accessibilityState === null || accessibilityState === void 0 ? void 0 : accessibilityState.busy)),
|
|
704
732
|
disabled: Boolean(interactionDisabled || (accessibilityState === null || accessibilityState === void 0 ? void 0 : accessibilityState.disabled)),
|
|
705
|
-
}, disabled: interactionDisabled, hitSlop: hitSlop !== null && hitSlop !== void 0 ? hitSlop : defaultHitSlop, onPress: handlePress, style: [
|
|
706
|
-
styles.root,
|
|
707
|
-
rootSizeStyle,
|
|
708
|
-
radiusStyle,
|
|
709
|
-
shadowStyle,
|
|
710
|
-
visualDisabled ? styles.disabledRoot : null,
|
|
711
|
-
webCursorStyle,
|
|
712
|
-
style,
|
|
713
|
-
], testID: testID, children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { pointerEvents: "none", style: [
|
|
733
|
+
}, disabled: interactionDisabled, hitSlop: hitSlop !== null && hitSlop !== void 0 ? hitSlop : defaultHitSlop, onPress: handlePress, style: rootStyle, testID: testID, children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { pointerEvents: "none", style: [
|
|
714
734
|
styles.content,
|
|
715
735
|
contentSizeStyle,
|
|
716
736
|
paddingStyle,
|
|
@@ -720,6 +740,101 @@ function ButtonStaticImpl({ variant = 'solid', tone = 'primary', size = 'md', sh
|
|
|
720
740
|
contentStyle,
|
|
721
741
|
], children: [LinearGradientComponent && gradientCfg ? ((0, jsx_runtime_1.jsx)(LinearGradientComponent, { pointerEvents: "none", colors: gradientCfg.colors, start: gradientCfg.start, end: gradientCfg.end, style: [react_native_1.StyleSheet.absoluteFillObject, radiusStyle] })) : null, iconOnly ? ((icon !== null && icon !== void 0 ? icon : resolvedText)) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [icon && iconPlacement === 'start' ? ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [iconBaseBoxStyle, iconPlacedBoxStyle], children: icon })) : null, resolvedText, icon && iconPlacement === 'end' ? ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [iconBaseBoxStyle, iconPlacedBoxStyle], children: icon })) : null] }))] }) }));
|
|
722
742
|
}
|
|
743
|
+
function ButtonPressAnimatedImpl({ variant = 'solid', tone = 'primary', size = 'md', shape = 'rounded', block = false, disabled = false, loading = false, loadingMode: _loadingMode = 'inline', pressEffect = 'auto', icon, iconPlacement = 'start', iconOnly = false, color, colors, border, layout, gradient, shadow = 'none', children, style, contentStyle, textStyle, accessibilityLabel, accessibilityState, hitSlop, onPress, onPressIn, onPressOut, testID, ...pressableProps }, ref) {
|
|
744
|
+
const frame = useButtonFrame({
|
|
745
|
+
accessibilityLabel,
|
|
746
|
+
block,
|
|
747
|
+
border,
|
|
748
|
+
children,
|
|
749
|
+
color,
|
|
750
|
+
colors,
|
|
751
|
+
disabled,
|
|
752
|
+
gradient,
|
|
753
|
+
icon,
|
|
754
|
+
iconOnly,
|
|
755
|
+
iconPlacement,
|
|
756
|
+
layout,
|
|
757
|
+
loading,
|
|
758
|
+
shadow,
|
|
759
|
+
shape,
|
|
760
|
+
size,
|
|
761
|
+
textStyle,
|
|
762
|
+
tone,
|
|
763
|
+
variant,
|
|
764
|
+
});
|
|
765
|
+
const { accentColor, borderStyle, contentSizeStyle, defaultHitSlop, gradientCfg, hasGradientBackground, hasIcon, iconBaseBoxStyle, iconPlacedBoxStyle, interactionDisabled, LinearGradientComponent, paddingStyle, radiusStyle, resolvedAccessibilityLabel, resolvedText, rootSizeStyle, shadowStyle, visualColors, visualDisabled, webCursorStyle, } = frame;
|
|
766
|
+
const pressSv = (0, react_native_reanimated_1.useSharedValue)(0);
|
|
767
|
+
const resolvedPressEffect = React.useMemo(() => resolvePressEffect(pressEffect, interactionDisabled), [interactionDisabled, pressEffect]);
|
|
768
|
+
const pressEffectFlags = React.useMemo(() => resolvePressEffectFlags(resolvedPressEffect), [resolvedPressEffect]);
|
|
769
|
+
const pressHighlightEnabled = pressEffectFlags.highlight;
|
|
770
|
+
const pressOpacityEnabled = pressEffectFlags.opacity;
|
|
771
|
+
const pressScaleEnabled = pressEffectFlags.scale;
|
|
772
|
+
const pressOverlay = React.useMemo(() => resolveOverlay({
|
|
773
|
+
accentColor,
|
|
774
|
+
colors,
|
|
775
|
+
hasGradientBackground,
|
|
776
|
+
variant,
|
|
777
|
+
}), [accentColor, colors, hasGradientBackground, variant]);
|
|
778
|
+
React.useEffect(() => {
|
|
779
|
+
if (interactionDisabled || resolvedPressEffect === 'none') {
|
|
780
|
+
pressSv.value = 0;
|
|
781
|
+
}
|
|
782
|
+
}, [interactionDisabled, pressSv, resolvedPressEffect]);
|
|
783
|
+
const rootAnimatedStyle = (0, react_native_reanimated_1.useAnimatedStyle)(() => {
|
|
784
|
+
const baseOpacity = visualDisabled ? DISABLED_OPACITY : 1;
|
|
785
|
+
const scale = pressScaleEnabled
|
|
786
|
+
? (0, react_native_reanimated_1.interpolate)(pressSv.value, [0, 1], [1, PRESSED_SCALE])
|
|
787
|
+
: 1;
|
|
788
|
+
const opacity = pressOpacityEnabled
|
|
789
|
+
? (0, react_native_reanimated_1.interpolate)(pressSv.value, [0, 1], [baseOpacity, PRESSED_OPACITY])
|
|
790
|
+
: baseOpacity;
|
|
791
|
+
return {
|
|
792
|
+
opacity,
|
|
793
|
+
transform: [{ scale }],
|
|
794
|
+
};
|
|
795
|
+
}, [pressOpacityEnabled, pressScaleEnabled, visualDisabled]);
|
|
796
|
+
const overlayAnimatedStyle = (0, react_native_reanimated_1.useAnimatedStyle)(() => {
|
|
797
|
+
const opacity = pressHighlightEnabled
|
|
798
|
+
? (0, react_native_reanimated_1.interpolate)(pressSv.value, [0, 1], [0, pressOverlay.strength])
|
|
799
|
+
: 0;
|
|
800
|
+
return { opacity };
|
|
801
|
+
}, [pressHighlightEnabled, pressOverlay.strength]);
|
|
802
|
+
const handlePress = React.useCallback((event) => {
|
|
803
|
+
if (interactionDisabled)
|
|
804
|
+
return;
|
|
805
|
+
onPress === null || onPress === void 0 ? void 0 : onPress(event);
|
|
806
|
+
}, [interactionDisabled, onPress]);
|
|
807
|
+
const handlePressIn = React.useCallback((event) => {
|
|
808
|
+
if (!interactionDisabled && resolvedPressEffect !== 'none') {
|
|
809
|
+
pressSv.value = (0, react_native_reanimated_1.withTiming)(1, PRESS_TIMING);
|
|
810
|
+
}
|
|
811
|
+
onPressIn === null || onPressIn === void 0 ? void 0 : onPressIn(event);
|
|
812
|
+
}, [interactionDisabled, onPressIn, pressSv, resolvedPressEffect]);
|
|
813
|
+
const handlePressOut = React.useCallback((event) => {
|
|
814
|
+
if (!interactionDisabled && resolvedPressEffect !== 'none') {
|
|
815
|
+
pressSv.value = (0, react_native_reanimated_1.withTiming)(0, PRESS_TIMING);
|
|
816
|
+
}
|
|
817
|
+
onPressOut === null || onPressOut === void 0 ? void 0 : onPressOut(event);
|
|
818
|
+
}, [interactionDisabled, onPressOut, pressSv, resolvedPressEffect]);
|
|
819
|
+
return ((0, jsx_runtime_1.jsx)(AnimatedPressable, { ...pressableProps, ref: ref, accessibilityLabel: resolvedAccessibilityLabel, accessibilityRole: "button", accessibilityState: {
|
|
820
|
+
...accessibilityState,
|
|
821
|
+
busy: Boolean(loading || (accessibilityState === null || accessibilityState === void 0 ? void 0 : accessibilityState.busy)),
|
|
822
|
+
disabled: Boolean(interactionDisabled || (accessibilityState === null || accessibilityState === void 0 ? void 0 : accessibilityState.disabled)),
|
|
823
|
+
}, disabled: interactionDisabled, hitSlop: hitSlop !== null && hitSlop !== void 0 ? hitSlop : defaultHitSlop, onPress: handlePress, onPressIn: handlePressIn, onPressOut: handlePressOut, style: [styles.root, rootSizeStyle, radiusStyle, shadowStyle, rootAnimatedStyle, webCursorStyle, style], testID: testID, children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { pointerEvents: "none", style: [
|
|
824
|
+
styles.content,
|
|
825
|
+
contentSizeStyle,
|
|
826
|
+
paddingStyle,
|
|
827
|
+
radiusStyle,
|
|
828
|
+
borderStyle,
|
|
829
|
+
{ backgroundColor: visualColors.backgroundColor },
|
|
830
|
+
contentStyle,
|
|
831
|
+
], children: [LinearGradientComponent && gradientCfg ? ((0, jsx_runtime_1.jsx)(LinearGradientComponent, { pointerEvents: "none", colors: gradientCfg.colors, start: gradientCfg.start, end: gradientCfg.end, style: [react_native_1.StyleSheet.absoluteFillObject, radiusStyle] })) : null, pressHighlightEnabled ? ((0, jsx_runtime_1.jsx)(react_native_reanimated_1.default.View, { pointerEvents: "none", style: [
|
|
832
|
+
react_native_1.StyleSheet.absoluteFillObject,
|
|
833
|
+
radiusStyle,
|
|
834
|
+
{ backgroundColor: pressOverlay.color },
|
|
835
|
+
overlayAnimatedStyle,
|
|
836
|
+
] })) : null, iconOnly ? ((icon !== null && icon !== void 0 ? icon : resolvedText)) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [icon && iconPlacement === 'start' ? ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [iconBaseBoxStyle, iconPlacedBoxStyle], children: icon })) : null, resolvedText, icon && iconPlacement === 'end' ? ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [iconBaseBoxStyle, iconPlacedBoxStyle], children: icon })) : null] }))] }) }));
|
|
837
|
+
}
|
|
723
838
|
function ButtonAnimatedImpl({ variant = 'solid', tone = 'primary', size = 'md', shape = 'rounded', block = false, disabled = false, loading = false, loadingMode = 'inline', pressEffect = 'auto', icon, iconPlacement = 'start', iconOnly = false, color, colors, border, layout, gradient, shadow = 'none', children, style, contentStyle, textStyle, accessibilityLabel, accessibilityState, hitSlop, onPress, onPressIn, onPressOut, testID, ...pressableProps }, ref) {
|
|
724
839
|
const resolvedLoadingMode = iconOnly ? 'overlay' : loadingMode;
|
|
725
840
|
const centeredLoading = resolvedLoadingMode !== 'inline';
|
|
@@ -752,7 +867,7 @@ function ButtonAnimatedImpl({ variant = 'solid', tone = 'primary', size = 'md',
|
|
|
752
867
|
variant,
|
|
753
868
|
}), [iconOnly, layout, metrics, variant]);
|
|
754
869
|
const pressSv = (0, react_native_reanimated_1.useSharedValue)(0);
|
|
755
|
-
const loadingSv = (0, react_native_reanimated_1.useSharedValue)(
|
|
870
|
+
const loadingSv = (0, react_native_reanimated_1.useSharedValue)(0);
|
|
756
871
|
const [spinnerMounted, setSpinnerMounted] = React.useState(loading);
|
|
757
872
|
const hideSpinnerTimerRef = React.useRef(null);
|
|
758
873
|
const resolvedPressEffect = React.useMemo(() => resolvePressEffect(pressEffect, interactionDisabled), [interactionDisabled, pressEffect]);
|
|
@@ -903,13 +1018,32 @@ function ButtonAnimatedImpl({ variant = 'solid', tone = 'primary', size = 'md',
|
|
|
903
1018
|
}
|
|
904
1019
|
const ButtonStaticWithRef = React.forwardRef(ButtonStaticImpl);
|
|
905
1020
|
ButtonStaticWithRef.displayName = 'ButtonStatic';
|
|
1021
|
+
const ButtonPressAnimatedWithRef = React.forwardRef(ButtonPressAnimatedImpl);
|
|
1022
|
+
ButtonPressAnimatedWithRef.displayName = 'ButtonPressAnimated';
|
|
906
1023
|
const ButtonAnimatedWithRef = React.forwardRef(ButtonAnimatedImpl);
|
|
907
|
-
ButtonAnimatedWithRef.displayName = '
|
|
1024
|
+
ButtonAnimatedWithRef.displayName = 'ButtonLoadingAnimated';
|
|
908
1025
|
function ButtonImpl(props, ref) {
|
|
909
|
-
|
|
910
|
-
|
|
1026
|
+
const loadingRequested = Boolean(props.loading);
|
|
1027
|
+
const animatedPressRequested = shouldRequestAnimatedPressPath(props);
|
|
1028
|
+
const [retainLoadingAnimated, setRetainLoadingAnimated] = React.useState(loadingRequested);
|
|
1029
|
+
React.useEffect(() => {
|
|
1030
|
+
if (loadingRequested) {
|
|
1031
|
+
setRetainLoadingAnimated(true);
|
|
1032
|
+
return undefined;
|
|
1033
|
+
}
|
|
1034
|
+
if (!retainLoadingAnimated)
|
|
1035
|
+
return undefined;
|
|
1036
|
+
const timer = setTimeout(() => {
|
|
1037
|
+
setRetainLoadingAnimated(false);
|
|
1038
|
+
}, ANIMATED_EXIT_RESIDENCY_MS);
|
|
1039
|
+
return () => {
|
|
1040
|
+
clearTimeout(timer);
|
|
1041
|
+
};
|
|
1042
|
+
}, [loadingRequested, retainLoadingAnimated]);
|
|
1043
|
+
if (loadingRequested || retainLoadingAnimated) {
|
|
1044
|
+
return (0, jsx_runtime_1.jsx)(ButtonAnimatedWithRef, { ...props, ref: ref });
|
|
911
1045
|
}
|
|
912
|
-
return (0, jsx_runtime_1.jsx)(
|
|
1046
|
+
return animatedPressRequested ? ((0, jsx_runtime_1.jsx)(ButtonPressAnimatedWithRef, { ...props, ref: ref })) : ((0, jsx_runtime_1.jsx)(ButtonStaticWithRef, { ...props, ref: ref }));
|
|
913
1047
|
}
|
|
914
1048
|
const ButtonWithRef = React.forwardRef(ButtonImpl);
|
|
915
1049
|
ButtonWithRef.displayName = 'Button';
|
|
@@ -943,6 +1077,9 @@ const styles = react_native_1.StyleSheet.create({
|
|
|
943
1077
|
disabledRoot: {
|
|
944
1078
|
opacity: DISABLED_OPACITY,
|
|
945
1079
|
},
|
|
1080
|
+
pressedRoot: {
|
|
1081
|
+
opacity: PRESSED_OPACITY,
|
|
1082
|
+
},
|
|
946
1083
|
spinnerBox: {
|
|
947
1084
|
alignItems: 'center',
|
|
948
1085
|
justifyContent: 'center',
|
package/package.json
CHANGED
package/src/ui/Button/README.md
CHANGED
|
@@ -154,11 +154,11 @@ export function Demo() {
|
|
|
154
154
|
}
|
|
155
155
|
```
|
|
156
156
|
|
|
157
|
-
渐变依赖 `expo-linear-gradient
|
|
157
|
+
渐变依赖 `expo-linear-gradient`,缺失时会使用第一段颜色作为稳定兜底。默认 `pressEffect="auto"` 走普通 `Pressable` 静态路径,并用原生 pressed style 做轻量透明度反馈;只有 `scale`、`scale-opacity`、`highlight`、`scale-highlight` 和 loading 过渡会升级到 Reanimated 动画路径。动静态切换由 Button 内部自动管理,loading 退出动画结束后才会回到静态路径。
|
|
158
158
|
|
|
159
159
|
## 列表高频渲染
|
|
160
160
|
|
|
161
|
-
在 `FlatList` / `FlashList` cell
|
|
161
|
+
在 `FlatList` / `FlashList` cell 中批量渲染普通按钮时,默认 `pressEffect="auto"` 已命中轻量 `Pressable + View` 路径,不会为每个 cell 挂载 Reanimated press/loading 动画结构。列表内需要更强反馈时优先使用 `pressEffect="opacity"`;只有确实需要缩放、高亮遮罩或 loading 动画时再使用对应动画配置。
|
|
162
162
|
|
|
163
163
|
## 常用 Props
|
|
164
164
|
|
package/src/ui/Button/index.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
DimensionValue,
|
|
5
5
|
GestureResponderEvent,
|
|
6
6
|
Insets,
|
|
7
|
+
PressableStateCallbackType,
|
|
7
8
|
StyleProp,
|
|
8
9
|
TextStyle,
|
|
9
10
|
ViewStyle,
|
|
@@ -201,6 +202,7 @@ const LOADING_TIMING = {
|
|
|
201
202
|
easing: Easing.out(Easing.cubic),
|
|
202
203
|
reduceMotion: ReduceMotion.System,
|
|
203
204
|
} as const;
|
|
205
|
+
const ANIMATED_EXIT_RESIDENCY_MS = LOADING_TIMING.duration + 40;
|
|
204
206
|
|
|
205
207
|
const DISABLED_OPACITY = 0.52;
|
|
206
208
|
const PRESSED_OPACITY = 0.82;
|
|
@@ -574,7 +576,7 @@ function resolveVisualColors({
|
|
|
574
576
|
function resolvePressEffect(effect: ButtonPressEffect, interactionDisabled: boolean): ButtonPressEffect {
|
|
575
577
|
if (interactionDisabled) return 'none';
|
|
576
578
|
if (effect !== 'auto') return effect;
|
|
577
|
-
return '
|
|
579
|
+
return 'opacity';
|
|
578
580
|
}
|
|
579
581
|
|
|
580
582
|
function resolvePressEffectFlags(effect: ButtonPressEffect): PressEffectFlags {
|
|
@@ -837,16 +839,24 @@ function resolveWebCursorStyle(disabled: boolean): ViewStyle | undefined {
|
|
|
837
839
|
return { cursor: disabled ? 'not-allowed' : 'pointer' } as ViewStyle;
|
|
838
840
|
}
|
|
839
841
|
|
|
840
|
-
function
|
|
842
|
+
function requiresAnimatedPressEffect(effect: ButtonPressEffect | undefined) {
|
|
843
|
+
return (
|
|
844
|
+
effect === 'scale' ||
|
|
845
|
+
effect === 'scale-opacity' ||
|
|
846
|
+
effect === 'highlight' ||
|
|
847
|
+
effect === 'scale-highlight'
|
|
848
|
+
);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
function shouldRequestAnimatedPressPath({
|
|
841
852
|
disabled,
|
|
842
|
-
loading,
|
|
843
853
|
pressEffect,
|
|
844
854
|
}: {
|
|
845
855
|
disabled?: boolean;
|
|
846
|
-
loading?: boolean;
|
|
847
856
|
pressEffect?: ButtonPressEffect;
|
|
848
857
|
}) {
|
|
849
|
-
|
|
858
|
+
if (disabled) return false;
|
|
859
|
+
return requiresAnimatedPressEffect(pressEffect);
|
|
850
860
|
}
|
|
851
861
|
|
|
852
862
|
function useButtonFrame({
|
|
@@ -1079,7 +1089,7 @@ function ButtonStaticImpl(
|
|
|
1079
1089
|
disabled = false,
|
|
1080
1090
|
loading = false,
|
|
1081
1091
|
loadingMode: _loadingMode = 'inline',
|
|
1082
|
-
pressEffect
|
|
1092
|
+
pressEffect = 'auto',
|
|
1083
1093
|
icon,
|
|
1084
1094
|
iconPlacement = 'start',
|
|
1085
1095
|
iconOnly = false,
|
|
@@ -1151,6 +1161,28 @@ function ButtonStaticImpl(
|
|
|
1151
1161
|
},
|
|
1152
1162
|
[interactionDisabled, onPress]
|
|
1153
1163
|
);
|
|
1164
|
+
const pressOpacityEnabled = !interactionDisabled && (pressEffect === 'auto' || pressEffect === 'opacity');
|
|
1165
|
+
const rootStyle = React.useCallback(
|
|
1166
|
+
({ pressed }: PressableStateCallbackType) => [
|
|
1167
|
+
styles.root,
|
|
1168
|
+
rootSizeStyle,
|
|
1169
|
+
radiusStyle,
|
|
1170
|
+
shadowStyle,
|
|
1171
|
+
visualDisabled ? styles.disabledRoot : null,
|
|
1172
|
+
pressOpacityEnabled && pressed ? styles.pressedRoot : null,
|
|
1173
|
+
webCursorStyle,
|
|
1174
|
+
style,
|
|
1175
|
+
],
|
|
1176
|
+
[
|
|
1177
|
+
pressOpacityEnabled,
|
|
1178
|
+
radiusStyle,
|
|
1179
|
+
rootSizeStyle,
|
|
1180
|
+
shadowStyle,
|
|
1181
|
+
style,
|
|
1182
|
+
visualDisabled,
|
|
1183
|
+
webCursorStyle,
|
|
1184
|
+
]
|
|
1185
|
+
);
|
|
1154
1186
|
|
|
1155
1187
|
return (
|
|
1156
1188
|
<Pressable
|
|
@@ -1166,15 +1198,7 @@ function ButtonStaticImpl(
|
|
|
1166
1198
|
disabled={interactionDisabled}
|
|
1167
1199
|
hitSlop={hitSlop ?? defaultHitSlop}
|
|
1168
1200
|
onPress={handlePress}
|
|
1169
|
-
style={
|
|
1170
|
-
styles.root,
|
|
1171
|
-
rootSizeStyle,
|
|
1172
|
-
radiusStyle,
|
|
1173
|
-
shadowStyle,
|
|
1174
|
-
visualDisabled ? styles.disabledRoot : null,
|
|
1175
|
-
webCursorStyle,
|
|
1176
|
-
style,
|
|
1177
|
-
]}
|
|
1201
|
+
style={rootStyle}
|
|
1178
1202
|
testID={testID}
|
|
1179
1203
|
>
|
|
1180
1204
|
<View
|
|
@@ -1217,6 +1241,235 @@ function ButtonStaticImpl(
|
|
|
1217
1241
|
);
|
|
1218
1242
|
}
|
|
1219
1243
|
|
|
1244
|
+
function ButtonPressAnimatedImpl(
|
|
1245
|
+
{
|
|
1246
|
+
variant = 'solid',
|
|
1247
|
+
tone = 'primary',
|
|
1248
|
+
size = 'md',
|
|
1249
|
+
shape = 'rounded',
|
|
1250
|
+
block = false,
|
|
1251
|
+
disabled = false,
|
|
1252
|
+
loading = false,
|
|
1253
|
+
loadingMode: _loadingMode = 'inline',
|
|
1254
|
+
pressEffect = 'auto',
|
|
1255
|
+
icon,
|
|
1256
|
+
iconPlacement = 'start',
|
|
1257
|
+
iconOnly = false,
|
|
1258
|
+
color,
|
|
1259
|
+
colors,
|
|
1260
|
+
border,
|
|
1261
|
+
layout,
|
|
1262
|
+
gradient,
|
|
1263
|
+
shadow = 'none',
|
|
1264
|
+
children,
|
|
1265
|
+
style,
|
|
1266
|
+
contentStyle,
|
|
1267
|
+
textStyle,
|
|
1268
|
+
accessibilityLabel,
|
|
1269
|
+
accessibilityState,
|
|
1270
|
+
hitSlop,
|
|
1271
|
+
onPress,
|
|
1272
|
+
onPressIn,
|
|
1273
|
+
onPressOut,
|
|
1274
|
+
testID,
|
|
1275
|
+
...pressableProps
|
|
1276
|
+
}: ButtonProps,
|
|
1277
|
+
ref: React.ForwardedRef<ButtonRef>
|
|
1278
|
+
) {
|
|
1279
|
+
const frame = useButtonFrame({
|
|
1280
|
+
accessibilityLabel,
|
|
1281
|
+
block,
|
|
1282
|
+
border,
|
|
1283
|
+
children,
|
|
1284
|
+
color,
|
|
1285
|
+
colors,
|
|
1286
|
+
disabled,
|
|
1287
|
+
gradient,
|
|
1288
|
+
icon,
|
|
1289
|
+
iconOnly,
|
|
1290
|
+
iconPlacement,
|
|
1291
|
+
layout,
|
|
1292
|
+
loading,
|
|
1293
|
+
shadow,
|
|
1294
|
+
shape,
|
|
1295
|
+
size,
|
|
1296
|
+
textStyle,
|
|
1297
|
+
tone,
|
|
1298
|
+
variant,
|
|
1299
|
+
});
|
|
1300
|
+
const {
|
|
1301
|
+
accentColor,
|
|
1302
|
+
borderStyle,
|
|
1303
|
+
contentSizeStyle,
|
|
1304
|
+
defaultHitSlop,
|
|
1305
|
+
gradientCfg,
|
|
1306
|
+
hasGradientBackground,
|
|
1307
|
+
hasIcon,
|
|
1308
|
+
iconBaseBoxStyle,
|
|
1309
|
+
iconPlacedBoxStyle,
|
|
1310
|
+
interactionDisabled,
|
|
1311
|
+
LinearGradientComponent,
|
|
1312
|
+
paddingStyle,
|
|
1313
|
+
radiusStyle,
|
|
1314
|
+
resolvedAccessibilityLabel,
|
|
1315
|
+
resolvedText,
|
|
1316
|
+
rootSizeStyle,
|
|
1317
|
+
shadowStyle,
|
|
1318
|
+
visualColors,
|
|
1319
|
+
visualDisabled,
|
|
1320
|
+
webCursorStyle,
|
|
1321
|
+
} = frame;
|
|
1322
|
+
|
|
1323
|
+
const pressSv = useSharedValue(0);
|
|
1324
|
+
const resolvedPressEffect = React.useMemo(
|
|
1325
|
+
() => resolvePressEffect(pressEffect, interactionDisabled),
|
|
1326
|
+
[interactionDisabled, pressEffect]
|
|
1327
|
+
);
|
|
1328
|
+
const pressEffectFlags = React.useMemo(
|
|
1329
|
+
() => resolvePressEffectFlags(resolvedPressEffect),
|
|
1330
|
+
[resolvedPressEffect]
|
|
1331
|
+
);
|
|
1332
|
+
const pressHighlightEnabled = pressEffectFlags.highlight;
|
|
1333
|
+
const pressOpacityEnabled = pressEffectFlags.opacity;
|
|
1334
|
+
const pressScaleEnabled = pressEffectFlags.scale;
|
|
1335
|
+
const pressOverlay = React.useMemo(
|
|
1336
|
+
() =>
|
|
1337
|
+
resolveOverlay({
|
|
1338
|
+
accentColor,
|
|
1339
|
+
colors,
|
|
1340
|
+
hasGradientBackground,
|
|
1341
|
+
variant,
|
|
1342
|
+
}),
|
|
1343
|
+
[accentColor, colors, hasGradientBackground, variant]
|
|
1344
|
+
);
|
|
1345
|
+
|
|
1346
|
+
React.useEffect(() => {
|
|
1347
|
+
if (interactionDisabled || resolvedPressEffect === 'none') {
|
|
1348
|
+
pressSv.value = 0;
|
|
1349
|
+
}
|
|
1350
|
+
}, [interactionDisabled, pressSv, resolvedPressEffect]);
|
|
1351
|
+
|
|
1352
|
+
const rootAnimatedStyle = useAnimatedStyle(() => {
|
|
1353
|
+
const baseOpacity = visualDisabled ? DISABLED_OPACITY : 1;
|
|
1354
|
+
const scale = pressScaleEnabled
|
|
1355
|
+
? interpolate(pressSv.value, [0, 1], [1, PRESSED_SCALE])
|
|
1356
|
+
: 1;
|
|
1357
|
+
const opacity = pressOpacityEnabled
|
|
1358
|
+
? interpolate(pressSv.value, [0, 1], [baseOpacity, PRESSED_OPACITY])
|
|
1359
|
+
: baseOpacity;
|
|
1360
|
+
|
|
1361
|
+
return {
|
|
1362
|
+
opacity,
|
|
1363
|
+
transform: [{ scale }],
|
|
1364
|
+
};
|
|
1365
|
+
}, [pressOpacityEnabled, pressScaleEnabled, visualDisabled]);
|
|
1366
|
+
|
|
1367
|
+
const overlayAnimatedStyle = useAnimatedStyle(() => {
|
|
1368
|
+
const opacity = pressHighlightEnabled
|
|
1369
|
+
? interpolate(pressSv.value, [0, 1], [0, pressOverlay.strength])
|
|
1370
|
+
: 0;
|
|
1371
|
+
return { opacity };
|
|
1372
|
+
}, [pressHighlightEnabled, pressOverlay.strength]);
|
|
1373
|
+
|
|
1374
|
+
const handlePress = React.useCallback(
|
|
1375
|
+
(event: GestureResponderEvent) => {
|
|
1376
|
+
if (interactionDisabled) return;
|
|
1377
|
+
onPress?.(event);
|
|
1378
|
+
},
|
|
1379
|
+
[interactionDisabled, onPress]
|
|
1380
|
+
);
|
|
1381
|
+
|
|
1382
|
+
const handlePressIn = React.useCallback(
|
|
1383
|
+
(event: GestureResponderEvent) => {
|
|
1384
|
+
if (!interactionDisabled && resolvedPressEffect !== 'none') {
|
|
1385
|
+
pressSv.value = withTiming(1, PRESS_TIMING);
|
|
1386
|
+
}
|
|
1387
|
+
onPressIn?.(event);
|
|
1388
|
+
},
|
|
1389
|
+
[interactionDisabled, onPressIn, pressSv, resolvedPressEffect]
|
|
1390
|
+
);
|
|
1391
|
+
|
|
1392
|
+
const handlePressOut = React.useCallback(
|
|
1393
|
+
(event: GestureResponderEvent) => {
|
|
1394
|
+
if (!interactionDisabled && resolvedPressEffect !== 'none') {
|
|
1395
|
+
pressSv.value = withTiming(0, PRESS_TIMING);
|
|
1396
|
+
}
|
|
1397
|
+
onPressOut?.(event);
|
|
1398
|
+
},
|
|
1399
|
+
[interactionDisabled, onPressOut, pressSv, resolvedPressEffect]
|
|
1400
|
+
);
|
|
1401
|
+
|
|
1402
|
+
return (
|
|
1403
|
+
<AnimatedPressable
|
|
1404
|
+
{...pressableProps}
|
|
1405
|
+
ref={ref}
|
|
1406
|
+
accessibilityLabel={resolvedAccessibilityLabel}
|
|
1407
|
+
accessibilityRole="button"
|
|
1408
|
+
accessibilityState={{
|
|
1409
|
+
...accessibilityState,
|
|
1410
|
+
busy: Boolean(loading || accessibilityState?.busy),
|
|
1411
|
+
disabled: Boolean(interactionDisabled || accessibilityState?.disabled),
|
|
1412
|
+
}}
|
|
1413
|
+
disabled={interactionDisabled}
|
|
1414
|
+
hitSlop={hitSlop ?? defaultHitSlop}
|
|
1415
|
+
onPress={handlePress}
|
|
1416
|
+
onPressIn={handlePressIn}
|
|
1417
|
+
onPressOut={handlePressOut}
|
|
1418
|
+
style={[styles.root, rootSizeStyle, radiusStyle, shadowStyle, rootAnimatedStyle, webCursorStyle, style]}
|
|
1419
|
+
testID={testID}
|
|
1420
|
+
>
|
|
1421
|
+
<View
|
|
1422
|
+
pointerEvents="none"
|
|
1423
|
+
style={[
|
|
1424
|
+
styles.content,
|
|
1425
|
+
contentSizeStyle,
|
|
1426
|
+
paddingStyle,
|
|
1427
|
+
radiusStyle,
|
|
1428
|
+
borderStyle,
|
|
1429
|
+
{ backgroundColor: visualColors.backgroundColor },
|
|
1430
|
+
contentStyle,
|
|
1431
|
+
]}
|
|
1432
|
+
>
|
|
1433
|
+
{LinearGradientComponent && gradientCfg ? (
|
|
1434
|
+
<LinearGradientComponent
|
|
1435
|
+
pointerEvents="none"
|
|
1436
|
+
colors={gradientCfg.colors}
|
|
1437
|
+
start={gradientCfg.start}
|
|
1438
|
+
end={gradientCfg.end}
|
|
1439
|
+
style={[StyleSheet.absoluteFillObject, radiusStyle]}
|
|
1440
|
+
/>
|
|
1441
|
+
) : null}
|
|
1442
|
+
|
|
1443
|
+
{pressHighlightEnabled ? (
|
|
1444
|
+
<Animated.View
|
|
1445
|
+
pointerEvents="none"
|
|
1446
|
+
style={[
|
|
1447
|
+
StyleSheet.absoluteFillObject,
|
|
1448
|
+
radiusStyle,
|
|
1449
|
+
{ backgroundColor: pressOverlay.color },
|
|
1450
|
+
overlayAnimatedStyle,
|
|
1451
|
+
]}
|
|
1452
|
+
/>
|
|
1453
|
+
) : null}
|
|
1454
|
+
|
|
1455
|
+
{iconOnly ? (
|
|
1456
|
+
(icon ?? resolvedText)
|
|
1457
|
+
) : (
|
|
1458
|
+
<>
|
|
1459
|
+
{icon && iconPlacement === 'start' ? (
|
|
1460
|
+
<View style={[iconBaseBoxStyle, iconPlacedBoxStyle]}>{icon}</View>
|
|
1461
|
+
) : null}
|
|
1462
|
+
{resolvedText}
|
|
1463
|
+
{icon && iconPlacement === 'end' ? (
|
|
1464
|
+
<View style={[iconBaseBoxStyle, iconPlacedBoxStyle]}>{icon}</View>
|
|
1465
|
+
) : null}
|
|
1466
|
+
</>
|
|
1467
|
+
)}
|
|
1468
|
+
</View>
|
|
1469
|
+
</AnimatedPressable>
|
|
1470
|
+
);
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1220
1473
|
function ButtonAnimatedImpl(
|
|
1221
1474
|
{
|
|
1222
1475
|
variant = 'solid',
|
|
@@ -1314,7 +1567,7 @@ function ButtonAnimatedImpl(
|
|
|
1314
1567
|
);
|
|
1315
1568
|
|
|
1316
1569
|
const pressSv = useSharedValue(0);
|
|
1317
|
-
const loadingSv = useSharedValue(
|
|
1570
|
+
const loadingSv = useSharedValue(0);
|
|
1318
1571
|
const [spinnerMounted, setSpinnerMounted] = React.useState(loading);
|
|
1319
1572
|
const hideSpinnerTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
1320
1573
|
|
|
@@ -1602,15 +1855,43 @@ function ButtonAnimatedImpl(
|
|
|
1602
1855
|
const ButtonStaticWithRef = React.forwardRef<ButtonRef, ButtonProps>(ButtonStaticImpl);
|
|
1603
1856
|
ButtonStaticWithRef.displayName = 'ButtonStatic';
|
|
1604
1857
|
|
|
1858
|
+
const ButtonPressAnimatedWithRef = React.forwardRef<ButtonRef, ButtonProps>(ButtonPressAnimatedImpl);
|
|
1859
|
+
ButtonPressAnimatedWithRef.displayName = 'ButtonPressAnimated';
|
|
1860
|
+
|
|
1605
1861
|
const ButtonAnimatedWithRef = React.forwardRef<ButtonRef, ButtonProps>(ButtonAnimatedImpl);
|
|
1606
|
-
ButtonAnimatedWithRef.displayName = '
|
|
1862
|
+
ButtonAnimatedWithRef.displayName = 'ButtonLoadingAnimated';
|
|
1607
1863
|
|
|
1608
1864
|
function ButtonImpl(props: ButtonProps, ref: React.ForwardedRef<ButtonRef>) {
|
|
1609
|
-
|
|
1610
|
-
|
|
1865
|
+
const loadingRequested = Boolean(props.loading);
|
|
1866
|
+
const animatedPressRequested = shouldRequestAnimatedPressPath(props);
|
|
1867
|
+
const [retainLoadingAnimated, setRetainLoadingAnimated] = React.useState(loadingRequested);
|
|
1868
|
+
|
|
1869
|
+
React.useEffect(() => {
|
|
1870
|
+
if (loadingRequested) {
|
|
1871
|
+
setRetainLoadingAnimated(true);
|
|
1872
|
+
return undefined;
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
if (!retainLoadingAnimated) return undefined;
|
|
1876
|
+
|
|
1877
|
+
const timer = setTimeout(() => {
|
|
1878
|
+
setRetainLoadingAnimated(false);
|
|
1879
|
+
}, ANIMATED_EXIT_RESIDENCY_MS);
|
|
1880
|
+
|
|
1881
|
+
return () => {
|
|
1882
|
+
clearTimeout(timer);
|
|
1883
|
+
};
|
|
1884
|
+
}, [loadingRequested, retainLoadingAnimated]);
|
|
1885
|
+
|
|
1886
|
+
if (loadingRequested || retainLoadingAnimated) {
|
|
1887
|
+
return <ButtonAnimatedWithRef {...props} ref={ref} />;
|
|
1611
1888
|
}
|
|
1612
1889
|
|
|
1613
|
-
return
|
|
1890
|
+
return animatedPressRequested ? (
|
|
1891
|
+
<ButtonPressAnimatedWithRef {...props} ref={ref} />
|
|
1892
|
+
) : (
|
|
1893
|
+
<ButtonStaticWithRef {...props} ref={ref} />
|
|
1894
|
+
);
|
|
1614
1895
|
}
|
|
1615
1896
|
|
|
1616
1897
|
const ButtonWithRef = React.forwardRef<ButtonRef, ButtonProps>(ButtonImpl);
|
|
@@ -1647,6 +1928,9 @@ const styles = StyleSheet.create({
|
|
|
1647
1928
|
disabledRoot: {
|
|
1648
1929
|
opacity: DISABLED_OPACITY,
|
|
1649
1930
|
},
|
|
1931
|
+
pressedRoot: {
|
|
1932
|
+
opacity: PRESSED_OPACITY,
|
|
1933
|
+
},
|
|
1650
1934
|
spinnerBox: {
|
|
1651
1935
|
alignItems: 'center',
|
|
1652
1936
|
justifyContent: 'center',
|