Skip to content

Commit a964766

Browse files
committed
Merge branch 'main' of github.com:adobe-private/react-spectrum-v3
2 parents 16fc29a + 2e7c7a5 commit a964766

File tree

5 files changed

+197
-8
lines changed

5 files changed

+197
-8
lines changed

packages/@react-aria/interactions/docs/useHover.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ but `:hover` is problematic on touch devices due to mouse emulation in mobile br
4949
and device, `:hover` may never apply, or may apply continuously until the user touches another element.
5050
`useHover` only applies when the pointer is truly capable of hovering, and emulated mouse events are ignored.
5151

52+
Read our [blog post](/blog/building-a-button-part-2.html) about the complexities of hover event handling to learn more.
53+
5254
## Usage
5355

5456
`useHover` returns props that you should spread onto the target element:
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
<!-- Copyright 2020 Adobe. All rights reserved.
2+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
3+
you may not use this file except in compliance with the License. You may obtain a copy
4+
of the License at http://www.apache.org/licenses/LICENSE-2.0
5+
Unless required by applicable law or agreed to in writing, software distributed under
6+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
7+
OF ANY KIND, either express or implied. See the License for the specific language
8+
governing permissions and limitations under the License. -->
9+
10+
import {Layout} from '@react-spectrum/docs';
11+
export default Layout;
12+
13+
import docs from 'docs:@react-aria/interactions';
14+
import typesDocs from 'docs:@react-types/shared/src/events.d.ts';
15+
import {HeaderInfo, FunctionAPI, TypeContext, InterfaceType, TypeLink} from '@react-spectrum/docs';
16+
import packageData from '@react-aria/interactions/package.json';
17+
18+
```jsx import
19+
import {useLongPress, usePress} from '@react-aria/interactions';
20+
```
21+
22+
---
23+
category: Interactions
24+
keywords: [long press interactions, long press events, long press gesture, aria]
25+
---
26+
27+
# useLongPress
28+
29+
<p>{docs.exports.useLongPress.description}</p>
30+
31+
<HeaderInfo
32+
packageData={packageData}
33+
componentNames={['useLongPress']} />
34+
35+
## API
36+
37+
<FunctionAPI function={docs.exports.useLongPress} links={docs.links} />
38+
39+
## Features
40+
41+
`useLongPress` handles long press interactions across both mouse and touch devices. A long press is triggered when a user presses
42+
and holds their pointer over a target for a minimum period of time. If the user moves their pointer off of the target before the
43+
time threshold, the interaction is canceled. Once a long press event is triggered, other pointer interactions that may be active
44+
such as `usePress` and `useMove` will be canceled so that only the long press is activated.
45+
46+
* Handles mouse and touch events
47+
* Uses pointer events where available, with fallbacks to mouse and touch events
48+
* Ignores emulated mouse events in mobile browsers
49+
* Prevents text selection on touch devices while long pressing
50+
* Prevents browser and OS context menus from appearing while long pressing
51+
* Customizable time threshold for long press
52+
* Supports an accessibility description to indicate to assistive technology users that a long press action is available
53+
54+
## Usage
55+
56+
`useLongPress` returns props that you should spread onto the target element:
57+
58+
<TypeContext.Provider value={docs.links}>
59+
<InterfaceType properties={docs.links[docs.exports.useLongPress.return.id].properties} />
60+
</TypeContext.Provider>
61+
62+
`useLongPress` supports the following event handlers and options:
63+
64+
<TypeContext.Provider value={docs.links}>
65+
<InterfaceType properties={docs.links[docs.exports.useLongPress.parameters[0].value.id].properties} />
66+
</TypeContext.Provider>
67+
68+
Each of these handlers is fired with a `LongPressEvent`, which exposes information about the target and the
69+
type of event that triggered the interaction.
70+
71+
<TypeContext.Provider value={typesDocs.links}>
72+
<InterfaceType properties={typesDocs.exports.LongPressEvent.properties} />
73+
</TypeContext.Provider>
74+
75+
## Example
76+
77+
This example shows a button that has both a normal press action using [usePress](usePress.html), as well as a long
78+
press action using `useLongPress`. Pressing the button will set the mode to "Normal speed", and long pressing it will
79+
set the mode to "Hyper speed". All of the emitted events are also logged below. Note that when long pressing the button,
80+
only a long press is emitted, and no normal press is emitted on pointer up.
81+
82+
**Note**: this example does not have a keyboard accessible way to trigger the long press action. Because the method of triggering
83+
this action will differ depending on the component, it is outside the scope of `useLongPress`. Make sure to implement a keyboard
84+
friendly alternative to all long press interactions if you are using this hook directly.
85+
86+
```tsx example
87+
import {mergeProps} from '@react-aria/utils';
88+
89+
function Example() {
90+
let [events, setEvents] = React.useState([]);
91+
let [mode, setMode] = React.useState('Activate');
92+
let {longPressProps} = useLongPress({
93+
accessibilityDescription: 'Long press to activate hyper speed',
94+
onLongPressStart: e => setEvents(
95+
events => [`long press start with ${e.pointerType}`, ...events]
96+
),
97+
onLongPressEnd: e => setEvents(
98+
events => [`long press end with ${e.pointerType}`, ...events]
99+
),
100+
onLongPress: e => {
101+
setMode('Hyper speed');
102+
setEvents(
103+
events => [`long press with ${e.pointerType}`, ...events]
104+
);
105+
}
106+
});
107+
108+
let {pressProps} = usePress({
109+
onPress: e => {
110+
setMode('Normal speed');
111+
setEvents(
112+
events => [`press with ${e.pointerType}`, ...events]
113+
);
114+
}
115+
});
116+
117+
return (
118+
<>
119+
<button {...mergeProps(pressProps, longPressProps)}>{mode}</button>
120+
<ul
121+
style={{
122+
maxHeight: '200px',
123+
overflow: 'auto'
124+
}}>
125+
{events.map((e, i) => <li key={i}>{e}</li>)}
126+
</ul>
127+
</>
128+
);
129+
}
130+
```

packages/@react-aria/interactions/docs/usePress.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ the visual appearance of the target. If the pointer is released over the target,
5252
* Handles canceling press interactions on scroll
5353
* Normalizes many cross browser inconsistencies
5454

55+
Read our [blog post](/blog/building-a-button-part-1.html) about the complexities of press event handling to learn more.
56+
5557
## Usage
5658

5759
`usePress` returns props that you should spread onto the target element, along with the current press state:

packages/@react-aria/table/docs/useTable.mdx

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ HTML tables are meant for static content, rather than tables with rich interacti
6464
* Single, multiple, or no row selection via mouse, touch, or keyboard interactions
6565
* Support for disabled rows, which cannot be selected
6666
* Optional support for checkboxes in each row for selection, as well as in the header to select all rows
67+
* Support for both `toggle` and `replace` selection behaviors
68+
* Support for row actions via double click, <kbd>Enter</kbd> key, or tapping
69+
* Long press to enter selection mode on touch when there is both selection and row actions
6770
* Column sorting support
6871
* Async loading, infinite scrolling, filtering, and sorting support
6972
* Support for column groups via nested columns
@@ -132,7 +135,12 @@ import {useRef} from 'react';
132135
import {useFocusRing} from '@react-aria/focus';
133136

134137
function Table(props) {
135-
let state = useTableState({...props, showSelectionCheckboxes: props.selectionMode === 'multiple'});
138+
let {selectionMode, selectionBehavior, onAction} = props;
139+
let state = useTableState({
140+
...props,
141+
showSelectionCheckboxes: selectionMode === 'multiple' && selectionBehavior !== 'replace'
142+
});
143+
136144
let ref = useRef();
137145
let {collection} = state;
138146
let {gridProps} = useTable(props, state, ref);
@@ -152,7 +160,7 @@ function Table(props) {
152160
</TableRowGroup>
153161
<TableRowGroup type="tbody">
154162
{[...collection.body.childNodes].map(row => (
155-
<TableRow key={row.key} item={row} state={state}>
163+
<TableRow key={row.key} item={row} state={state} onAction={onAction}>
156164
{[...row.childNodes].map(cell =>
157165
cell.props.isSelectionCell
158166
? <TableCheckboxCell key={cell.key} cell={cell} state={state} />
@@ -251,27 +259,32 @@ Now that we've covered the table header, let's move on to the body. We'll use
251259
the <TypeLink links={docs.links} type={docs.exports.useTableRow} /> hook to render each row in the table.
252260
Table rows can be focused and navigated to using the keyboard via the arrow keys. In addition, table rows
253261
can optionally support selection via mouse, touch, or keyboard. Clicking, tapping, or pressing the <kbd>Space</kbd>
254-
key anywhere in the row selects it.
262+
key anywhere in the row selects it. Row actions are also supported via the provided `onAction` prop. See [below](#row-and-cell-actions) for details.
255263

256264
We'll use the <TypeLink links={selectionDocs.links} type={selectionDocs.exports.SelectionManager} /> object exposed
257265
by the `state` to determine if a row is selected, and render a pink background if so. We'll also use the <TypeLink links={focusDocs.links} type={focusDocs.exports.useFocusRing} />
258266
hook to render a focus ring when the user navigates to the row with the keyboard.
259267

260268
```tsx example export=true render=false
261-
function TableRow({item, children, state}) {
269+
function TableRow({item, children, state, onAction}) {
262270
let ref = useRef();
263271
let isSelected = state.selectionManager.isSelected(item.key);
264-
let {rowProps} = useTableRow({node: item}, state, ref);
272+
let {rowProps, isPressed} = useTableRow({
273+
node: item,
274+
onAction: onAction && (() => onAction(item.key))
275+
}, state, ref);
265276
let {isFocusVisible, focusProps} = useFocusRing();
266277

267278
return (
268279
<tr
269280
style={{
270281
background: isSelected
271282
? 'blueviolet'
272-
: item.index % 2
273-
? 'var(--spectrum-alias-highlight-hover)'
274-
: 'none',
283+
: isPressed
284+
? 'var(--spectrum-global-color-gray-400)'
285+
: item.index % 2
286+
? 'var(--spectrum-alias-highlight-hover)'
287+
: 'none',
275288
color: isSelected ? 'white' : null,
276289
outline: isFocusVisible ? '2px solid orange' : 'none'
277290
}}
@@ -584,6 +597,26 @@ Note that you are responsible for the styling of disabled rows, however, the sel
584597
<PokemonTable selectionMode="multiple" disabledKeys={[3]} />
585598
```
586599

600+
### Selection behavior
601+
602+
By default, `useTable` uses the `"toggle"` selection behavior, which behaves like a checkbox group: clicking, tapping, or pressing the <kbd>Space</kbd> or <kbd>Enter</kbd> keys toggles selection for the focused row. Using the arrow keys moves focus but does not change selection. The `"toggle"` selection mode is often paired with a column of checkboxes in each row as an explicit affordance for selection.
603+
604+
When the `selectionBehavior` prop is set to `"replace"`, clicking a row with the mouse _replaces_ the selection with only that row. Using the arrow keys moves both focus and selection. To select multiple rows, modifier keys such as <kbd>Ctrl</kbd>, <kbd>Cmd</kbd>, and <kbd>Shift</kbd> can be used. To move focus without moving selection, the <kbd>Ctrl</kbd> key on Windows or the <kbd>Option</kbd> key on macOS can be held while pressing the arrow keys. Holding this modifier while pressing the <kbd>Space</kbd> key toggles selection for the focused row, which allows multiple selection of non-contiguous items. On touch screen devices, selection always behaves as toggle since modifier keys may not be available. This behavior emulates native platforms such as macOS and Windows, and is often used when checkboxes in each row are not desired.
605+
606+
```tsx example
607+
<PokemonTable selectionMode="multiple" selectionBehavior="replace" />
608+
```
609+
610+
### Row and cell actions
611+
612+
`useTable` may be used in use cases where users can perform actions on rows or cells, such as navigating into items to open them or get more details. In the default `"toggle"` selection behavior, it is recommended to use a link in the first column for navigation.
613+
614+
In the `"replace"` selection behavior, the `onAction` prop can be used to enable row and cell actions, which differ depending on the interaction method. When provided to `useTableRow` or `useTableCell`, double clicking with a mouse or pressing the <kbd>Enter</kbd> key triggers `onAction`, while single click and the <kbd>Space</kbd> key are reserved for selection. On touch devices, `onAction` becomes the primary tap interaction, and a long press enters into selection mode, which temporarily swaps the selection behavior to `"toggle"` to perform selection (you may wish to display checkboxes when this happens). Deselecting all items exits selection mode and reverts the selection behavior back to `"replace"`. Double clicking matches the behavior of desktop platforms like macOS and Windows, and a separate selection mode matches mobile platforms like iOS and Android.
615+
616+
```tsx example
617+
<PokemonTable selectionMode="multiple" selectionBehavior="replace" onAction={key => alert(`Opening item ${key}...`)} />
618+
```
619+
587620
### Sorting
588621

589622
Table supports sorting its data when a column header is pressed. To designate that a Column should support sorting, provide it with

packages/@react-spectrum/table/docs/TableView.mdx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,28 @@ You can disable specific rows by providing an array of keys to TableView via the
465465
<PokemonTable selectionMode="multiple" disabledKeys={[3]} />
466466
```
467467

468+
### Highlight selection
469+
470+
By default, TableView uses the checkbox selection style, which includes a column of checkboxes in each row for selection. When the `selectionStyle` prop is set to `"highlight"`, the checkboxes are hidden, and the selected rows are displayed with a highlighted background instead.
471+
472+
In addition to changing the appearance, the selection behavior also changes depending on the `selectionStyle` prop. In the default checkbox selection style, clicking, tapping, or pressing the <kbd>Space</kbd> or <kbd>Enter</kbd> keys toggles selection for the focused row. Using the arrow keys moves focus but does not change selection.
473+
474+
In the highlight selection style, however, clicking a row with the mouse _replaces_ the selection with only that row. Using the arrow keys moves both focus and selection. To select multiple rows, modifier keys such as <kbd>Ctrl</kbd>, <kbd>Cmd</kbd>, and <kbd>Shift</kbd> can be used. To move focus without moving selection, the <kbd>Ctrl</kbd> key on Windows or the <kbd>Option</kbd> key on macOS can be held while pressing the arrow keys. Holding this modifier while pressing the <kbd>Space</kbd> key toggles selection for the focused row, which allows multiple selection of non-contiguous items. On touch screen devices, selection always behaves as toggle since modifier keys may not be available. This behavior emulates native platforms such as macOS and Windows.
475+
476+
```tsx example
477+
<PokemonTable selectionMode="multiple" selectionStyle="highlight" />
478+
```
479+
480+
### Row actions
481+
482+
TableView may be used in use cases where users can perform actions on rows, such as navigating into items to open them or get more details. In the default checkbox selection style, it is recommended to use a [Link](Link.html) component in the first column for navigation.
483+
484+
In the highlight selection style, the `onAction` prop can be used to enable row actions, which differ depending on the interaction method. When provided, double clicking with a mouse or pressing the <kbd>Enter</kbd> key triggers `onAction`, while single click and the <kbd>Space</kbd> key are reserved for selection. On touch devices, `onAction` becomes the primary tap interaction, and a long press enters into selection mode, which displays checkboxes to perform selection. Deselecting all items exits selection mode and hides the checkboxes. Double clicking matches the behavior of desktop platforms like macOS and Windows, and a separate selection mode matches mobile platforms like iOS and Android.
485+
486+
```tsx example
487+
<PokemonTable selectionMode="multiple" selectionStyle="highlight" onAction={key => alert(`Opening item ${key}...`)} />
488+
```
489+
468490
## Sorting
469491

470492
TableView supports sorting its data when a column header is pressed. To designate that a Column should support sorting, provide it with

0 commit comments

Comments
 (0)