diff --git a/docs/docs/api/gestures/base-gesture-config.md b/docs/docs/api/gestures/base-gesture-config.md index 37939c0092..4c45278d30 100644 --- a/docs/docs/api/gestures/base-gesture-config.md +++ b/docs/docs/api/gestures/base-gesture-config.md @@ -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"`. diff --git a/docs/docs/api/gestures/gesture-detector.md b/docs/docs/api/gestures/gesture-detector.md index 98a5091b00..6533c65d57 100644 --- a/docs/docs/api/gestures/gesture-detector.md +++ b/docs/docs/api/gestures/gesture-detector.md @@ -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"`. diff --git a/docs/docs/gesture-handlers/api/common-gh.md b/docs/docs/gesture-handlers/api/common-gh.md index 7822601d5f..e933c6334d 100644 --- a/docs/docs/gesture-handlers/api/common-gh.md +++ b/docs/docs/gesture-handlers/api/common-gh.md @@ -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` diff --git a/src/components/DrawerLayout.tsx b/src/components/DrawerLayout.tsx index 7853951e90..479b3618bb 100644 --- a/src/components/DrawerLayout.tsx +++ b/src/components/DrawerLayout.tsx @@ -27,6 +27,7 @@ import { GestureEvent, HandlerStateChangeEvent, UserSelect, + ActiveCursor, } from '../handlers/gestureHandlerCommon'; import { PanGestureHandler, @@ -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 = { @@ -691,6 +699,7 @@ export default class DrawerLayout extends Component< ; 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 @@ -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. diff --git a/src/handlers/gestures/gesture.ts b/src/handlers/gestures/gesture.ts index 9f00793d84..93d8dd8560 100644 --- a/src/handlers/gestures/gesture.ts +++ b/src/handlers/gestures/gesture.ts @@ -6,6 +6,7 @@ import { GestureTouchEvent, GestureStateChangeEvent, GestureUpdateEvent, + ActiveCursor, } from '../gestureHandlerCommon'; import { getNextHandlerTag } from '../handlersRegistry'; import { GestureStateManagerType } from './gestureStateManager'; @@ -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; diff --git a/src/web/handlers/GestureHandler.ts b/src/web/handlers/GestureHandler.ts index f1ba865229..7fe00d100d 100644 --- a/src/web/handlers/GestureHandler.ts +++ b/src/web/handlers/GestureHandler.ts @@ -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(); @@ -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'; } } @@ -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; + } } } @@ -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(); diff --git a/src/web/interfaces.ts b/src/web/interfaces.ts index c591a1ffef..d8d682fbc1 100644 --- a/src/web/interfaces.ts +++ b/src/web/interfaces.ts @@ -1,4 +1,4 @@ -import { UserSelect } from '../handlers/gestureHandlerCommon'; +import { UserSelect, ActiveCursor } from '../handlers/gestureHandlerCommon'; import { Directions } from '../Directions'; import { State } from '../State'; @@ -22,6 +22,7 @@ type ConfigArgs = | boolean | HitSlop | UserSelect + | ActiveCursor | Directions | Handler[] | null @@ -34,6 +35,7 @@ export interface Config extends Record { hitSlop?: HitSlop; shouldCancelWhenOutside?: boolean; userSelect?: UserSelect; + activeCursor?: ActiveCursor; activateAfterLongPress?: number; failOffsetXStart?: number;