Skip to content

Commit 16914b0

Browse files
committed
Add JS shim for legacy validKeys props
1 parent 1db0687 commit 16914b0

File tree

5 files changed

+162
-24
lines changed

5 files changed

+162
-24
lines changed

packages/react-native/Libraries/Components/Pressable/Pressable.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,11 @@ function Pressable({
381381
// [macOS
382382
acceptsFirstMouse: acceptsFirstMouse !== false && !disabled,
383383
enableFocusRing: enableFocusRing !== false && !disabled,
384-
keyDownEvents: keyDownEvents ?? [{key: ' '}, {key: 'Enter'}],
384+
keyDownEvents:
385+
keyDownEvents ??
386+
(((props: any).validKeysDown: mixed) == null
387+
? [{key: ' '}, {key: 'Enter'}]
388+
: undefined),
385389
mouseDownCanMoveWindow: false,
386390
// macOS]
387391
};

packages/react-native/Libraries/Components/TextInput/RCTTextInputViewConfig.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,18 @@ const RCTTextInputViewConfig: PartialViewConfigWithoutName = {
5252
captured: 'onSubmitEditingCapture',
5353
},
5454
},
55+
topKeyDown: {
56+
phasedRegistrationNames: {
57+
bubbled: 'onKeyDown',
58+
captured: 'onKeyDownCapture',
59+
},
60+
},
61+
topKeyUp: {
62+
phasedRegistrationNames: {
63+
bubbled: 'onKeyUp',
64+
captured: 'onKeyUpCapture',
65+
},
66+
},
5567
topTouchCancel: {
5668
phasedRegistrationNames: {
5769
bubbled: 'onTouchCancel',
@@ -173,6 +185,8 @@ const RCTTextInputViewConfig: PartialViewConfigWithoutName = {
173185
clearTextOnSubmit: true,
174186
grammarCheck: true,
175187
hideVerticalScrollIndicator: true,
188+
keyDownEvents: true,
189+
keyUpEvents: true,
176190
pastedTypes: true,
177191
submitKeyEvents: true,
178192
tooltip: true,
@@ -191,6 +205,8 @@ const RCTTextInputViewConfig: PartialViewConfigWithoutName = {
191205
onAutoCorrectChange: true,
192206
onSpellCheckChange: true,
193207
onGrammarCheckChange: true,
208+
onKeyDown: true,
209+
onKeyUp: true,
194210
// macOS]
195211
}),
196212
disableKeyboardShortcuts: true,

packages/react-native/Libraries/Components/TextInput/TextInput.js

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ import StyleSheet, {type TextStyleProp} from '../../StyleSheet/StyleSheet';
6161
import Text from '../../Text/Text';
6262
import TextAncestorContext from '../../Text/TextAncestorContext';
6363
import Platform from '../../Utilities/Platform';
64+
import normalizeLegacyHandledKeyEvents, {
65+
type LegacyHandledKeyEvent,
66+
} from '../../Utilities/normalizeLegacyHandledKeyEvents';
6467
import useMergeRefs from '../../Utilities/useMergeRefs';
6568
import TextInputState from './TextInputState';
6669
import invariant from 'invariant';
@@ -386,6 +389,13 @@ function useTextInputStateSynchronization({
386389
*
387390
*/
388391
function InternalTextInput(props: TextInputProps): React.Node {
392+
const validKeysDown =
393+
((props: any).validKeysDown: ?$ReadOnlyArray<LegacyHandledKeyEvent>);
394+
const validKeysUp =
395+
((props: any).validKeysUp: ?$ReadOnlyArray<LegacyHandledKeyEvent>);
396+
const propsWithoutLegacyKeyProps = ({...props}: any);
397+
delete propsWithoutLegacyKeyProps.validKeysDown;
398+
delete propsWithoutLegacyKeyProps.validKeysUp;
389399
const {
390400
'aria-busy': ariaBusy,
391401
'aria-checked': ariaChecked,
@@ -400,7 +410,17 @@ function InternalTextInput(props: TextInputProps): React.Node {
400410
selectionHandleColor,
401411
cursorColor,
402412
...otherProps
403-
} = props;
413+
} = propsWithoutLegacyKeyProps;
414+
const normalizedKeyDownEvents =
415+
propsWithoutLegacyKeyProps.keyDownEvents ??
416+
normalizeLegacyHandledKeyEvents(validKeysDown);
417+
const normalizedKeyUpEvents =
418+
propsWithoutLegacyKeyProps.keyUpEvents ??
419+
normalizeLegacyHandledKeyEvents(validKeysUp);
420+
const isUsingLegacyKeyDownProp =
421+
propsWithoutLegacyKeyProps.keyDownEvents == null && validKeysDown != null;
422+
const isUsingLegacyKeyUpProp =
423+
propsWithoutLegacyKeyProps.keyUpEvents == null && validKeysUp != null;
404424

405425
const inputRef = useRef<null | TextInputInstance>(null);
406426

@@ -582,9 +602,9 @@ function InternalTextInput(props: TextInputProps): React.Node {
582602

583603
// [macOS
584604
const _onKeyDown = (event: KeyEvent) => {
585-
const keyDownEvents = props.keyDownEvents;
586-
if (keyDownEvents != null && !event.isPropagationStopped()) {
587-
const isHandled = keyDownEvents.some(
605+
let isHandled = false;
606+
if (normalizedKeyDownEvents != null && !event.isPropagationStopped()) {
607+
isHandled = normalizedKeyDownEvents.some(
588608
({key, metaKey, ctrlKey, altKey, shiftKey}: HandledKeyEvent) => {
589609
return (
590610
event.nativeEvent.key === key &&
@@ -595,17 +615,19 @@ function InternalTextInput(props: TextInputProps): React.Node {
595615
);
596616
},
597617
);
598-
if (isHandled === true) {
618+
if (isHandled === true && !isUsingLegacyKeyDownProp) {
599619
event.stopPropagation();
600620
}
601621
}
602-
props.onKeyDown?.(event);
622+
if (!isUsingLegacyKeyDownProp || isHandled) {
623+
propsWithoutLegacyKeyProps.onKeyDown?.(event);
624+
}
603625
};
604626

605627
const _onKeyUp = (event: KeyEvent) => {
606-
const keyUpEvents = props.keyUpEvents;
607-
if (keyUpEvents != null && !event.isPropagationStopped()) {
608-
const isHandled = keyUpEvents.some(
628+
let isHandled = false;
629+
if (normalizedKeyUpEvents != null && !event.isPropagationStopped()) {
630+
isHandled = normalizedKeyUpEvents.some(
609631
({key, metaKey, ctrlKey, altKey, shiftKey}: HandledKeyEvent) => {
610632
return (
611633
event.nativeEvent.key === key &&
@@ -616,11 +638,13 @@ function InternalTextInput(props: TextInputProps): React.Node {
616638
);
617639
},
618640
);
619-
if (isHandled === true) {
641+
if (isHandled === true && !isUsingLegacyKeyUpProp) {
620642
event.stopPropagation();
621643
}
622644
}
623-
props.onKeyUp?.(event);
645+
if (!isUsingLegacyKeyUpProp || isHandled) {
646+
propsWithoutLegacyKeyProps.onKeyUp?.(event);
647+
}
624648
};
625649
// macOS]
626650

@@ -773,6 +797,8 @@ function InternalTextInput(props: TextInputProps): React.Node {
773797
caretHidden={caretHidden}
774798
dataDetectorTypes={props.dataDetectorTypes}
775799
focusable={tabIndex !== undefined ? !tabIndex : focusable}
800+
keyDownEvents={normalizedKeyDownEvents}
801+
keyUpEvents={normalizedKeyUpEvents}
776802
mostRecentEventCount={mostRecentEventCount}
777803
nativeID={id ?? props.nativeID}
778804
numberOfLines={props.rows ?? props.numberOfLines}

packages/react-native/Libraries/Components/View/View.js

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ import type {ViewProps} from './ViewPropTypes';
1313

1414
import * as ReactNativeFeatureFlags from '../../../src/private/featureflags/ReactNativeFeatureFlags';
1515
import TextAncestorContext from '../../Text/TextAncestorContext';
16+
import normalizeLegacyHandledKeyEvents, {
17+
type LegacyHandledKeyEvent,
18+
} from '../../Utilities/normalizeLegacyHandledKeyEvents';
1619
import ViewNativeComponent from './ViewNativeComponent';
1720
import * as React from 'react';
1821
import {use} from 'react';
@@ -29,14 +32,31 @@ export default component View(
2932
...props: ViewProps
3033
) {
3134
const hasTextAncestor = use(TextAncestorContext);
35+
const validKeysDown =
36+
((props: any).validKeysDown: ?$ReadOnlyArray<LegacyHandledKeyEvent>);
37+
const validKeysUp =
38+
((props: any).validKeysUp: ?$ReadOnlyArray<LegacyHandledKeyEvent>);
39+
const propsWithoutLegacyKeyProps = ({...props}: any);
40+
delete propsWithoutLegacyKeyProps.validKeysDown;
41+
delete propsWithoutLegacyKeyProps.validKeysUp;
42+
const normalizedKeyDownEvents =
43+
propsWithoutLegacyKeyProps.keyDownEvents ??
44+
normalizeLegacyHandledKeyEvents(validKeysDown);
45+
const normalizedKeyUpEvents =
46+
propsWithoutLegacyKeyProps.keyUpEvents ??
47+
normalizeLegacyHandledKeyEvents(validKeysUp);
48+
const isUsingLegacyKeyDownProp =
49+
propsWithoutLegacyKeyProps.keyDownEvents == null && validKeysDown != null;
50+
const isUsingLegacyKeyUpProp =
51+
propsWithoutLegacyKeyProps.keyUpEvents == null && validKeysUp != null;
3252

3353
let actualView;
3454

3555
// [macOS
3656
const _onKeyDown = (event: KeyEvent) => {
37-
const keyDownEvents = props.keyDownEvents;
38-
if (keyDownEvents != null && !event.isPropagationStopped()) {
39-
const isHandled = keyDownEvents.some(
57+
let isHandled = false;
58+
if (normalizedKeyDownEvents != null && !event.isPropagationStopped()) {
59+
isHandled = normalizedKeyDownEvents.some(
4060
({key, metaKey, ctrlKey, altKey, shiftKey}: HandledKeyEvent) => {
4161
return (
4262
event.nativeEvent.key === key &&
@@ -47,17 +67,19 @@ export default component View(
4767
);
4868
},
4969
);
50-
if (isHandled === true) {
70+
if (isHandled === true && !isUsingLegacyKeyDownProp) {
5171
event.stopPropagation();
5272
}
5373
}
54-
props.onKeyDown?.(event);
74+
if (!isUsingLegacyKeyDownProp || isHandled) {
75+
propsWithoutLegacyKeyProps.onKeyDown?.(event);
76+
}
5577
};
5678

5779
const _onKeyUp = (event: KeyEvent) => {
58-
const keyUpEvents = props.keyUpEvents;
59-
if (keyUpEvents != null && !event.isPropagationStopped()) {
60-
const isHandled = keyUpEvents.some(
80+
let isHandled = false;
81+
if (normalizedKeyUpEvents != null && !event.isPropagationStopped()) {
82+
isHandled = normalizedKeyUpEvents.some(
6183
({key, metaKey, ctrlKey, altKey, shiftKey}: HandledKeyEvent) => {
6284
return (
6385
event.nativeEvent.key === key &&
@@ -68,11 +90,13 @@ export default component View(
6890
);
6991
},
7092
);
71-
if (isHandled === true) {
93+
if (isHandled === true && !isUsingLegacyKeyUpProp) {
7294
event.stopPropagation();
7395
}
7496
}
75-
props.onKeyUp?.(event);
97+
if (!isUsingLegacyKeyUpProp || isHandled) {
98+
propsWithoutLegacyKeyProps.onKeyUp?.(event);
99+
}
76100
};
77101
// macOS]
78102

@@ -96,11 +120,20 @@ export default component View(
96120
id,
97121
tabIndex,
98122
...otherProps
99-
} = props;
123+
} = propsWithoutLegacyKeyProps;
100124

101125
// Since we destructured props, we can now treat it as mutable
102126
const processedProps = otherProps as {...ViewProps};
103127

128+
processedProps.keyDownEvents = normalizedKeyDownEvents;
129+
processedProps.keyUpEvents = normalizedKeyUpEvents;
130+
if (processedProps.onKeyDown != null) {
131+
processedProps.onKeyDown = _onKeyDown;
132+
}
133+
if (processedProps.onKeyUp != null) {
134+
processedProps.onKeyUp = _onKeyUp;
135+
}
136+
104137
const parsedAriaLabelledBy = ariaLabelledBy?.split(/\s*,\s*/g);
105138
if (parsedAriaLabelledBy !== undefined) {
106139
processedProps.accessibilityLabelledBy = parsedAriaLabelledBy;
@@ -195,7 +228,7 @@ export default component View(
195228
nativeID,
196229
tabIndex,
197230
...otherProps
198-
} = props;
231+
} = propsWithoutLegacyKeyProps;
199232
const _accessibilityLabelledBy =
200233
ariaLabelledBy?.split(/\s*,\s*/g) ?? accessibilityLabelledBy;
201234

@@ -247,6 +280,8 @@ export default component View(
247280
: importantForAccessibility
248281
}
249282
nativeID={id ?? nativeID}
283+
keyDownEvents={normalizedKeyDownEvents}
284+
keyUpEvents={normalizedKeyUpEvents}
250285
// $FlowFixMe[exponential-spread]
251286
{...(otherProps.onKeyDown && {onKeyDown: _onKeyDown})} // [macOS]
252287
{...(otherProps.onKeyUp && {onKeyUp: _onKeyUp})} // [macOS]
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow strict-local
8+
* @format
9+
*/
10+
11+
import type {HandledKeyEvent} from '../Types/CoreEventTypes';
12+
13+
export type LegacyHandledKeyEvent = string | HandledKeyEvent;
14+
15+
function expandLegacyHandledKeyEvent(
16+
legacyHandledKeyEvent: LegacyHandledKeyEvent,
17+
): Array<HandledKeyEvent> {
18+
if (typeof legacyHandledKeyEvent !== 'string') {
19+
return [legacyHandledKeyEvent];
20+
}
21+
22+
const expandedHandledKeyEvents = [];
23+
for (const metaKey of [false, true]) {
24+
for (const ctrlKey of [false, true]) {
25+
for (const altKey of [false, true]) {
26+
for (const shiftKey of [false, true]) {
27+
expandedHandledKeyEvents.push({
28+
altKey,
29+
ctrlKey,
30+
key: legacyHandledKeyEvent,
31+
metaKey,
32+
shiftKey,
33+
});
34+
}
35+
}
36+
}
37+
}
38+
39+
return expandedHandledKeyEvents;
40+
}
41+
42+
export default function normalizeLegacyHandledKeyEvents(
43+
legacyHandledKeyEvents: ?$ReadOnlyArray<LegacyHandledKeyEvent>,
44+
): void | Array<HandledKeyEvent> {
45+
if (legacyHandledKeyEvents == null) {
46+
return undefined;
47+
}
48+
49+
const normalizedHandledKeyEvents = [];
50+
for (const legacyHandledKeyEvent of legacyHandledKeyEvents) {
51+
normalizedHandledKeyEvents.push(
52+
...expandLegacyHandledKeyEvent(legacyHandledKeyEvent),
53+
);
54+
}
55+
56+
return normalizedHandledKeyEvents;
57+
}

0 commit comments

Comments
 (0)