Last active
September 23, 2020 09:16
-
-
Save necolas/b2d35105365c31321e7e3a0b815328b4 to your computer and use it in GitHub Desktop.
React Pressable / OnLayout
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* OnLayout is built upon: View (and ResizeObserver), StyleSheet | |
*/ | |
const elementBreakpoints = { | |
small: { minWidth: 200 }, | |
medium: { minWidth: 300 } | |
large: { minWidth: 500 } | |
}; | |
// OnLayout forwards information about the container's dimensions (NOT the viewport). | |
// It allows complete control over what children and styles are rendered for a given state. | |
// It addresses the lack of "container queries" on the web, and the need to perform | |
// layout-dependent adjustments to component trees not just styles. | |
const Example = () => ( | |
<OnLayout | |
breakpoints={elementBreakpoints} | |
style={(state) => ([ | |
state.small && styles.small, | |
state.medium && !state.large && styles.mediumNotLarge, | |
state.large && styles.large | |
])} | |
/> | |
{(state) => ( | |
<Text>Hello, {state.large ? "big world" : "small world"}</Text> | |
)} | |
</OnLayout> | |
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Pressable is built upon: View, StyleSheet, and the Responder Event system. | |
*/ | |
type PressableState = $ReadOnly<{| | |
hovered: boolean, | |
focused: boolean, | |
pressed: boolean, | |
longPressed: boolean | |
|}> | |
// Pressable forwards information about the modality interaction state. | |
// It allows for complete control over what children and styles are rendered for a given state. | |
// It addresses various UX quirks on mobile web (e.g., hover is NEVER true during touch interactions). | |
const SimpleExample = () => ( | |
<Pressable | |
style={(state) => ([ | |
state.hovered && styles.hovered, | |
state.pressed && styles.pressed | |
])} | |
> | |
<Text>Press me</Text> | |
</Pressable> | |
); | |
// More complex uses cases – logging interactions, adding delays to hover/press effects, etc. – are possible. | |
// JS events are in sync with the state that is forwarded to "children" and "style". | |
const ComplexExample = () => ( | |
<Pressable | |
// accessibilityLabel="Create new post (long press for shortcuts to open the camera roll and video recorder)", | |
// accessibilityRole="button", | |
// accessibilityStates=["selected"], | |
// hitSlop, | |
// onLayout, | |
onBlur={e => {…}) | |
onFocus={e => {…}) | |
// Delays allow you to control when an interaction state is activated. | |
delayHoverIn={100} | |
delayHoverOut={200} | |
delayPressIn={150} | |
delayPressOut={0} | |
delayLongPress={0} | |
// Mouse | |
onHoverIn={e => {…}) | |
onHoverOut={e => {…}) | |
// Touch, Mouse, Keyboard | |
onPress={e => {…}) | |
onPressIn={e => {…}) | |
onPressMove={e => {…}} | |
onPressOut={e => {…}) | |
onLongPress={e => {…}} | |
onLongPressShouldCancelPress={() => true} | |
pressRetentionOffset={pressRetentionOffset: EdgeInsetsProp} | |
// Style as a function of interaction state. | |
style={getStyle} | |
> | |
// Children as a function of interaction state. | |
{getChildren} | |
</Pressable> | |
); | |
const getChildren = (state: PressableState) => { | |
const { hovered, pressed, longPressed } = state; | |
return ( | |
<> | |
// Fine control over render tree | |
{(hovered && !pressed) ? <Tooltip /> : null} | |
<Text style={pressed && style.pressedText}>Press me</Text> | |
{longPressed ? <RadialMenu /> : null} | |
</> | |
); | |
} | |
const getStyle = (state: PressableState) => { | |
const { hovered, focused, pressed, longPressed } = state; | |
return [ | |
styles.root, | |
// Fine control over how styles compose (or don't) | |
// Unlike CSS ":hover", hovered is never "true" during a touch. | |
hovered && !focused && styles.hovered, | |
focused && styles.focused, | |
pressed && styles.pressed, | |
longPressed && styles.longPressed | |
]; | |
}; | |
const styles = StyleSheet.create({ | |
root: { ... } | |
hovered: { ... }, | |
focused: { ... }, | |
pressed: { ... }, | |
longPressed: { ... }, | |
pressedText: { ... } | |
}); | |
// Questions? | |
// Use case for 'onHoverMove'? | |
// Use case for 'onMoveShouldCancelHover'? |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment