microsoft / react-native-macos

A framework for building native macOS apps with React.
https://microsoft.github.io/react-native-windows/
MIT License
3.49k stars 135 forks source link

[0.73-stable] Add mouse hover events to `RCTTextView` #2143

Closed amgleitman closed 2 months ago

amgleitman commented 3 months ago

Summary:

We have a feature request to allow mouse hover events for subelements inside a Text element. This builds off #2137 to get things all set.

For now we're only pushing this into 0.73-stable because we want to take more time to consider the native-side architecture for 0.74 onwards.

One slight drawback is that this only works completely properly when there is only one level of onMouseEnter events underneath the main box. For example, if we move the mouse from red to blue in the below example, we see a "leave red" event immediately before an "enter blue" event. The reason for this is that at the native level, all RCTVirtualTextViews that represent internal Text nodes are direct children of the parent RCTTextView instead of directly reflecting the DOM hierarchy. This can potentially be fixed by replacing the superview calls inside updateHoveredSubviewWithEvent: with something more appropriate, but this is still sufficient for our needs, so I think it's passable as a first iteration of this PR.

Test Plan:

Validated using the following example in RNTester:

{
  title: 'Transparent Background Color',
  render: function (): React.Node {
    const rootProps = {
      onMouseEnter: _ => console.log('Mouse enter gray'),
      onMouseLeave: _ => console.log('Mouse leave gray'),
    };

    return (
      <View style={{backgroundColor: '#00000020'}}>
        <Text {...rootProps}>
          Text in a gray box!
          <Text
            style={{color: 'red'}}
            onMouseEnter={_ => console.log('Mouse enter red')}
            onMouseLeave={_ => console.log('Mouse leave red')}>
            Another text in a (inline) red box (which is
            <Text
              style={{color: 'blue'}}
              onMouseEnter={_ => console.log('Mouse enter blue')}
              onMouseLeave={_ => console.log('Mouse leave blue')}>
              inside
            </Text>
            the gray box).
          </Text>
        </Text>
      </View>
    );
  },
}
amgleitman commented 3 months ago

It would be good to resolve if we can deterministically create a non-duplicate list of descendantViewTags without relying on throwing away duplicates through a Set, but otherwise this change looks good to me :)

We're now providing the virtual text subviews in a separate array, so this is no longer an issue.