Skip to content

Commit a82e859

Browse files
authored
activeCursor prop (#2550)
## Description This PR adds `activeCursor` prop which allows users to change cursor appearance upon handler activation. Initially it was added in [2.6.1](https://github.com/software-mansion/react-native-gesture-handler/releases/tag/2.6.1) as a response to [this issue](#700). However, not everyone liked the idea (which is understandable), so we decided to turn this change into property. This way anyone who was using it still will be able to do so, and those who didn't like the change don't have to remove it on their own. Co-authored-by: Michał Bert <michal.bert@swmansion.com>
1 parent b0b70c4 commit a82e859

File tree

8 files changed

+104
-7
lines changed

8 files changed

+104
-7
lines changed

docs/docs/api/gestures/base-gesture-config.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,7 @@ Adds a gesture that should be recognized simultaneously with this one.
5858
Adds a relation requiring another gesture to fail, before this one can activate.
5959

6060
**IMPORTANT:** Note that this method only marks the relation between gestures, without [composing them](../../gesture-composition).[`GestureDetector`](gesture-detector) will not recognize the `otherGestures` and it needs to be added to another detector in order to be recognized.
61+
62+
### `activeCursor(value)` (**web only**)
63+
64+
This parameter allows to specify which cursor should be used when gesture activates. Supports all CSS cursor values (e.g. `"grab"`, `"zoom-in"`). Default value is set to `"auto"`.

docs/docs/api/gestures/gesture-detector.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,4 @@ Starting with Reanimated-2.3.0-beta.4 Gesture Handler will provide a [StateManag
2828

2929
### `userSelect` (**web only**)
3030

31-
This parameter allows to specify which `userSelect` property should be applied to underlying view. Possible values are `"none" | "auto" | "text"`. Defaults to `"none"`.
31+
This parameter allows to specify which `userSelect` property should be applied to underlying view. Possible values are `"none" | "auto" | "text"`. Default value is set to `"none"`.

docs/docs/gesture-handlers/api/common-gh.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,11 @@ Specifying `width` or `height` is useful if we only want the gesture to activate
6565

6666
### `userSelect` (**web only**)
6767

68-
This parameter allows to specify which `userSelect` property should be applied to underlying view. Possible values are `"none" | "auto" | "text"`. Defaults to `"none"`.
68+
This parameter allows to specify which `userSelect` property should be applied to underlying view. Possible values are `"none" | "auto" | "text"`. Default value is set to `"none"`.
69+
70+
### `activeCursor` (**web only**)
71+
72+
This parameter allows to specify which cursor should be used when gesture activates. Supports all CSS cursor values (e.g. `"grab"`, `"zoom-in"`). Default value is set to `"auto"`.
6973

7074
### `onGestureEvent`
7175

src/components/DrawerLayout.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
GestureEvent,
2828
HandlerStateChangeEvent,
2929
UserSelect,
30+
ActiveCursor,
3031
} from '../handlers/gestureHandlerCommon';
3132
import {
3233
PanGestureHandler,
@@ -165,6 +166,13 @@ export interface DrawerLayoutProps {
165166
* Values: 'none'|'text'|'auto'
166167
*/
167168
userSelect?: UserSelect;
169+
170+
/**
171+
* @default 'auto'
172+
* Defines which cursor property should be used when gesture activates.
173+
* Values: see CSS cursor values
174+
*/
175+
activeCursor?: ActiveCursor;
168176
}
169177

170178
export type DrawerLayoutState = {
@@ -691,6 +699,7 @@ export default class DrawerLayout extends Component<
691699
<PanGestureHandler
692700
// @ts-ignore could be fixed in handler types
693701
userSelect={this.props.userSelect}
702+
activeCursor={this.props.activeCursor}
694703
ref={this.setPanGestureRef}
695704
hitSlop={hitSlop}
696705
activeOffsetX={gestureOrientation * minSwipeDistance!}

src/handlers/gestureHandlerCommon.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const commonProps = [
1919
'hitSlop',
2020
'cancelsTouchesInView',
2121
'userSelect',
22+
'activeCursor',
2223
] as const;
2324

2425
const componentInteractionProps = ['waitFor', 'simultaneousHandlers'] as const;
@@ -64,6 +65,43 @@ export type HitSlop =
6465
| Record<'height' | 'bottom', number>;
6566

6667
export type UserSelect = 'none' | 'auto' | 'text';
68+
export type ActiveCursor =
69+
| 'auto'
70+
| 'default'
71+
| 'none'
72+
| 'context-menu'
73+
| 'help'
74+
| 'pointer'
75+
| 'progress'
76+
| 'wait'
77+
| 'cell'
78+
| 'crosshair'
79+
| 'text'
80+
| 'vertical-text'
81+
| 'alias'
82+
| 'copy'
83+
| 'move'
84+
| 'no-drop'
85+
| 'not-allowed'
86+
| 'grab'
87+
| 'grabbing'
88+
| 'e-resize'
89+
| 'n-resize'
90+
| 'ne-resize'
91+
| 'nw-resize'
92+
| 's-resize'
93+
| 'se-resize'
94+
| 'sw-resize'
95+
| 'w-resize'
96+
| 'ew-resize'
97+
| 'ns-resize'
98+
| 'nesw-resize'
99+
| 'nwse-resize'
100+
| 'col-resize'
101+
| 'row-resize'
102+
| 'all-scroll'
103+
| 'zoom-in'
104+
| 'zoom-out';
67105

68106
//TODO(TS) events in handlers
69107

@@ -105,6 +143,7 @@ export type CommonGestureConfig = {
105143
shouldCancelWhenOutside?: boolean;
106144
hitSlop?: HitSlop;
107145
userSelect?: UserSelect;
146+
activeCursor?: ActiveCursor;
108147
};
109148

110149
// Events payloads are types instead of interfaces due to TS limitation.

src/handlers/gestures/gesture.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
GestureTouchEvent,
77
GestureStateChangeEvent,
88
GestureUpdateEvent,
9+
ActiveCursor,
910
} from '../gestureHandlerCommon';
1011
import { getNextHandlerTag } from '../handlersRegistry';
1112
import { GestureStateManagerType } from './gestureStateManager';
@@ -250,6 +251,11 @@ export abstract class BaseGesture<
250251
return this;
251252
}
252253

254+
activeCursor(activeCursor: ActiveCursor) {
255+
this.config.activeCursor = activeCursor;
256+
return this;
257+
}
258+
253259
runOnJS(runOnJS: boolean) {
254260
this.config.runOnJS = runOnJS;
255261
return this;

src/web/handlers/GestureHandler.ts

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,18 @@ export default abstract class GestureHandler {
167167
this.currentState === State.ACTIVE ||
168168
this.currentState === State.BEGAN
169169
) {
170+
// Here the order of this if statement and moveToState call is important.
171+
// At this point we can use currentState as previuos state, because immediately after changing cursor we call moveToState method.
172+
// By checking whether previous state was ACTIVE, we can decide if we should reset the cursor or not.
173+
if (
174+
this.config.activeCursor &&
175+
this.config.activeCursor !== 'auto' &&
176+
this.currentState === State.ACTIVE
177+
) {
178+
this.view.style.cursor = 'auto';
179+
}
180+
170181
this.moveToState(State.FAILED, sendIfDisabled);
171-
this.view.style.cursor = 'auto';
172182
}
173183

174184
this.resetProgress();
@@ -184,8 +194,17 @@ export default abstract class GestureHandler {
184194
this.currentState === State.BEGAN
185195
) {
186196
this.onCancel();
197+
198+
// Same as above - order matters
199+
if (
200+
this.config.activeCursor &&
201+
this.config.activeCursor !== 'auto' &&
202+
this.currentState === State.ACTIVE
203+
) {
204+
this.view.style.cursor = 'auto';
205+
}
206+
187207
this.moveToState(State.CANCELLED, sendIfDisabled);
188-
this.view.style.cursor = 'auto';
189208
}
190209
}
191210

@@ -195,7 +214,13 @@ export default abstract class GestureHandler {
195214
this.currentState === State.BEGAN
196215
) {
197216
this.moveToState(State.ACTIVE);
198-
this.view.style.cursor = 'grab';
217+
218+
if (
219+
(!this.view.style.cursor || this.view.style.cursor === 'auto') &&
220+
this.config.activeCursor
221+
) {
222+
this.view.style.cursor = this.config.activeCursor;
223+
}
199224
}
200225
}
201226

@@ -204,8 +229,16 @@ export default abstract class GestureHandler {
204229
this.currentState === State.BEGAN ||
205230
this.currentState === State.ACTIVE
206231
) {
232+
// Same as above - order matters
233+
if (
234+
this.config.activeCursor &&
235+
this.config.activeCursor !== 'auto' &&
236+
this.currentState === State.ACTIVE
237+
) {
238+
this.view.style.cursor = 'auto';
239+
}
240+
207241
this.moveToState(State.END);
208-
this.view.style.cursor = 'auto';
209242
}
210243

211244
this.resetProgress();

src/web/interfaces.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { UserSelect } from '../handlers/gestureHandlerCommon';
1+
import { UserSelect, ActiveCursor } from '../handlers/gestureHandlerCommon';
22
import { Directions } from '../Directions';
33
import { State } from '../State';
44

@@ -22,6 +22,7 @@ type ConfigArgs =
2222
| boolean
2323
| HitSlop
2424
| UserSelect
25+
| ActiveCursor
2526
| Directions
2627
| Handler[]
2728
| null
@@ -34,6 +35,7 @@ export interface Config extends Record<string, ConfigArgs> {
3435
hitSlop?: HitSlop;
3536
shouldCancelWhenOutside?: boolean;
3637
userSelect?: UserSelect;
38+
activeCursor?: ActiveCursor;
3739

3840
activateAfterLongPress?: number;
3941
failOffsetXStart?: number;

0 commit comments

Comments
 (0)