/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow * @format */ 'use strict'; const React = require('react'); const {StyleSheet, Text, View} = require('react-native'); type ExampleBoxComponentProps = $ReadOnly<{ onLog: (msg: string) => void, }>; type ExampleBoxProps = $ReadOnly<{ Component: React.ComponentType, }>; type ExampleBoxState = $ReadOnly<{ log: string[], }>; class ExampleBox extends React.Component { state: ExampleBoxState = { log: [], }; handleLog = (msg: string) => { // $FlowFixMe[cannot-write] this.state.log = this.state.log.concat([msg]); }; flushReactChanges = () => { this.forceUpdate(); }; /** * Capture phase of bubbling to append separator before any of the bubbling * happens. */ handleTouchCapture = () => { // $FlowFixMe[cannot-write] this.state.log = this.state.log.concat(['---']); }; render(): React.Node { const {Component} = this.props; return ( {this.state.log.join('\n')} ); } } class NoneExample extends React.Component<$FlowFixMe> { render(): React.Node { return ( this.props.onLog('A unspecified touched')} style={styles.box}> A: unspecified this.props.onLog('B none touched')} style={[styles.box, styles.boxPassedThrough]}> B: none this.props.onLog('C unspecified touched')} style={[styles.box, styles.boxPassedThrough]}> C: unspecified ); } } class NoneStyleExample extends React.Component<$FlowFixMe> { render(): React.Node { return ( this.props.onLog('A unspecified touched')} style={styles.box}> A: unspecified this.props.onLog('B none touched')} style={[ styles.box, styles.boxPassedThrough, styles.pointerEventNone, ]}> B: none this.props.onLog('C unspecified touched')} style={[styles.box, styles.boxPassedThrough]}> C: unspecified ); } } /** * Special demo text that makes itself untouchable so that it doesn't destroy * the experiment and confuse the output. */ class DemoText extends React.Component<$FlowFixMe> { render(): React.Node { return ( {this.props.children} ); } } class BoxNoneExample extends React.Component<$FlowFixMe> { render(): React.Node { return ( this.props.onLog('A unspecified touched')} style={styles.box}> A: unspecified this.props.onLog('B box-none touched')} style={[styles.box, styles.boxPassedThrough]}> B: box-none this.props.onLog('C unspecified touched')} style={styles.box}> C: unspecified this.props.onLog('C explicitly unspecified touched') } style={[styles.box]}> C: explicitly unspecified ); } } class BoxNoneStyleExample extends React.Component<$FlowFixMe> { render(): React.Node { return ( this.props.onLog('A unspecified touched')} style={styles.box}> A: unspecified this.props.onLog('B box-none touched')} style={[ styles.box, styles.boxPassedThrough, styles.pointerEventBoxNone, ]}> B: box-none this.props.onLog('C unspecified touched')} style={styles.box}> C: unspecified this.props.onLog('C explicitly unspecified touched') } style={[styles.box, styles.pointerEventAuto]}> C: explicitly unspecified ); } } class BoxOnlyExample extends React.Component<$FlowFixMe> { render(): React.Node { return ( this.props.onLog('A unspecified touched')} style={styles.box}> A: unspecified this.props.onLog('B box-only touched')} style={styles.box}> B: box-only this.props.onLog('C unspecified touched')} style={[styles.box, styles.boxPassedThrough]}> C: unspecified this.props.onLog('C explicitly unspecified touched') } style={[styles.box, styles.boxPassedThrough]}> C: explicitly unspecified ); } } class BoxOnlyStyleExample extends React.Component<$FlowFixMe> { render(): React.Node { return ( this.props.onLog('A unspecified touched')} style={styles.box}> A: unspecified this.props.onLog('B box-only touched')} style={[styles.box, styles.pointerEventBoxOnly]}> B: box-only this.props.onLog('C unspecified touched')} style={[styles.box, styles.boxPassedThrough]}> C: unspecified this.props.onLog('C explicitly unspecified touched') } style={[ styles.box, styles.boxPassedThrough, styles.pointerEventAuto, ]}> C: explicitly unspecified ); } } type OverflowExampleProps = $ReadOnly<{ overflow: 'hidden' | 'visible', onLog: (msg: string) => void, }>; class OverflowExample extends React.Component { render(): React.Node { const {overflow} = this.props; return ( this.props.onLog(`A overflow ${overflow} touched`)} style={[ styles.box, styles.boxWithOverflowSet, {overflow: this.props.overflow}, ]}> A: overflow: {overflow} this.props.onLog('B overflowing touched')} style={[styles.box, styles.boxOverflowing]}> B: overflowing this.props.onLog('C fully outside touched')} style={[styles.box, styles.boxFullyOutside]}> C: fully outside this.props.onLog('D fully outside child touched') } style={[styles.box, styles.boxFullyOutsideChild]}> D: child of fully outside ); } } class OverflowVisibleExample extends React.Component { render(): React.Node { return ; } } class OverflowHiddenExample extends React.Component { render(): React.Node { return ; } } type ExampleClass = { Component: React.ComponentType, title: string, description: string, ... }; const exampleClasses: Array = [ { Component: NoneExample, title: '`none`', description: '`none` causes touch events on the container and its child components to pass through to the parent container.', }, { Component: NoneStyleExample, title: '`none` style', description: '`none` causes touch events on the container and its child components to pass through to the parent container.', }, { Component: BoxNoneExample, title: '`box-none`', description: '`box-none` causes touch events on the container to pass through and will only detect touch events on its child components.', }, { Component: BoxNoneStyleExample, title: '`box-none` style', description: '`box-none` causes touch events on the container to pass through and will only detect touch events on its child components.', }, { Component: BoxOnlyExample, title: '`box-only`', description: "`box-only` causes touch events on the container's child components to pass through and will only detect touch events on the container itself.", }, { Component: BoxOnlyStyleExample, title: '`box-only` style', description: "`box-only` causes touch events on the container's child components to pass through and will only detect touch events on the container itself.", }, { Component: OverflowVisibleExample, title: '`overflow: visible`', description: '`overflow: visible` style should allow subelements that are outside of the parent box to be touchable. Tapping the parts of Box B outside Box A should print "B touched" and "A touched", and tapping Box C should also print "C touched" and "A touched".', }, { Component: OverflowHiddenExample, title: '`overflow: hidden`', description: '`overflow: hidden` style should only allow subelements within the parent box to be touchable. Tapping just below Box A (where Box B would otherwise extend if it weren\'t cut off) should not trigger any touches or messages. Touching Box D (inside the bounds) should print "D touched" and "A touched".', }, ]; const infoToExample = (info: ExampleClass) => { return { title: info.title, description: info.description, render: function () { return ; }, }; }; const styles = StyleSheet.create({ text: { fontSize: 10, color: '#5577cc', }, textPassedThrough: { color: '#88aadd', }, box: { backgroundColor: '#aaccff', borderWidth: 1, borderColor: '#7799cc', padding: 10, margin: 5, }, boxPassedThrough: { borderColor: '#99bbee', }, boxWithOverflowSet: { paddingBottom: 40, marginBottom: 50, }, boxOverflowing: { position: 'absolute', top: 30, paddingBottom: 40, }, boxFullyOutside: { position: 'absolute', left: 200, top: 65, }, boxFullyOutsideChild: { position: 'absolute', left: 0, top: -65, width: 100, }, logText: { fontSize: 9, }, logBox: { padding: 20, margin: 10, borderWidth: 0.5, borderColor: '#f0f0f0', backgroundColor: '#f9f9f9', }, pointerEventBoxNone: { pointerEvents: 'box-none', }, pointerEventBoxOnly: { pointerEvents: 'box-only', }, pointerEventNone: { pointerEvents: 'none', }, pointerEventAuto: { pointerEvents: 'auto', }, }); exports.framework = 'React'; exports.title = 'Pointer Events'; exports.category = 'Basic'; exports.description = ('Demonstrates the use of the pointerEvents prop of a ' + 'View to control how touches should be handled.': string); exports.examples = (exampleClasses.map(infoToExample): Array);