diff --git a/docs/docs/components/link.md b/docs/docs/components/link.md index 3f03c326e..47644cea5 100644 --- a/docs/docs/components/link.md +++ b/docs/docs/components/link.md @@ -37,6 +37,10 @@ onPress: (e: SyntheticEvent, url: string) => void = undefined; // within the bounds of the view and the press has not been canceled onLongPress: (e: SyntheticEvent, url:string) => void = undefined; +// Event called when context menu is triggered, either by +// right mouse button click or context menu key +onContextMenu: (e: MouseEvent) => void = undefined; + // Can the link be included in a text selection? selectable: boolean = false; diff --git a/samples/RXPTest/src/Tests/LinkTest.tsx b/samples/RXPTest/src/Tests/LinkTest.tsx index 4ff492416..d39ea84dd 100644 --- a/samples/RXPTest/src/Tests/LinkTest.tsx +++ b/samples/RXPTest/src/Tests/LinkTest.tsx @@ -70,7 +70,8 @@ class LinkView extends RX.Component { { 'Press on this link to test press callback. Hold to test long presses. ' + - 'Move mouse pointer to test hovering.' } + 'Right click to test context menu callback. ' + + ' Move mouse pointer to test hovering.' } @@ -81,6 +82,7 @@ class LinkView extends RX.Component { onLongPress={ () => { this.setState({ test1Result: 'Long press detected' }); } } onHoverStart={ () => { this.setState({ test1Hovering: true }); } } onHoverEnd={ () => { this.setState({ test1Hovering: false }); } } + onContextMenu={ () => { this.setState({ test1Result: 'Context menu detected' }); } } allowFontScaling={ false } > { 'Press or hold' } diff --git a/src/common/Types.ts b/src/common/Types.ts index 1d463b823..e31297d3d 100644 --- a/src/common/Types.ts +++ b/src/common/Types.ts @@ -831,6 +831,7 @@ export interface LinkProps extends CommonStyledProps { onLongPress?: (e: RX.Types.SyntheticEvent, url: string) => void; onHoverStart?: (e: SyntheticEvent) => void; onHoverEnd?: (e: SyntheticEvent) => void; + onContextMenu?: (e: MouseEvent) => void; } // TextInput diff --git a/src/native-common/Link.tsx b/src/native-common/Link.tsx index 730b58037..6c947c06b 100644 --- a/src/native-common/Link.tsx +++ b/src/native-common/Link.tsx @@ -54,6 +54,9 @@ export class Link extends React.Component { protected _onPress = (e: RX.Types.SyntheticEvent) => { if (EventHelpers.isRightMouseButton(e)) { + if (this.props.onContextMenu) { + this.props.onContextMenu(EventHelpers.toMouseEvent(e)); + } return; } @@ -71,6 +74,14 @@ export class Link extends React.Component { } protected _onLongPress = (e: RX.Types.SyntheticEvent) => { + // Right mouse button doesn't change behavior based on press length. + if (EventHelpers.isRightMouseButton(e)) { + if (this.props.onContextMenu) { + this.props.onContextMenu(EventHelpers.toMouseEvent(e)); + } + return; + } + if (!EventHelpers.isRightMouseButton(e) && this.props.onLongPress) { this.props.onLongPress(EventHelpers.toMouseEvent(e), this.props.url); } diff --git a/src/web/Link.tsx b/src/web/Link.tsx index 9d054b823..f068bbcb7 100644 --- a/src/web/Link.tsx +++ b/src/web/Link.tsx @@ -11,6 +11,7 @@ import React = require('react'); import Styles from './Styles'; import Types = require('../common/Types'); +import EventHelpers from '../native-common/utils/EventHelpers'; import { applyFocusableComponentMixin } from './utils/FocusManager'; const _styles = { @@ -59,6 +60,7 @@ export class Link extends React.Component { onMouseDown={ this._onMouseDown } onMouseUp={ this._onMouseUp } tabIndex={ this.props.tabIndex } + onContextMenu={ this.props.onContextMenu ? this._onContextMenu : undefined } > { this.props.children } @@ -100,7 +102,11 @@ export class Link extends React.Component { this._longPressTimer = setTimeout(() => { this._longPressTimer = undefined; - if (this.props.onLongPress) { + + const mouseEvent = e as React.MouseEvent; + // Ignore right mouse button for long press. Context menu will + // be always displayed on mouseUp no matter the press length. + if (this.props.onLongPress && mouseEvent.button !== 2) { this.props.onLongPress(e, this.props.url); } }, _longPressTime); @@ -113,6 +119,14 @@ export class Link extends React.Component { this._longPressTimer = undefined; } } + + private _onContextMenu = (e: Types.SyntheticEvent) => { + if (this.props.onContextMenu) { + e.stopPropagation(); + e.preventDefault(); + this.props.onContextMenu(EventHelpers.toMouseEvent(e)); + } + } } applyFocusableComponentMixin(Link);