tangled
alpha
login
or
join now
stream.place
/
streamplace
75
fork
atom
Live video on the AT Protocol
75
fork
atom
overview
issues
1
pulls
pipelines
add hovering to button or whatever
Natalie B.
1 month ago
f43902b6
5300bb8d
+42
-12
3 changed files
expand all
collapse all
unified
split
js
app
components
follow-button.tsx
components
src
components
ui
button.tsx
primitives
button.tsx
+2
-1
js/app/components/follow-button.tsx
···
123
123
disabled={isFollowing === null}
124
124
loading={isFollowing === null}
125
125
leftIcon={!isFollowing && <Icon icon={Plus} size="sm" />}
126
126
+
hoverStyle={isFollowing ? { backgroundColor: "#dc2626" } : undefined}
126
127
>
127
128
{isFollowing === null
128
129
? "Loading..."
129
130
: isFollowing
130
130
-
? "Unfollow"
131
131
+
? "Following"
131
132
: "Follow"}
132
133
</Button>
133
134
{error && <Text style={[{ color: "#c00" }, zero.ml[2]]}>{error}</Text>}
+3
js/components/src/components/ui/button.tsx
···
41
41
loading?: boolean;
42
42
loadingText?: string;
43
43
width?: "full" | "min" | number;
44
44
+
hoverStyle?: ButtonPrimitiveProps["hoverStyle"];
44
45
}
45
46
46
47
export const Button = forwardRef<any, ButtonProps>(
···
56
57
disabled,
57
58
style,
58
59
width = "full",
60
60
+
hoverStyle,
59
61
...props
60
62
},
61
63
ref,
···
222
224
ref={ref}
223
225
disabled={disabled || loading}
224
226
style={[buttonStyle, sizeStyles.button, widthStyle, style]}
227
227
+
hoverStyle={hoverStyle}
225
228
{...props}
226
229
>
227
230
<ButtonPrimitive.Content style={sizeStyles.inner}>
+37
-11
js/components/src/components/ui/primitives/button.tsx
···
1
1
-
import React, { forwardRef } from "react";
1
1
+
import React, { forwardRef, useState } from "react";
2
2
import {
3
3
AccessibilityRole,
4
4
GestureResponderEvent,
5
5
+
Platform,
6
6
+
Pressable,
7
7
+
PressableProps,
8
8
+
StyleProp,
5
9
StyleSheet,
6
10
Text,
7
11
TextProps,
8
8
-
TouchableOpacity,
9
9
-
TouchableOpacityProps,
10
12
View,
11
13
ViewProps,
14
14
+
ViewStyle,
12
15
} from "react-native";
13
16
14
17
// Base button primitive interface
15
15
-
export interface ButtonPrimitiveProps
16
16
-
extends Omit<TouchableOpacityProps, "onPress"> {
18
18
+
export interface ButtonPrimitiveProps extends Omit<PressableProps, "onPress"> {
17
19
onPress?: (event: GestureResponderEvent) => void;
18
20
disabled?: boolean;
19
21
loading?: boolean;
···
21
23
accessibilityLabel?: string;
22
24
accessibilityHint?: string;
23
25
testID?: string;
26
26
+
hoverStyle?: StyleProp<ViewStyle>;
24
27
}
25
28
26
29
// Button root primitive - handles all touch interactions
27
30
export const ButtonRoot = forwardRef<
28
28
-
React.ComponentRef<typeof TouchableOpacity>,
31
31
+
React.ComponentRef<typeof Pressable>,
29
32
ButtonPrimitiveProps
30
33
>(
31
34
(
···
43
46
accessibilityState,
44
47
testID,
45
48
style,
46
46
-
activeOpacity = 0.7,
49
49
+
hoverStyle,
47
50
...props
48
51
},
49
52
ref,
50
53
) => {
54
54
+
const [isHovered, setIsHovered] = useState(false);
55
55
+
51
56
const handlePress = React.useCallback(
52
57
(event: GestureResponderEvent) => {
53
58
if (!disabled && !loading && onPress) {
···
84
89
[disabled, loading, onLongPress],
85
90
);
86
91
92
92
+
const handleHoverIn = React.useCallback(() => {
93
93
+
if (!disabled && !loading) {
94
94
+
setIsHovered(true);
95
95
+
}
96
96
+
}, [disabled, loading]);
97
97
+
98
98
+
const handleHoverOut = React.useCallback(() => {
99
99
+
setIsHovered(false);
100
100
+
}, []);
101
101
+
87
102
return (
88
88
-
<TouchableOpacity
103
103
+
<Pressable
89
104
ref={ref}
90
105
onPress={handlePress}
91
106
onPressIn={handlePressIn}
92
107
onPressOut={handlePressOut}
93
108
onLongPress={handleLongPress}
109
109
+
onHoverIn={handleHoverIn}
110
110
+
onHoverOut={handleHoverOut}
94
111
disabled={disabled || loading}
95
95
-
activeOpacity={disabled || loading ? 1 : activeOpacity}
96
112
accessibilityRole={accessibilityRole}
97
113
accessibilityLabel={accessibilityLabel}
98
114
accessibilityHint={accessibilityHint}
···
104
120
testID={testID}
105
121
style={[
106
122
primitiveStyles.button,
123
123
+
primitiveStyles.transition,
107
124
(disabled || loading) && primitiveStyles.disabled,
108
108
-
style,
125
125
+
style as any,
126
126
+
isHovered && hoverStyle,
109
127
]}
110
128
{...props}
111
129
>
112
130
{children}
113
113
-
</TouchableOpacity>
131
131
+
</Pressable>
114
132
);
115
133
},
116
134
);
···
245
263
alignItems: "center",
246
264
justifyContent: "center",
247
265
},
266
266
+
transition:
267
267
+
Platform.OS === "web"
268
268
+
? // probably fine if web-only
269
269
+
({
270
270
+
transitionDuration: "150ms",
271
271
+
transitionProperty: "background-color, border-color, color",
272
272
+
} as any)
273
273
+
: undefined,
248
274
disabled: {
249
275
opacity: 0.5,
250
276
},