Skip to content

Commit

Permalink
macOS Keyboard Support (#657)
Browse files Browse the repository at this point in the history
* Update RCTCxxBridge.mm

* macOS keyboard support

* update comment

* add key value checks so we don't send all events

* macOS keyboard support

* update comment

* add key value checks so we don't send all events

* [ado] Workaround homebrew openssl issue (#669)

actions/runner-images#1811 (comment)

* Fix detox yarn error with Xcode 12 (#670)

* Update RCTCxxBridge.mm

* fix detox errors with xcode 12

* only use valid keys, bubble events to super

* macOS keyboard support

* update comment

* add key value checks so we don't send all events

* only use valid keys, bubble events to super

* macOS keyboard support

* add key value checks so we don't send all events

* resolve bad merge

* update valid key bug, api typo

* spacing fix

* fix flow errors

* fix snapshot tests for new APIs

* yarn lint --fix

* fix flipper

Co-authored-by: Eloy Durán <eloy.de.enige@gmail.com>
  • Loading branch information
HeyImChris and alloy authored Dec 12, 2020
1 parent a2f4df1 commit c0ec10d
Show file tree
Hide file tree
Showing 23 changed files with 585 additions and 3 deletions.
32 changes: 31 additions & 1 deletion Libraries/Components/Button.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const View = require('./View/View');

const invariant = require('invariant');

import type {PressEvent} from '../Types/CoreEventTypes';
import type {PressEvent, KeyEvent} from '../Types/CoreEventTypes';
import type {FocusEvent, BlurEvent} from './TextInput/TextInput'; // TODO(OSS Candidate ISS#2710739)
import type {ColorValue} from '../StyleSheet/StyleSheetTypes';

Expand Down Expand Up @@ -113,6 +113,28 @@ type ButtonProps = $ReadOnly<{|
* Handler to be called when the button loses key focus
*/
onFocus?: ?(e: FocusEvent) => void,

/**
* Handler to be called when a key down press is detected
*/
onKeyDown?: ?(e: KeyEvent) => void,

/**
* Handler to be called when a key up press is detected
*/
onKeyUp?: ?(e: KeyEvent) => void,

/*
* Array of keys to receive key down events for
* For arrow keys, add "leftArrow", "rightArrow", "upArrow", "downArrow",
*/
validKeysDown?: ?Array<string>,

/*
* Array of keys to receive key up events for
* For arrow keys, add "leftArrow", "rightArrow", "upArrow", "downArrow",
*/
validKeysUp?: ?Array<string>,
// ]TODO(OSS Candidate ISS#2710739)
|}>;

Expand Down Expand Up @@ -163,6 +185,10 @@ class Button extends React.Component<ButtonProps> {
testID,
onFocus, // TODO(OSS Candidate ISS#2710739)
onBlur, // TODO(OSS Candidate ISS#2710739)
onKeyDown,
validKeysDown,
validKeysUp,
onKeyUp,
} = this.props;
const buttonStyles = [styles.button];
const textStyles = [styles.text];
Expand Down Expand Up @@ -207,6 +233,10 @@ class Button extends React.Component<ButtonProps> {
onPress={onPress}
onFocus={onFocus} // TODO(OSS Candidate ISS#2710739)
onBlur={onBlur} // TODO(OSS Candidate ISS#2710739)
onKeyDown={onKeyDown}
onKeyUp={onKeyUp}
validKeysDown={validKeysDown}
validKeysUp={validKeysUp}
touchSoundDisabled={touchSoundDisabled}>
<View style={buttonStyles}>
<Text style={textStyles} disabled={disabled}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ exports[`<Pressable /> should render as expected: should deep render when mocked
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
onKeyUp={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
Expand All @@ -29,6 +31,8 @@ exports[`<Pressable /> should render as expected: should deep render when not mo
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
onKeyUp={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ exports[`TextInput tests should render as expected: should deep render when mock
onChange={[Function]}
onClick={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
onKeyUp={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
Expand Down Expand Up @@ -42,6 +44,8 @@ exports[`TextInput tests should render as expected: should deep render when not
onChange={[Function]}
onClick={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
onKeyUp={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
Expand Down
14 changes: 14 additions & 0 deletions Libraries/Components/Touchable/TouchableNativeFeedback.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,18 @@ type Props = $ReadOnly<{|
*/
nextFocusUp?: ?number,

/*
* Array of keys to receive key down events for
* For arrow keys, add "leftArrow", "rightArrow", "upArrow", "downArrow",
*/
validKeysDown?: ?Array<string>,

/*
* Array of keys to receive key up events for
* For arrow keys, add "leftArrow", "rightArrow", "upArrow", "downArrow",
*/
validKeysUp?: ?Array<string>,

/**
* Set to true to add the ripple effect to the foreground of the view, instead
* of the background. This is useful if one of your child views has a
Expand Down Expand Up @@ -297,6 +309,8 @@ class TouchableNativeFeedback extends React.Component<Props, State> {
nextFocusUp: this.props.nextFocusUp,
onLayout: this.props.onLayout,
testID: this.props.testID,
validKeysDown: this.props.validKeysDown,
validKeysUp: this.props.validKeysUp,
},
...children,
);
Expand Down
27 changes: 27 additions & 0 deletions Libraries/Components/Touchable/TouchableOpacity.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,17 @@ type Props = $ReadOnly<{|
style?: ?ViewStyleProp,

hostRef: React.Ref<typeof Animated.View>,
/*
* Array of keys to receive key down events for
* For arrow keys, add "leftArrow", "rightArrow", "upArrow", "downArrow",
*/
validKeysDown?: ?Array<string>,

/*
* Array of keys to receive key up events for
* For arrow keys, add "leftArrow", "rightArrow", "upArrow", "downArrow",
*/
validKeysUp?: ?Array<string>,
|}>;

type State = $ReadOnly<{|
Expand Down Expand Up @@ -165,6 +176,18 @@ class TouchableOpacity extends React.Component<Props, State> {
this.props.onFocus(event);
}
},
onKeyDown: event => {
if (this.props.onKeyDown != null) {
this.props.onKeyDown(event);
}
},
onKeyUp: event => {
if (this.props.onKeyUp != null) {
this.props.onKeyUp(event);
}
},
validKeysDown: this.props.validKeysDown,
validKeysUp: this.props.validKeysUp,
onLongPress: this.props.onLongPress,
onPress: this.props.onPress,
onPressIn: event => {
Expand Down Expand Up @@ -279,6 +302,10 @@ class TouchableOpacity extends React.Component<Props, State> {
onDrop={this.props.onDrop}
onFocus={this.props.onFocus}
onBlur={this.props.onBlur}
onKeyDown={this.props.onKeyDown}
onKeyUp={this.props.onKeyUp}
validKeysDown={this.props.validKeysDown}
validKeysUp={this.props.validKeysUp}
draggedTypes={this.props.draggedTypes} // ]TODO(macOS ISS#2323203)
ref={this.props.hostRef}
{...eventHandlersWithoutBlurAndFocus}>
Expand Down
13 changes: 13 additions & 0 deletions Libraries/Components/Touchable/TouchableWithoutFeedback.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import type {EdgeInsetsProp} from '../../StyleSheet/EdgeInsetsPropType';
import type {
BlurEvent,
FocusEvent,
KeyEvent,
LayoutEvent,
PressEvent,
MouseEvent, // TODO(macOS ISS#2323203)
Expand Down Expand Up @@ -64,6 +65,10 @@ type Props = $ReadOnly<{|
onAccessibilityAction?: ?(event: AccessibilityActionEvent) => mixed,
onBlur?: ?(event: BlurEvent) => mixed,
onFocus?: ?(event: FocusEvent) => mixed,
onKeyDown?: ?(event: KeyEvent) => mixed,
onKeyUp?: ?(event: KeyEvent) => mixed,
validKeysDown?: ?Array<string>,
validKeysUp?: ?Array<string>,
onLayout?: ?(event: LayoutEvent) => mixed,
onLongPress?: ?(event: PressEvent) => mixed,
onPress?: ?(event: PressEvent) => mixed,
Expand Down Expand Up @@ -106,6 +111,10 @@ const PASSTHROUGH_PROPS = [
'onAccessibilityAction',
'onBlur',
'onFocus',
'onKeyDown',
'onKeyUp',
'validKeysDown',
'validKeysUp',
'onLayout',
'onMouseEnter', // [TODO(macOS ISS#2323203)
'onMouseLeave',
Expand Down Expand Up @@ -227,6 +236,10 @@ function createPressabilityConfig(props: Props): PressabilityConfig {
android_disableSound: props.touchSoundDisabled,
onBlur: props.onBlur,
onFocus: props.onFocus,
onKeyDown: props.onKeyDown,
onKeyUp: props.onKeyUp,
validKeysDown: props.validKeysDown,
validKeysUp: props.validKeysUp,
onLongPress: props.onLongPress,
onPress: props.onPress,
onPressIn: props.onPressIn,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ exports[`TouchableHighlight renders correctly 1`] = `
enableFocusRing={true}
focusable={false}
onClick={[Function]}
onKeyDown={[Function]}
onKeyUp={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
Expand Down
4 changes: 4 additions & 0 deletions Libraries/Components/View/ReactNativeViewAttributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ const UIView = {
onDragEnter: true,
onDragLeave: true,
onDrop: true,
onKeyDown: true,
onKeyUp: true,
validKeysDown: true,
validKeysUp: true,
draggedTypes: true, // ]TODO(macOS ISS#2323203)
style: ReactNativeStyleAttributes,
};
Expand Down
14 changes: 14 additions & 0 deletions Libraries/Components/View/ReactNativeViewViewConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@ const ReactNativeViewConfig = {
captured: 'onFocusCapture',
},
},
topKeyUp: {
phasedRegistrationNames: {
bubbled: 'onKeyUp',
captured: 'onKeyUpCapture',
},
},
topKeyDown: {
phasedRegistrationNames: {
bubbled: 'onKeyDown',
captured: 'onKeyDownCapture',
},
},
topKeyPress: {
phasedRegistrationNames: {
bubbled: 'onKeyPress',
Expand Down Expand Up @@ -343,6 +355,8 @@ const ReactNativeViewConfig = {
: {process: require('../../StyleSheet/processTransform')}): any),
translateX: true,
translateY: true,
validKeysDown: true,
validKeysUp: true,
width: true,
zIndex: true,
},
Expand Down
4 changes: 4 additions & 0 deletions Libraries/Components/View/ReactNativeViewViewConfigMacOS.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ const ReactNativeViewViewConfigMacOS = {
onDragLeave: true,
onDrop: true,
onFocus: true,
onKeyDown: true,
onKeyUp: true,
validKeysDown: true,
validKeysUp: true,
onMouseEnter: true,
onMouseLeave: true,
tooltip: true,
Expand Down
17 changes: 17 additions & 0 deletions Libraries/Components/View/ViewPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import type {
Layout,
LayoutEvent,
ScrollEvent, // TODO(macOS ISS#2323203)
KeyEvent,
} from '../../Types/CoreEventTypes';
import type {EdgeInsetsProp} from '../../StyleSheet/EdgeInsetsPropType';
import type {Node} from 'react';
Expand All @@ -33,6 +34,8 @@ import type {

// [TODO(macOS ISS#2323203)
import type {DraggedTypesType} from '../View/DraggedType';
//$FlowFixMe
import {array} from 'yargs';
// ]TODO(macOS ISS#2323203)

export type ViewLayout = Layout;
Expand All @@ -41,6 +44,8 @@ export type ViewLayoutEvent = LayoutEvent;
type BubblingEventProps = $ReadOnly<{|
onBlur?: ?(event: BlurEvent) => mixed,
onFocus?: ?(event: FocusEvent) => mixed,
onKeyDown?: ?(event: KeyEvent) => mixed,
onKeyUp?: ?(event: KeyEvent) => mixed,
|}>;

type DirectEventProps = $ReadOnly<{|
Expand Down Expand Up @@ -599,6 +604,18 @@ export type ViewProps = $ReadOnly<{|
*/
enableFocusRing?: ?boolean, // TODO(macOS ISS#2323203)

/*
* Array of keys to receive key down events for
* For arrow keys, add "leftArrow", "rightArrow", "upArrow", "downArrow",
*/
validKeysDown?: ?array<string>,

/*
* Array of keys to receive key up events for
* For arrow keys, add "leftArrow", "rightArrow", "upArrow", "downArrow",
*/
validKeysUp?: ?array<string>,

/**
* Enables Dran'n'Drop Support for certain types of dragged types
*
Expand Down
41 changes: 41 additions & 0 deletions Libraries/Pressability/Pressability.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {normalizeRect, type RectOrSize} from '../StyleSheet/Rect';
import type {
BlurEvent,
FocusEvent,
KeyEvent,
PressEvent,
MouseEvent,
} from '../Types/CoreEventTypes';
Expand Down Expand Up @@ -93,6 +94,28 @@ export type PressabilityConfig = $ReadOnly<{|
*/
onFocus?: ?(event: FocusEvent) => mixed,

/*
* Called after a key down event is detected.
*/
onKeyDown?: ?(event: KeyEvent) => mixed,

/*
* Called after a key up event is detected.
*/
onKeyUp?: ?(event: KeyEvent) => mixed,

/*
* Array of keys to receive key down events for
* For arrow keys, add "leftArrow", "rightArrow", "upArrow", "downArrow",
*/
validKeysDown?: ?Array<string>,

/*
* Array of keys to receive key up events for
* For arrow keys, add "leftArrow", "rightArrow", "upArrow", "downArrow",
*/
validKeysUp?: ?Array<string>,

/**
* Called when the hover is activated to provide visual feedback.
*/
Expand Down Expand Up @@ -156,6 +179,8 @@ export type EventHandlers = $ReadOnly<{|
onBlur: (event: BlurEvent) => void,
onClick: (event: PressEvent) => void,
onFocus: (event: FocusEvent) => void,
onKeyDown: (event: KeyEvent) => void,
onKeyUp: (event: KeyEvent) => void,
onMouseEnter?: (event: MouseEvent) => void,
onMouseLeave?: (event: MouseEvent) => void,
onResponderGrant: (event: PressEvent) => void,
Expand Down Expand Up @@ -446,6 +471,21 @@ export default class Pressability {
},
};

const keyEventHandlers = {
onKeyDown: (event: KeyEvent): void => {
const {onKeyDown} = this._config;
if (onKeyDown != null) {
onKeyDown(event);
}
},
onKeyUp: (event: KeyEvent): void => {
const {onKeyUp} = this._config;
if (onKeyUp != null) {
onKeyUp(event);
}
},
};

const responderEventHandlers = {
onStartShouldSetResponder: (): boolean => {
const {disabled} = this._config;
Expand Down Expand Up @@ -602,6 +642,7 @@ export default class Pressability {
...focusEventHandlers,
...responderEventHandlers,
...mouseEventHandlers,
...keyEventHandlers,
};
}

Expand Down
Loading

0 comments on commit c0ec10d

Please sign in to comment.