Skip to content

Commit

Permalink
activeCursor prop (#2550)
Browse files Browse the repository at this point in the history
## 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>
  • Loading branch information
m-bert and m-bert authored Jul 20, 2023
1 parent b0b70c4 commit a82e859
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 7 deletions.
4 changes: 4 additions & 0 deletions docs/docs/api/gestures/base-gesture-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,7 @@ Adds a gesture that should be recognized simultaneously with this one.
Adds a relation requiring another gesture to fail, before this one can activate.

**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.

### `activeCursor(value)` (**web only**)

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"`.
2 changes: 1 addition & 1 deletion docs/docs/api/gestures/gesture-detector.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ Starting with Reanimated-2.3.0-beta.4 Gesture Handler will provide a [StateManag

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

This parameter allows to specify which `userSelect` property should be applied to underlying view. Possible values are `"none" | "auto" | "text"`. Defaults to `"none"`.
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"`.
6 changes: 5 additions & 1 deletion docs/docs/gesture-handlers/api/common-gh.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,11 @@ Specifying `width` or `height` is useful if we only want the gesture to activate

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

This parameter allows to specify which `userSelect` property should be applied to underlying view. Possible values are `"none" | "auto" | "text"`. Defaults to `"none"`.
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"`.

### `activeCursor` (**web only**)

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"`.

### `onGestureEvent`

Expand Down
9 changes: 9 additions & 0 deletions src/components/DrawerLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
GestureEvent,
HandlerStateChangeEvent,
UserSelect,
ActiveCursor,
} from '../handlers/gestureHandlerCommon';
import {
PanGestureHandler,
Expand Down Expand Up @@ -165,6 +166,13 @@ export interface DrawerLayoutProps {
* Values: 'none'|'text'|'auto'
*/
userSelect?: UserSelect;

/**
* @default 'auto'
* Defines which cursor property should be used when gesture activates.
* Values: see CSS cursor values
*/
activeCursor?: ActiveCursor;
}

export type DrawerLayoutState = {
Expand Down Expand Up @@ -691,6 +699,7 @@ export default class DrawerLayout extends Component<
<PanGestureHandler
// @ts-ignore could be fixed in handler types
userSelect={this.props.userSelect}
activeCursor={this.props.activeCursor}
ref={this.setPanGestureRef}
hitSlop={hitSlop}
activeOffsetX={gestureOrientation * minSwipeDistance!}
Expand Down
39 changes: 39 additions & 0 deletions src/handlers/gestureHandlerCommon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const commonProps = [
'hitSlop',
'cancelsTouchesInView',
'userSelect',
'activeCursor',
] as const;

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

export type UserSelect = 'none' | 'auto' | 'text';
export type ActiveCursor =
| 'auto'
| 'default'
| 'none'
| 'context-menu'
| 'help'
| 'pointer'
| 'progress'
| 'wait'
| 'cell'
| 'crosshair'
| 'text'
| 'vertical-text'
| 'alias'
| 'copy'
| 'move'
| 'no-drop'
| 'not-allowed'
| 'grab'
| 'grabbing'
| 'e-resize'
| 'n-resize'
| 'ne-resize'
| 'nw-resize'
| 's-resize'
| 'se-resize'
| 'sw-resize'
| 'w-resize'
| 'ew-resize'
| 'ns-resize'
| 'nesw-resize'
| 'nwse-resize'
| 'col-resize'
| 'row-resize'
| 'all-scroll'
| 'zoom-in'
| 'zoom-out';

//TODO(TS) events in handlers

Expand Down Expand Up @@ -105,6 +143,7 @@ export type CommonGestureConfig = {
shouldCancelWhenOutside?: boolean;
hitSlop?: HitSlop;
userSelect?: UserSelect;
activeCursor?: ActiveCursor;
};

// Events payloads are types instead of interfaces due to TS limitation.
Expand Down
6 changes: 6 additions & 0 deletions src/handlers/gestures/gesture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
GestureTouchEvent,
GestureStateChangeEvent,
GestureUpdateEvent,
ActiveCursor,
} from '../gestureHandlerCommon';
import { getNextHandlerTag } from '../handlersRegistry';
import { GestureStateManagerType } from './gestureStateManager';
Expand Down Expand Up @@ -250,6 +251,11 @@ export abstract class BaseGesture<
return this;
}

activeCursor(activeCursor: ActiveCursor) {
this.config.activeCursor = activeCursor;
return this;
}

runOnJS(runOnJS: boolean) {
this.config.runOnJS = runOnJS;
return this;
Expand Down
41 changes: 37 additions & 4 deletions src/web/handlers/GestureHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,18 @@ export default abstract class GestureHandler {
this.currentState === State.ACTIVE ||
this.currentState === State.BEGAN
) {
// Here the order of this if statement and moveToState call is important.
// At this point we can use currentState as previuos state, because immediately after changing cursor we call moveToState method.
// By checking whether previous state was ACTIVE, we can decide if we should reset the cursor or not.
if (
this.config.activeCursor &&
this.config.activeCursor !== 'auto' &&
this.currentState === State.ACTIVE
) {
this.view.style.cursor = 'auto';
}

this.moveToState(State.FAILED, sendIfDisabled);
this.view.style.cursor = 'auto';
}

this.resetProgress();
Expand All @@ -184,8 +194,17 @@ export default abstract class GestureHandler {
this.currentState === State.BEGAN
) {
this.onCancel();

// Same as above - order matters
if (
this.config.activeCursor &&
this.config.activeCursor !== 'auto' &&
this.currentState === State.ACTIVE
) {
this.view.style.cursor = 'auto';
}

this.moveToState(State.CANCELLED, sendIfDisabled);
this.view.style.cursor = 'auto';
}
}

Expand All @@ -195,7 +214,13 @@ export default abstract class GestureHandler {
this.currentState === State.BEGAN
) {
this.moveToState(State.ACTIVE);
this.view.style.cursor = 'grab';

if (
(!this.view.style.cursor || this.view.style.cursor === 'auto') &&
this.config.activeCursor
) {
this.view.style.cursor = this.config.activeCursor;
}
}
}

Expand All @@ -204,8 +229,16 @@ export default abstract class GestureHandler {
this.currentState === State.BEGAN ||
this.currentState === State.ACTIVE
) {
// Same as above - order matters
if (
this.config.activeCursor &&
this.config.activeCursor !== 'auto' &&
this.currentState === State.ACTIVE
) {
this.view.style.cursor = 'auto';
}

this.moveToState(State.END);
this.view.style.cursor = 'auto';
}

this.resetProgress();
Expand Down
4 changes: 3 additions & 1 deletion src/web/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { UserSelect } from '../handlers/gestureHandlerCommon';
import { UserSelect, ActiveCursor } from '../handlers/gestureHandlerCommon';
import { Directions } from '../Directions';
import { State } from '../State';

Expand All @@ -22,6 +22,7 @@ type ConfigArgs =
| boolean
| HitSlop
| UserSelect
| ActiveCursor
| Directions
| Handler[]
| null
Expand All @@ -34,6 +35,7 @@ export interface Config extends Record<string, ConfigArgs> {
hitSlop?: HitSlop;
shouldCancelWhenOutside?: boolean;
userSelect?: UserSelect;
activeCursor?: ActiveCursor;

activateAfterLongPress?: number;
failOffsetXStart?: number;
Expand Down

0 comments on commit a82e859

Please sign in to comment.