software-mansion / react-native-gesture-handler

Declarative API exposing platform native touch and gesture system to React Native.
https://docs.swmansion.com/react-native-gesture-handler/
MIT License
6.13k stars 982 forks source link

Fix `Pressable` not inferring dimensions from it's children #3020

Closed latekvo closed 3 months ago

latekvo commented 3 months ago

Description

When Pressable does not have it's dimensions robustly set, it should infer them from it's children, to fit-content. Unfortunately that's not what happens.

This PR fixes this issue.

Test plan

Results

before after
Screenshot 2024-07-31 at 15 12 34 Screenshot 2024-07-31 at 15 10 44

Code

import React from 'react';
import { StyleSheet, Text, View, Pressable as RNPressable } from 'react-native';
import {
  Pressable as GHPressable,
  PressableProps,
} from 'react-native-gesture-handler';

function Pressables(props: PressableProps) {
  const onPressInGH = () => console.log('GH press');
  const onPressInRN = () => console.log('RN press');

  return (
    <View style={styles.container}>
      <GHPressable
        {...(props as any)}
        onPressIn={onPressInGH}
        style={[styles.pressable, props.style]}>
        {props.children ?? <Text>Gesture Handler!</Text>}
      </GHPressable>
      <RNPressable
        {...(props as any)}
        onPressIn={onPressInRN}
        style={[styles.pressable, props.style]}>
        {props.children ?? <Text>React Native!</Text>}
      </RNPressable>
    </View>
  );
}

export default function EmptyExample() {
  return (
    <View style={styles.multirow}>
      <Text style={styles.header}>Padding</Text>
      <Pressables style={{ padding: 16 }} />

      <Text style={styles.header}>GH nested pressable</Text>
      <Pressables style={{ flex: 1, backgroundColor: 'plum' }}>
        <GHPressable
          style={{
            backgroundColor: 'orange',
          }}>
          <Text>Gesture Handler</Text>
        </GHPressable>
      </Pressables>

      <Text style={styles.header}>RN nested pressable</Text>
      <Pressables style={{ flex: 1, backgroundColor: 'plum' }}>
        <RNPressable
          style={{
            backgroundColor: 'orange',
          }}>
          <Text>React Native</Text>
        </RNPressable>
      </Pressables>

      <Text style={styles.header}>2 nested pressables</Text>
      <Pressables
        style={{ flex: 1, backgroundColor: 'plum', flexDirection: 'row' }}>
        <GHPressable
          style={{
            backgroundColor: 'pink',
          }}>
          <Text style={{ padding: 8 }}>GH</Text>
        </GHPressable>
        <RNPressable
          style={{
            backgroundColor: 'orange',
          }}>
          <Text style={{ padding: 8 }}>RN</Text>
        </RNPressable>
      </Pressables>

      <Text style={styles.header}>Nested box model styling</Text>
      <Pressables>
        <View style={{ backgroundColor: 'orange', padding: 8, margin: 8 }}>
          <View style={{ backgroundColor: 'plum', margin: 8 }}>
            <Text>Hello World!</Text>
          </View>
        </View>
      </Pressables>

      <Text style={styles.header}>Flex view in a fixed size Pressable</Text>
      <Pressables style={{ width: 100, height: 100 }}>
        <View style={styles.textWrapper}>
          <Text>Pressable!</Text>
        </View>
      </Pressables>

      <Text style={styles.header}>Flex view in a formless size Pressable</Text>
      <Pressables>
        <View style={styles.textWrapper}>
          <Text>Pressable!</Text>
        </View>
      </Pressables>
    </View>
  );
}

const styles = StyleSheet.create({
  header: {
    fontSize: 16,
    fontWeight: 'bold',
    textAlign: 'center',
  },
  multirow: {
    flex: 1,
    flexDirection: 'column',
    overflow: 'scroll',
  },
  container: {
    flexDirection: 'row',
    justifyContent: 'space-around',
    gap: 30,
    margin: 30,
    marginTop: 5,
  },
  pressable: {
    backgroundColor: 'tomato',
    borderWidth: 1,
    maxWidth: '30%',
  },
  textWrapper: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
});
latekvo commented 3 months ago

Other available solutions include:

I think StyleSheet.absoluteFill, { position: 'relative' } is the most promising one but I'll have to look into it.

update: Could not find a difference between any of the listed styles. Additionally tested:

and most other possible combinations of these styles.

latekvo commented 3 months ago

As of f3f590a I updated the description to include nested flexbox example.

Technically { height: '100%' } would suffice instead of the current { width: '100%', height: '100%' } based solely on the tests that I did, but I am not fully confident I understand the underlaying mechanics of why that is, which is why I would prefer to leave it as { width: '100%', height: '100%' }.

Please let me know WDYT @j-piasecki

j-piasecki commented 3 months ago

Whether width or height alone would suffice is likely dependant on the layout direction - with flexDirection: 'column' height will be enough and with flexDirection: 'row' width will be enough.

Using both is probably best.