@cyberkaidev/ui 0.1.2 → 0.1.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/lib/module/components/Button.js +10 -13
- package/lib/module/components/Button.js.map +1 -1
- package/lib/module/components/RippleEffect.js +35 -38
- package/lib/module/components/RippleEffect.js.map +1 -1
- package/lib/typescript/src/components/Button.d.ts +7 -6
- package/lib/typescript/src/components/Button.d.ts.map +1 -1
- package/lib/typescript/src/components/RippleEffect.d.ts +2 -1
- package/lib/typescript/src/components/RippleEffect.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/Button.tsx +13 -15
- package/src/components/RippleEffect.tsx +51 -50
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import { Platform,
|
|
4
|
-
import {
|
|
3
|
+
import { Platform, TouchableOpacity } from 'react-native';
|
|
4
|
+
import { RippleEffect } from "./RippleEffect.js";
|
|
5
5
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
6
6
|
/**
|
|
7
|
-
* Base pressable. Android gets
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* resolution works without `moduleSuffixes`.
|
|
7
|
+
* Base pressable. Android gets a touch ripple; every other platform gets
|
|
8
|
+
* opacity feedback. Kept in one file (not `.ios`/`.android`) so consumers'
|
|
9
|
+
* type resolution works without `moduleSuffixes`.
|
|
11
10
|
*/
|
|
12
11
|
export function Button({
|
|
13
12
|
children,
|
|
@@ -18,11 +17,11 @@ export function Button({
|
|
|
18
17
|
style,
|
|
19
18
|
testID,
|
|
20
19
|
hitSlop,
|
|
21
|
-
rippleColor
|
|
20
|
+
rippleColor,
|
|
21
|
+
rippleContainerBorderRadius
|
|
22
22
|
}) {
|
|
23
|
-
const colors = useColors();
|
|
24
23
|
if (Platform.OS === 'android') {
|
|
25
|
-
return /*#__PURE__*/_jsx(
|
|
24
|
+
return /*#__PURE__*/_jsx(RippleEffect, {
|
|
26
25
|
testID: testID,
|
|
27
26
|
style: style,
|
|
28
27
|
disabled: disabled,
|
|
@@ -30,10 +29,8 @@ export function Button({
|
|
|
30
29
|
onPressIn: onPressIn,
|
|
31
30
|
onPressOut: onPressOut,
|
|
32
31
|
hitSlop: hitSlop,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
foreground: true
|
|
36
|
-
},
|
|
32
|
+
rippleColor: rippleColor,
|
|
33
|
+
rippleContainerBorderRadius: rippleContainerBorderRadius,
|
|
37
34
|
children: children
|
|
38
35
|
});
|
|
39
36
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["Platform","
|
|
1
|
+
{"version":3,"names":["Platform","TouchableOpacity","RippleEffect","jsx","_jsx","Button","children","onPress","onPressIn","onPressOut","disabled","style","testID","hitSlop","rippleColor","rippleContainerBorderRadius","OS"],"sourceRoot":"../../../src","sources":["components/Button.tsx"],"mappings":";;AACA,SAASA,QAAQ,EAAEC,gBAAgB,QAAQ,cAAc;AAGzD,SAASC,YAAY,QAAQ,mBAAgB;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAiB9C;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,MAAMA,CAAC;EACrBC,QAAQ;EACRC,OAAO;EACPC,SAAS;EACTC,UAAU;EACVC,QAAQ;EACRC,KAAK;EACLC,MAAM;EACNC,OAAO;EACPC,WAAW;EACXC;AACK,CAAC,EAAE;EACR,IAAIf,QAAQ,CAACgB,EAAE,KAAK,SAAS,EAAE;IAC7B,oBACEZ,IAAA,CAACF,YAAY;MACXU,MAAM,EAAEA,MAAO;MACfD,KAAK,EAAEA,KAAM;MACbD,QAAQ,EAAEA,QAAS;MACnBH,OAAO,EAAEA,OAAQ;MACjBC,SAAS,EAAEA,SAAU;MACrBC,UAAU,EAAEA,UAAW;MACvBI,OAAO,EAAEA,OAAQ;MACjBC,WAAW,EAAEA,WAAY;MACzBC,2BAA2B,EAAEA,2BAA4B;MAAAT,QAAA,EAExDA;IAAQ,CACG,CAAC;EAEnB;EAEA,oBACEF,IAAA,CAACH,gBAAgB;IACfW,MAAM,EAAEA,MAAO;IACfD,KAAK,EAAEA,KAAM;IACbD,QAAQ,EAAEA,QAAS;IACnBH,OAAO,EAAEA,OAAQ;IACjBC,SAAS,EAAEA,SAAU;IACrBC,UAAU,EAAEA,UAAW;IACvBI,OAAO,EAAEA,OAAQ;IAAAP,QAAA,EAEhBA;EAAQ,CACO,CAAC;AAEvB","ignoreList":[]}
|
|
@@ -9,6 +9,13 @@ const RELEASE_GROW_DURATION = 150;
|
|
|
9
9
|
const RELEASE_FADE_DURATION = 250;
|
|
10
10
|
const PRESS_FADE_IN_DURATION = 75;
|
|
11
11
|
|
|
12
|
+
// The ripple animations run on the JS driver on purpose. On the New
|
|
13
|
+
// Architecture a native-driven animation started in the same tick the
|
|
14
|
+
// `Animated.View` is created has no native view to bind to yet, so it never
|
|
15
|
+
// plays — which is why the ripple showed on RN 0.83 but not on 0.86+.
|
|
16
|
+
const USE_NATIVE_DRIVER = false;
|
|
17
|
+
const toFinite = (value, fallback) => Number.isFinite(value) ? value : fallback;
|
|
18
|
+
|
|
12
19
|
/** Wraps a child and paints a touch ripple at the press location. */
|
|
13
20
|
export function RippleEffect({
|
|
14
21
|
children,
|
|
@@ -54,17 +61,19 @@ export function RippleEffect({
|
|
|
54
61
|
const w2 = 0.5 * layout.width;
|
|
55
62
|
const h2 = 0.5 * layout.height;
|
|
56
63
|
|
|
57
|
-
// Fall back to the center when the gesture carries no coordinates
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
// Fall back to the center when the gesture carries no coordinates, and
|
|
65
|
+
// clamp to the pressable's box — some setups report press coordinates
|
|
66
|
+
// relative to the wrong ancestor, which would push the circle off-screen
|
|
67
|
+
// where `overflow: 'hidden'` clips it away entirely.
|
|
68
|
+
const centered = rippleCentered || !event?.nativeEvent;
|
|
69
|
+
const clampX = value => layout.width > 0 ? Math.min(Math.max(value, 0), layout.width) : value;
|
|
70
|
+
const clampY = value => layout.height > 0 ? Math.min(Math.max(value, 0), layout.height) : value;
|
|
71
|
+
const locationX = centered ? w2 : clampX(toFinite(event.nativeEvent.locationX, w2));
|
|
72
|
+
const locationY = centered ? h2 : clampY(toFinite(event.nativeEvent.locationY, h2));
|
|
65
73
|
const offsetX = Math.abs(w2 - locationX);
|
|
66
74
|
const offsetY = Math.abs(h2 - locationY);
|
|
67
|
-
const
|
|
75
|
+
const computedRadius = rippleSize > 0 ? 0.5 * rippleSize : Math.sqrt((w2 + offsetX) ** 2 + (h2 + offsetY) ** 2);
|
|
76
|
+
const radius = Number.isFinite(computedRadius) && computedRadius > 0 ? computedRadius : Math.max(w2, h2, CIRCLE_RADIUS);
|
|
68
77
|
const ripple = {
|
|
69
78
|
unique: uniqueRef.current++,
|
|
70
79
|
locationX,
|
|
@@ -75,13 +84,19 @@ export function RippleEffect({
|
|
|
75
84
|
};
|
|
76
85
|
ripplesRef.current.set(ripple.unique, ripple);
|
|
77
86
|
activeRippleRef.current = ripple.unique;
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
87
|
+
Animated.timing(ripple.scale, {
|
|
88
|
+
toValue: 1,
|
|
89
|
+
duration: rippleDuration,
|
|
90
|
+
easing: Easing.out(Easing.ease),
|
|
91
|
+
useNativeDriver: USE_NATIVE_DRIVER
|
|
92
|
+
}).start();
|
|
93
|
+
Animated.timing(ripple.opacity, {
|
|
94
|
+
toValue: rippleOpacity,
|
|
95
|
+
duration: Math.min(rippleDuration, PRESS_FADE_IN_DURATION),
|
|
96
|
+
useNativeDriver: USE_NATIVE_DRIVER
|
|
97
|
+
}).start();
|
|
83
98
|
setRippleIds(current => current.concat(ripple.unique));
|
|
84
|
-
}, [layout.width, layout.height, onPressIn, rippleCentered, rippleSequential, rippleSize]);
|
|
99
|
+
}, [layout.width, layout.height, onPressIn, rippleCentered, rippleDuration, rippleOpacity, rippleSequential, rippleSize]);
|
|
85
100
|
const handlePressOut = React.useCallback(event => {
|
|
86
101
|
onPressOut?.(event);
|
|
87
102
|
const releasedUnique = activeRippleRef.current;
|
|
@@ -100,11 +115,11 @@ export function RippleEffect({
|
|
|
100
115
|
Animated.parallel([Animated.timing(ripple.scale, {
|
|
101
116
|
toValue: 1,
|
|
102
117
|
duration: RELEASE_GROW_DURATION,
|
|
103
|
-
useNativeDriver:
|
|
118
|
+
useNativeDriver: USE_NATIVE_DRIVER
|
|
104
119
|
}), Animated.timing(ripple.opacity, {
|
|
105
120
|
toValue: 0,
|
|
106
121
|
duration: RELEASE_FADE_DURATION,
|
|
107
|
-
useNativeDriver:
|
|
122
|
+
useNativeDriver: USE_NATIVE_DRIVER
|
|
108
123
|
})]).start(removeRipple);
|
|
109
124
|
}, [onPressOut, rippleFades]);
|
|
110
125
|
return /*#__PURE__*/_jsxs(Pressable, {
|
|
@@ -117,6 +132,7 @@ export function RippleEffect({
|
|
|
117
132
|
style: style,
|
|
118
133
|
children: children
|
|
119
134
|
}), /*#__PURE__*/_jsx(View, {
|
|
135
|
+
collapsable: false,
|
|
120
136
|
pointerEvents: "none",
|
|
121
137
|
style: [styles.container, {
|
|
122
138
|
borderRadius: rippleContainerBorderRadius
|
|
@@ -126,9 +142,7 @@ export function RippleEffect({
|
|
|
126
142
|
if (!ripple) return null;
|
|
127
143
|
return /*#__PURE__*/_jsx(RippleCircle, {
|
|
128
144
|
ripple: ripple,
|
|
129
|
-
color: color
|
|
130
|
-
duration: rippleDuration,
|
|
131
|
-
toOpacity: rippleOpacity
|
|
145
|
+
color: color
|
|
132
146
|
}, id);
|
|
133
147
|
})
|
|
134
148
|
})]
|
|
@@ -136,9 +150,7 @@ export function RippleEffect({
|
|
|
136
150
|
}
|
|
137
151
|
function RippleCircleComponent({
|
|
138
152
|
ripple,
|
|
139
|
-
color
|
|
140
|
-
duration,
|
|
141
|
-
toOpacity
|
|
153
|
+
color
|
|
142
154
|
}) {
|
|
143
155
|
const {
|
|
144
156
|
locationX,
|
|
@@ -147,21 +159,6 @@ function RippleCircleComponent({
|
|
|
147
159
|
scale,
|
|
148
160
|
opacity
|
|
149
161
|
} = ripple;
|
|
150
|
-
React.useEffect(() => {
|
|
151
|
-
Animated.timing(scale, {
|
|
152
|
-
toValue: 1,
|
|
153
|
-
duration,
|
|
154
|
-
easing: Easing.out(Easing.ease),
|
|
155
|
-
useNativeDriver: true
|
|
156
|
-
}).start();
|
|
157
|
-
Animated.timing(opacity, {
|
|
158
|
-
toValue: toOpacity,
|
|
159
|
-
duration: Math.min(duration, PRESS_FADE_IN_DURATION),
|
|
160
|
-
useNativeDriver: true
|
|
161
|
-
}).start();
|
|
162
|
-
// Entrance runs exactly once, when the ripple view mounts.
|
|
163
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
164
|
-
}, []);
|
|
165
162
|
const rippleStyle = {
|
|
166
163
|
top: locationY - CIRCLE_RADIUS,
|
|
167
164
|
[I18nManager.isRTL ? 'right' : 'left']: locationX - CIRCLE_RADIUS,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","Animated","Easing","I18nManager","Pressable","StyleSheet","View","useColors","jsx","_jsx","jsxs","_jsxs","CIRCLE_RADIUS","RELEASE_GROW_DURATION","RELEASE_FADE_DURATION","PRESS_FADE_IN_DURATION","RippleEffect","children","rippleColor","rippleOpacity","rippleDuration","rippleSize","rippleContainerBorderRadius","rippleCentered","rippleSequential","rippleFades","disabled","style","onLayout","onPressIn","onPressOut","pressableProps","colors","color","text","layout","setLayout","useState","width","height","rippleIds","setRippleIds","ripplesRef","useRef","Map","activeRippleRef","uniqueRef","handleLayout","event","nativeEvent","handlePressIn","useCallback","current","size","w2","h2","locationX","locationY","offsetX","
|
|
1
|
+
{"version":3,"names":["React","Animated","Easing","I18nManager","Pressable","StyleSheet","View","useColors","jsx","_jsx","jsxs","_jsxs","CIRCLE_RADIUS","RELEASE_GROW_DURATION","RELEASE_FADE_DURATION","PRESS_FADE_IN_DURATION","USE_NATIVE_DRIVER","toFinite","value","fallback","Number","isFinite","RippleEffect","children","rippleColor","rippleOpacity","rippleDuration","rippleSize","rippleContainerBorderRadius","rippleCentered","rippleSequential","rippleFades","disabled","style","onLayout","onPressIn","onPressOut","pressableProps","colors","color","text","layout","setLayout","useState","width","height","rippleIds","setRippleIds","ripplesRef","useRef","Map","activeRippleRef","uniqueRef","handleLayout","event","nativeEvent","handlePressIn","useCallback","current","size","w2","h2","centered","clampX","Math","min","max","clampY","locationX","locationY","offsetX","abs","offsetY","computedRadius","sqrt","radius","ripple","unique","scale","Value","opacity","set","timing","toValue","duration","easing","out","ease","useNativeDriver","start","concat","handlePressOut","releasedUnique","get","removeRipple","delete","filter","id","parallel","collapsable","pointerEvents","styles","container","borderRadius","map","RippleCircle","RippleCircleComponent","rippleStyle","top","isRTL","backgroundColor","transform","interpolate","inputRange","outputRange","circle","memo","create","absoluteFillObject","overflow","position"],"sourceRoot":"../../../src","sources":["components/RippleEffect.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -9,15 +9,16 @@ type Props = {
|
|
|
9
9
|
style?: StyleProp<ViewStyle>;
|
|
10
10
|
testID?: string;
|
|
11
11
|
hitSlop?: Insets;
|
|
12
|
-
/** Android only.
|
|
12
|
+
/** Android only. */
|
|
13
13
|
rippleColor?: string;
|
|
14
|
+
/** Android only. */
|
|
15
|
+
rippleContainerBorderRadius?: number;
|
|
14
16
|
};
|
|
15
17
|
/**
|
|
16
|
-
* Base pressable. Android gets
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* resolution works without `moduleSuffixes`.
|
|
18
|
+
* Base pressable. Android gets a touch ripple; every other platform gets
|
|
19
|
+
* opacity feedback. Kept in one file (not `.ios`/`.android`) so consumers'
|
|
20
|
+
* type resolution works without `moduleSuffixes`.
|
|
20
21
|
*/
|
|
21
|
-
export declare function Button({ children, onPress, onPressIn, onPressOut, disabled, style, testID, hitSlop, rippleColor, }: Props): import("react").JSX.Element;
|
|
22
|
+
export declare function Button({ children, onPress, onPressIn, onPressOut, disabled, style, testID, hitSlop, rippleColor, rippleContainerBorderRadius, }: Props): import("react").JSX.Element;
|
|
22
23
|
export {};
|
|
23
24
|
//# sourceMappingURL=Button.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../../../../src/components/Button.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAIjE,KAAK,KAAK,GAAG;IACX,QAAQ,EAAE,SAAS,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,
|
|
1
|
+
{"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../../../../src/components/Button.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAIjE,KAAK,KAAK,GAAG;IACX,QAAQ,EAAE,SAAS,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oBAAoB;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB;IACpB,2BAA2B,CAAC,EAAE,MAAM,CAAC;CACtC,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,EACrB,QAAQ,EACR,OAAO,EACP,SAAS,EACT,UAAU,EACV,QAAQ,EACR,KAAK,EACL,MAAM,EACN,OAAO,EACP,WAAW,EACX,2BAA2B,GAC5B,EAAE,KAAK,+BAgCP"}
|
|
@@ -2,7 +2,8 @@ import React from 'react';
|
|
|
2
2
|
import type { PressableProps, StyleProp, ViewStyle } from 'react-native';
|
|
3
3
|
type Props = Omit<PressableProps, 'style'> & {
|
|
4
4
|
children: React.ReactNode;
|
|
5
|
-
/** Applied to an inner wrapper around `children`, not to the pressable
|
|
5
|
+
/** Applied to an inner wrapper around `children`, not to the pressable, so
|
|
6
|
+
* the ripple layer can never take part in the caller's flex layout. */
|
|
6
7
|
style?: StyleProp<ViewStyle>;
|
|
7
8
|
/** Ripple color. Default: theme `text`. */
|
|
8
9
|
rippleColor?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RippleEffect.d.ts","sourceRoot":"","sources":["../../../../src/components/RippleEffect.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAS1B,OAAO,KAAK,EAGV,cAAc,EACd,SAAS,EACT,SAAS,EACV,MAAM,cAAc,CAAC;AAatB,KAAK,KAAK,GAAG,IAAI,CAAC,cAAc,EAAE,OAAO,CAAC,GAAG;IAC3C,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B
|
|
1
|
+
{"version":3,"file":"RippleEffect.d.ts","sourceRoot":"","sources":["../../../../src/components/RippleEffect.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAS1B,OAAO,KAAK,EAGV,cAAc,EACd,SAAS,EACT,SAAS,EACV,MAAM,cAAc,CAAC;AAatB,KAAK,KAAK,GAAG,IAAI,CAAC,cAAc,EAAE,OAAO,CAAC,GAAG;IAC3C,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;4EACwE;IACxE,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,2CAA2C;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAgBF,qEAAqE;AACrE,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,WAAW,EACX,aAAmB,EACnB,cAAoB,EACpB,UAAc,EACd,2BAA+B,EAC/B,cAAsB,EACtB,gBAAwB,EACxB,WAAkB,EAClB,QAAgB,EAChB,KAAK,EACL,QAAQ,EACR,SAAS,EACT,UAAU,EACV,GAAG,cAAc,EAClB,EAAE,KAAK,qBA4JP"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
-
import { Platform,
|
|
2
|
+
import { Platform, TouchableOpacity } from 'react-native';
|
|
3
3
|
import type { Insets, StyleProp, ViewStyle } from 'react-native';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { RippleEffect } from './RippleEffect';
|
|
6
6
|
|
|
7
7
|
type Props = {
|
|
8
8
|
children: ReactNode;
|
|
@@ -13,15 +13,16 @@ type Props = {
|
|
|
13
13
|
style?: StyleProp<ViewStyle>;
|
|
14
14
|
testID?: string;
|
|
15
15
|
hitSlop?: Insets;
|
|
16
|
-
/** Android only.
|
|
16
|
+
/** Android only. */
|
|
17
17
|
rippleColor?: string;
|
|
18
|
+
/** Android only. */
|
|
19
|
+
rippleContainerBorderRadius?: number;
|
|
18
20
|
};
|
|
19
21
|
|
|
20
22
|
/**
|
|
21
|
-
* Base pressable. Android gets
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* resolution works without `moduleSuffixes`.
|
|
23
|
+
* Base pressable. Android gets a touch ripple; every other platform gets
|
|
24
|
+
* opacity feedback. Kept in one file (not `.ios`/`.android`) so consumers'
|
|
25
|
+
* type resolution works without `moduleSuffixes`.
|
|
25
26
|
*/
|
|
26
27
|
export function Button({
|
|
27
28
|
children,
|
|
@@ -33,12 +34,11 @@ export function Button({
|
|
|
33
34
|
testID,
|
|
34
35
|
hitSlop,
|
|
35
36
|
rippleColor,
|
|
37
|
+
rippleContainerBorderRadius,
|
|
36
38
|
}: Props) {
|
|
37
|
-
const colors = useColors();
|
|
38
|
-
|
|
39
39
|
if (Platform.OS === 'android') {
|
|
40
40
|
return (
|
|
41
|
-
<
|
|
41
|
+
<RippleEffect
|
|
42
42
|
testID={testID}
|
|
43
43
|
style={style}
|
|
44
44
|
disabled={disabled}
|
|
@@ -46,13 +46,11 @@ export function Button({
|
|
|
46
46
|
onPressIn={onPressIn}
|
|
47
47
|
onPressOut={onPressOut}
|
|
48
48
|
hitSlop={hitSlop}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
foreground: true,
|
|
52
|
-
}}
|
|
49
|
+
rippleColor={rippleColor}
|
|
50
|
+
rippleContainerBorderRadius={rippleContainerBorderRadius}
|
|
53
51
|
>
|
|
54
52
|
{children}
|
|
55
|
-
</
|
|
53
|
+
</RippleEffect>
|
|
56
54
|
);
|
|
57
55
|
}
|
|
58
56
|
|
|
@@ -28,7 +28,8 @@ type RippleItemType = {
|
|
|
28
28
|
|
|
29
29
|
type Props = Omit<PressableProps, 'style'> & {
|
|
30
30
|
children: React.ReactNode;
|
|
31
|
-
/** Applied to an inner wrapper around `children`, not to the pressable
|
|
31
|
+
/** Applied to an inner wrapper around `children`, not to the pressable, so
|
|
32
|
+
* the ripple layer can never take part in the caller's flex layout. */
|
|
32
33
|
style?: StyleProp<ViewStyle>;
|
|
33
34
|
/** Ripple color. Default: theme `text`. */
|
|
34
35
|
rippleColor?: string;
|
|
@@ -46,6 +47,15 @@ const RELEASE_GROW_DURATION = 150;
|
|
|
46
47
|
const RELEASE_FADE_DURATION = 250;
|
|
47
48
|
const PRESS_FADE_IN_DURATION = 75;
|
|
48
49
|
|
|
50
|
+
// The ripple animations run on the JS driver on purpose. On the New
|
|
51
|
+
// Architecture a native-driven animation started in the same tick the
|
|
52
|
+
// `Animated.View` is created has no native view to bind to yet, so it never
|
|
53
|
+
// plays — which is why the ripple showed on RN 0.83 but not on 0.86+.
|
|
54
|
+
const USE_NATIVE_DRIVER = false;
|
|
55
|
+
|
|
56
|
+
const toFinite = (value: number, fallback: number) =>
|
|
57
|
+
Number.isFinite(value) ? value : fallback;
|
|
58
|
+
|
|
49
59
|
/** Wraps a child and paints a touch ripple at the press location. */
|
|
50
60
|
export function RippleEffect({
|
|
51
61
|
children,
|
|
@@ -88,19 +98,33 @@ export function RippleEffect({
|
|
|
88
98
|
const w2 = 0.5 * layout.width;
|
|
89
99
|
const h2 = 0.5 * layout.height;
|
|
90
100
|
|
|
91
|
-
// Fall back to the center when the gesture carries no coordinates
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
101
|
+
// Fall back to the center when the gesture carries no coordinates, and
|
|
102
|
+
// clamp to the pressable's box — some setups report press coordinates
|
|
103
|
+
// relative to the wrong ancestor, which would push the circle off-screen
|
|
104
|
+
// where `overflow: 'hidden'` clips it away entirely.
|
|
105
|
+
const centered = rippleCentered || !event?.nativeEvent;
|
|
106
|
+
const clampX = (value: number) =>
|
|
107
|
+
layout.width > 0 ? Math.min(Math.max(value, 0), layout.width) : value;
|
|
108
|
+
const clampY = (value: number) =>
|
|
109
|
+
layout.height > 0 ? Math.min(Math.max(value, 0), layout.height) : value;
|
|
110
|
+
const locationX = centered
|
|
111
|
+
? w2
|
|
112
|
+
: clampX(toFinite(event.nativeEvent.locationX, w2));
|
|
113
|
+
const locationY = centered
|
|
114
|
+
? h2
|
|
115
|
+
: clampY(toFinite(event.nativeEvent.locationY, h2));
|
|
96
116
|
|
|
97
117
|
const offsetX = Math.abs(w2 - locationX);
|
|
98
118
|
const offsetY = Math.abs(h2 - locationY);
|
|
99
119
|
|
|
100
|
-
const
|
|
120
|
+
const computedRadius =
|
|
101
121
|
rippleSize > 0
|
|
102
122
|
? 0.5 * rippleSize
|
|
103
123
|
: Math.sqrt((w2 + offsetX) ** 2 + (h2 + offsetY) ** 2);
|
|
124
|
+
const radius =
|
|
125
|
+
Number.isFinite(computedRadius) && computedRadius > 0
|
|
126
|
+
? computedRadius
|
|
127
|
+
: Math.max(w2, h2, CIRCLE_RADIUS);
|
|
104
128
|
|
|
105
129
|
const ripple: RippleItemType = {
|
|
106
130
|
unique: uniqueRef.current++,
|
|
@@ -114,10 +138,19 @@ export function RippleEffect({
|
|
|
114
138
|
ripplesRef.current.set(ripple.unique, ripple);
|
|
115
139
|
activeRippleRef.current = ripple.unique;
|
|
116
140
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
141
|
+
Animated.timing(ripple.scale, {
|
|
142
|
+
toValue: 1,
|
|
143
|
+
duration: rippleDuration,
|
|
144
|
+
easing: Easing.out(Easing.ease),
|
|
145
|
+
useNativeDriver: USE_NATIVE_DRIVER,
|
|
146
|
+
}).start();
|
|
147
|
+
|
|
148
|
+
Animated.timing(ripple.opacity, {
|
|
149
|
+
toValue: rippleOpacity,
|
|
150
|
+
duration: Math.min(rippleDuration, PRESS_FADE_IN_DURATION),
|
|
151
|
+
useNativeDriver: USE_NATIVE_DRIVER,
|
|
152
|
+
}).start();
|
|
153
|
+
|
|
121
154
|
setRippleIds((current) => current.concat(ripple.unique));
|
|
122
155
|
},
|
|
123
156
|
[
|
|
@@ -125,6 +158,8 @@ export function RippleEffect({
|
|
|
125
158
|
layout.height,
|
|
126
159
|
onPressIn,
|
|
127
160
|
rippleCentered,
|
|
161
|
+
rippleDuration,
|
|
162
|
+
rippleOpacity,
|
|
128
163
|
rippleSequential,
|
|
129
164
|
rippleSize,
|
|
130
165
|
]
|
|
@@ -157,12 +192,12 @@ export function RippleEffect({
|
|
|
157
192
|
Animated.timing(ripple.scale, {
|
|
158
193
|
toValue: 1,
|
|
159
194
|
duration: RELEASE_GROW_DURATION,
|
|
160
|
-
useNativeDriver:
|
|
195
|
+
useNativeDriver: USE_NATIVE_DRIVER,
|
|
161
196
|
}),
|
|
162
197
|
Animated.timing(ripple.opacity, {
|
|
163
198
|
toValue: 0,
|
|
164
199
|
duration: RELEASE_FADE_DURATION,
|
|
165
|
-
useNativeDriver:
|
|
200
|
+
useNativeDriver: USE_NATIVE_DRIVER,
|
|
166
201
|
}),
|
|
167
202
|
]).start(removeRipple);
|
|
168
203
|
},
|
|
@@ -177,11 +212,9 @@ export function RippleEffect({
|
|
|
177
212
|
onPressIn={handlePressIn}
|
|
178
213
|
onPressOut={handlePressOut}
|
|
179
214
|
>
|
|
180
|
-
{/* The caller's `style` (often a flex row with `justifyContent`) is
|
|
181
|
-
applied here, wrapping only the real children — so the absolutely
|
|
182
|
-
positioned ripple layer below can never take part in that layout. */}
|
|
183
215
|
<View style={style}>{children}</View>
|
|
184
216
|
<View
|
|
217
|
+
collapsable={false}
|
|
185
218
|
pointerEvents="none"
|
|
186
219
|
style={[
|
|
187
220
|
styles.container,
|
|
@@ -191,15 +224,7 @@ export function RippleEffect({
|
|
|
191
224
|
{rippleIds.map((id) => {
|
|
192
225
|
const ripple = ripplesRef.current.get(id);
|
|
193
226
|
if (!ripple) return null;
|
|
194
|
-
return
|
|
195
|
-
<RippleCircle
|
|
196
|
-
key={id}
|
|
197
|
-
ripple={ripple}
|
|
198
|
-
color={color}
|
|
199
|
-
duration={rippleDuration}
|
|
200
|
-
toOpacity={rippleOpacity}
|
|
201
|
-
/>
|
|
202
|
-
);
|
|
227
|
+
return <RippleCircle key={id} ripple={ripple} color={color} />;
|
|
203
228
|
})}
|
|
204
229
|
</View>
|
|
205
230
|
</Pressable>
|
|
@@ -209,35 +234,11 @@ export function RippleEffect({
|
|
|
209
234
|
type RippleCircleProps = {
|
|
210
235
|
ripple: RippleItemType;
|
|
211
236
|
color: string;
|
|
212
|
-
duration: number;
|
|
213
|
-
toOpacity: number;
|
|
214
237
|
};
|
|
215
238
|
|
|
216
|
-
function RippleCircleComponent({
|
|
217
|
-
ripple,
|
|
218
|
-
color,
|
|
219
|
-
duration,
|
|
220
|
-
toOpacity,
|
|
221
|
-
}: RippleCircleProps) {
|
|
239
|
+
function RippleCircleComponent({ ripple, color }: RippleCircleProps) {
|
|
222
240
|
const { locationX, locationY, radius, scale, opacity } = ripple;
|
|
223
241
|
|
|
224
|
-
React.useEffect(() => {
|
|
225
|
-
Animated.timing(scale, {
|
|
226
|
-
toValue: 1,
|
|
227
|
-
duration,
|
|
228
|
-
easing: Easing.out(Easing.ease),
|
|
229
|
-
useNativeDriver: true,
|
|
230
|
-
}).start();
|
|
231
|
-
|
|
232
|
-
Animated.timing(opacity, {
|
|
233
|
-
toValue: toOpacity,
|
|
234
|
-
duration: Math.min(duration, PRESS_FADE_IN_DURATION),
|
|
235
|
-
useNativeDriver: true,
|
|
236
|
-
}).start();
|
|
237
|
-
// Entrance runs exactly once, when the ripple view mounts.
|
|
238
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
239
|
-
}, []);
|
|
240
|
-
|
|
241
242
|
const rippleStyle = {
|
|
242
243
|
top: locationY - CIRCLE_RADIUS,
|
|
243
244
|
[I18nManager.isRTL ? 'right' : 'left']: locationX - CIRCLE_RADIUS,
|