@@ -35,7 +35,7 @@ import type {
35
35
ViewToken ,
36
36
ViewabilityConfigCallbackPair ,
37
37
} from './ViewabilityHelper' ;
38
- import type { ScrollEvent } from '../Types/CoreEventTypes' ; // TODO(macOS GH#774)
38
+ import type { KeyEvent } from '../Types/CoreEventTypes' ; // TODO(macOS GH#774)
39
39
import {
40
40
VirtualizedListCellContextProvider ,
41
41
VirtualizedListContext ,
@@ -109,12 +109,24 @@ type OptionalProps = {|
109
109
* this for debugging purposes. Defaults to false.
110
110
*/
111
111
disableVirtualization ?: ?boolean ,
112
+ // [TODO(macOS GH#774)
112
113
/**
113
- * Handles key down events and updates selection based on the key event
114
+ * Allows you to 'select' a row using arrow keys. The selected row will have the prop `isSelected`
115
+ * passed in as true to it's renderItem / ListItemComponent. You can also imperatively select a row
116
+ * using the `selectRowAtIndex` method. You can set the initially selected row using the
117
+ * `initialSelectedIndex` prop.
118
+ * Keyboard Behavior:
119
+ * - ArrowUp: Select row above current selected row
120
+ * - ArrowDown: Select row below current selected row
121
+ * - Option+ArrowUp: Select the first row
122
+ * - Opton+ArrowDown: Select the last 'realized' row
123
+ * - Home: Scroll to top of list
124
+ * - End: Scroll to end of list
114
125
*
115
126
* @platform macos
116
127
*/
117
- enableSelectionOnKeyPress ?: ?boolean , // TODO(macOS GH#774)
128
+ enableSelectionOnKeyPress ?: ?boolean ,
129
+ // ]TODO(macOS GH#774)
118
130
/**
119
131
* A marker property for telling the list to re-render (since it implements `PureComponent`). If
120
132
* any of your `renderItem`, Header, Footer, etc. functions depend on anything outside of the
@@ -1309,14 +1321,16 @@ class VirtualizedList extends React.PureComponent<Props, State> {
1309
1321
}
1310
1322
1311
1323
_defaultRenderScrollComponent = props => {
1312
- let keyEventHandler = this . props . onScrollKeyDown ; // [TODO(macOS GH#774)
1313
- if ( ! keyEventHandler ) {
1314
- keyEventHandler = this . props . enableSelectionOnKeyPress
1315
- ? this . _handleKeyDown
1316
- : null ;
1317
- }
1324
+ // [TODO(macOS GH#774)
1318
1325
const preferredScrollerStyleDidChangeHandler =
1319
- this . props . onPreferredScrollerStyleDidChange ; // ]TODO(macOS GH#774)
1326
+ this . props . onPreferredScrollerStyleDidChange ;
1327
+
1328
+ const keyboardNavigationProps = {
1329
+ focusable : true ,
1330
+ validKeysDown : [ 'ArrowUp' , 'ArrowDown' , 'Home' , 'End' ] ,
1331
+ onKeyDown : this . _handleKeyDown ,
1332
+ } ;
1333
+ // ]TODO(macOS GH#774)
1320
1334
const onRefresh = props . onRefresh ;
1321
1335
if ( this . _isNestedWithSameOrientation ( ) ) {
1322
1336
// $FlowFixMe[prop-missing] - Typing ReactNativeComponent revealed errors
@@ -1333,8 +1347,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
1333
1347
< ScrollView
1334
1348
{ ...props }
1335
1349
// [TODO(macOS GH#774)
1336
- { ...( props . enableSelectionOnKeyPress && { focusable : true } ) }
1337
- onScrollKeyDown = { keyEventHandler }
1350
+ { ...( props . enableSelectionOnKeyPress && keyboardNavigationProps ) }
1338
1351
onPreferredScrollerStyleDidChange = {
1339
1352
preferredScrollerStyleDidChangeHandler
1340
1353
} // TODO(macOS GH#774)]
@@ -1356,8 +1369,8 @@ class VirtualizedList extends React.PureComponent<Props, State> {
1356
1369
// $FlowFixMe Invalid prop usage
1357
1370
< ScrollView
1358
1371
{ ...props }
1359
- { ... ( props . enableSelectionOnKeyPress && { focusable : true } ) } // [TODO(macOS GH#774)
1360
- onScrollKeyDown = { keyEventHandler }
1372
+ // [TODO(macOS GH#774)
1373
+ { ... ( props . enableSelectionOnKeyPress && keyboardNavigationProps ) }
1361
1374
onPreferredScrollerStyleDidChange = {
1362
1375
preferredScrollerStyleDidChangeHandler
1363
1376
} // TODO(macOS GH#774)]
@@ -1539,64 +1552,50 @@ class VirtualizedList extends React.PureComponent<Props, State> {
1539
1552
}
1540
1553
} ;
1541
1554
1542
- _handleKeyDown = ( event : ScrollEvent ) => {
1543
- if ( this . props . onScrollKeyDown ) {
1544
- this . props . onScrollKeyDown ( event ) ;
1545
- } else {
1546
- if ( Platform . OS === 'macos' ) {
1547
- // $FlowFixMe Cannot get e.nativeEvent because property nativeEvent is missing in Event
1548
- const nativeEvent = event . nativeEvent ;
1549
- const key = nativeEvent . key ;
1550
-
1551
- let prevIndex = - 1 ;
1552
- let newIndex = - 1 ;
1553
- if ( 'selectedRowIndex' in this . state ) {
1554
- prevIndex = this . state . selectedRowIndex ;
1555
- }
1555
+ _handleKeyDown = ( event : KeyEvent ) => {
1556
+ if ( Platform . OS === 'macos' ) {
1557
+ this . props . onKeyDown ?. ( event ) ;
1558
+ if ( event . defaultPrevented ) {
1559
+ return ;
1560
+ }
1561
+
1562
+ const nativeEvent = event . nativeEvent ;
1563
+ const key = nativeEvent . key ;
1556
1564
1557
- // const {data, getItem} = this.props;
1558
- if ( key === 'UP_ARROW' ) {
1565
+ let prevIndex = - 1 ;
1566
+ let newIndex = - 1 ;
1567
+ if ( 'selectedRowIndex' in this . state ) {
1568
+ prevIndex = this . state . selectedRowIndex ;
1569
+ }
1570
+
1571
+ // const {data, getItem} = this.props;
1572
+ if ( key = = = 'ArrowUp' ) {
1573
+ if ( nativeEvent . altKey ) {
1574
+ // Option+Up selects the first element
1575
+ newIndex = this . _selectRowAtIndex ( 0 ) ;
1576
+ } else {
1559
1577
newIndex = this . _selectRowAboveIndex ( prevIndex ) ;
1560
- this . _handleSelectionChange ( prevIndex , newIndex ) ;
1561
- } else if ( key = = = 'DOWN_ARROW' ) {
1578
+ }
1579
+ this . _handleSelectionChange ( prevIndex , newIndex ) ;
1580
+ } else if ( key = = = 'ArrowDown' ) {
1581
+ if ( nativeEvent . altKey ) {
1582
+ // Option+Down selects the last element
1583
+ newIndex = this . _selectRowAtIndex ( this . state . last ) ;
1584
+ } else {
1562
1585
newIndex = this . _selectRowBelowIndex ( prevIndex ) ;
1563
- this . _handleSelectionChange ( prevIndex , newIndex ) ;
1564
- } else if ( key = = = 'ENTER' ) {
1586
+ }
1587
+ this . _handleSelectionChange ( prevIndex , newIndex ) ;
1588
+ } else if ( key = = = 'Enter' ) {
1589
+ if ( this . props . onSelectionEntered ) {
1590
+ const item = this . props . getItem ( this . props . data , prevIndex ) ;
1565
1591
if ( this . props . onSelectionEntered ) {
1566
- const item = this . props . getItem ( this . props . data , prevIndex ) ;
1567
- if ( this . props . onSelectionEntered ) {
1568
- this . props . onSelectionEntered ( item ) ;
1569
- }
1592
+ this . props . onSelectionEntered ( item ) ;
1570
1593
}
1571
- } else if ( key = = = 'OPTION_UP' ) {
1572
- newIndex = this . _selectRowAtIndex ( 0 ) ;
1573
- this . _handleSelectionChange ( prevIndex , newIndex ) ;
1574
- } else if ( key = = = 'OPTION_DOWN' ) {
1575
- newIndex = this . _selectRowAtIndex ( this . state . last ) ;
1576
- this . _handleSelectionChange ( prevIndex , newIndex ) ;
1577
- } else if ( key = = = 'PAGE_UP' ) {
1578
- const maxY =
1579
- event . nativeEvent . contentSize . height -
1580
- event . nativeEvent . layoutMeasurement . height ;
1581
- const newOffset = Math . min (
1582
- maxY ,
1583
- nativeEvent . contentOffset . y + - nativeEvent . layoutMeasurement . height ,
1584
- ) ;
1585
- this . scrollToOffset ( { animated : true , offset : newOffset } ) ;
1586
- } else if ( key = = = 'PAGE_DOWN' ) {
1587
- const maxY =
1588
- event . nativeEvent . contentSize . height -
1589
- event . nativeEvent . layoutMeasurement . height ;
1590
- const newOffset = Math . min (
1591
- maxY ,
1592
- nativeEvent . contentOffset . y + nativeEvent . layoutMeasurement . height ,
1593
- ) ;
1594
- this . scrollToOffset ( { animated : true , offset : newOffset } ) ;
1595
- } else if ( key = = = 'HOME' ) {
1596
- this . scrollToOffset ( { animated : true , offset : 0 } ) ;
1597
- } else if ( key = = = 'END' ) {
1598
- this . scrollToEnd ( { animated : true } ) ;
1599
1594
}
1595
+ } else if ( key = = = 'Home' ) {
1596
+ this . scrollToOffset ( { animated : true , offset : 0 } ) ;
1597
+ } else if ( key = = = 'End' ) {
1598
+ this . scrollToEnd ( { animated : true } ) ;
1600
1599
}
1601
1600
}
1602
1601
} ;
0 commit comments