Added code to disable mouse gestures in main view when a modal dialog is overlaying the main view. (#846)

This commit is contained in:
Eric Traut 2018-10-08 08:30:34 -07:00 коммит произвёл GitHub
Родитель b1c84e932e
Коммит 42183e21cd
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 114 добавлений и 3 удалений

Просмотреть файл

@ -33,12 +33,39 @@ const _styles = {
height: 50, height: 50,
width: 50, width: 50,
backgroundColor: 'blue' backgroundColor: 'blue'
}),
button: RX.Styles.createButtonStyle({
borderColor: 'black',
borderWidth: 1,
borderRadius: 8,
paddingHorizontal: 8,
paddingVertical: 4
}),
modalDialog: RX.Styles.createTextStyle({
flex: 1,
alignSelf: 'stretch',
backgroundColor: 'rgba(0, 0, 0, 0.1)',
alignItems: 'center',
justifyContent: 'center'
}),
modalBox1: RX.Styles.createTextStyle({
padding: 20,
backgroundColor: '#eee',
borderColor: 'black',
borderWidth: 1,
alignItems: 'center',
justifyContent: 'center'
}),
modalText: RX.Styles.createViewStyle({
margin: 8
}) })
}; };
const _colors = ['red', 'green', 'blue']; const _colors = ['red', 'green', 'blue'];
const _shades = ['#000', '#333', '#666', '#999', '#CCC', '#FFF']; const _shades = ['#000', '#333', '#666', '#999', '#CCC', '#FFF'];
const modal1Id = 'modal1';
interface GestureViewState { interface GestureViewState {
test1ColorIndex: number; test1ColorIndex: number;
test2ColorIndex: number; test2ColorIndex: number;
@ -196,10 +223,39 @@ class GestureViewView extends RX.Component<RX.CommonProps, GestureViewState> {
style={ [_styles.smallBox, test4ColorStyle, this._test4AnimatedStyle] } style={ [_styles.smallBox, test4ColorStyle, this._test4AnimatedStyle] }
/> />
</RX.GestureView> </RX.GestureView>
<RX.View style={ _styles.explainTextContainer } key={ 'explanation5' }>
<RX.Button style={ _styles.button } onPress={ this._onShowModal }>
<RX.Text style={ _styles.explainText }>
{ 'Show Modal Dialog' }
</RX.Text>
</RX.Button>
</RX.View>
</RX.View> </RX.View>
); );
} }
private _onShowModal = (e: RX.Types.SyntheticEvent) => {
e.stopPropagation();
RX.Modal.show((
<RX.View style={ _styles.modalDialog }>
<RX.View style={ _styles.modalBox1 } onPress={ this._onDismissDialog }>
<RX.Text style={ _styles.modalText }>
{ 'Gesture targets should be disabled' }
</RX.Text>
<RX.Text style={ _styles.modalText }>
{ 'Click here to dismiss dialog' }
</RX.Text>
</RX.View>
</RX.View>
),
modal1Id);
}
private _onDismissDialog = (e: RX.Types.SyntheticEvent) => {
RX.Modal.dismiss(modal1Id);
}
private _onPinchZoomTest1(state: RX.Types.MultiTouchGestureState) { private _onPinchZoomTest1(state: RX.Types.MultiTouchGestureState) {
// Determine ratio of distance to initial distance. // Determine ratio of distance to initial distance.
let zoomRatio = state.distance / state.initialDistance; let zoomRatio = state.distance / state.initialDistance;

Просмотреть файл

@ -13,6 +13,7 @@ import * as ReactDOM from 'react-dom';
import { PopupDescriptor, RootView } from './RootView'; import { PopupDescriptor, RootView } from './RootView';
import { Types } from '../common/Interfaces'; import { Types } from '../common/Interfaces';
import Timers from '../common/utils/Timers'; import Timers from '../common/utils/Timers';
import MouseResponder from './utils/MouseResponder';
const MAX_CACHED_POPUPS = 4; const MAX_CACHED_POPUPS = 4;
@ -75,6 +76,10 @@ export class FrontLayerViewManager {
this._activePopupOptions!!!.getAnchor() === options.getAnchor(); this._activePopupOptions!!!.getAnchor() === options.getAnchor();
} }
private _updateModalDisplayedState() {
MouseResponder.setModalIsDisplayed(this.isModalDisplayed());
}
showPopup(options: Types.PopupOptions, popupId: string, showDelay?: number): boolean { showPopup(options: Types.PopupOptions, popupId: string, showDelay?: number): boolean {
// If options.dismissIfShown is true, calling this method will behave like a toggle. // If options.dismissIfShown is true, calling this method will behave like a toggle.
// On one call, it will open the popup. If it is called when pop up is seen, it will // On one call, it will open the popup. If it is called when pop up is seen, it will
@ -180,6 +185,8 @@ export class FrontLayerViewManager {
const activePopup = (!this._activePopupOptions || this._activePopupShowDelay > 0) ? undefined : const activePopup = (!this._activePopupOptions || this._activePopupShowDelay > 0) ? undefined :
{ popupOptions: this._activePopupOptions, popupId: this._activePopupId!!! }; { popupOptions: this._activePopupOptions, popupId: this._activePopupId!!! };
this._updateModalDisplayedState();
let rootView = ( let rootView = (
<RootView <RootView
mainView={ this._mainView } mainView={ this._mainView }

Просмотреть файл

@ -8,6 +8,7 @@
* It provides support for the scroll wheel, clicks and double clicks. * It provides support for the scroll wheel, clicks and double clicks.
*/ */
import * as PropTypes from 'prop-types';
import * as React from 'react'; import * as React from 'react';
import { clone, isUndefined } from './utils/lodashMini'; import { clone, isUndefined } from './utils/lodashMini';
@ -43,6 +44,10 @@ enum GestureType {
PanHorizontal PanHorizontal
} }
export interface GestureViewContext {
isInRxMainView?: boolean;
}
let _idCounter = 1; let _idCounter = 1;
export class GestureView extends React.Component<Types.GestureViewProps, Types.Stateless> { export class GestureView extends React.Component<Types.GestureViewProps, Types.Stateless> {
@ -65,6 +70,10 @@ export class GestureView extends React.Component<Types.GestureViewProps, Types.S
private _gestureTypeLocked = false; private _gestureTypeLocked = false;
private _skipNextTap = false; private _skipNextTap = false;
static contextTypes: React.ValidationMap<any> = {
isInRxMainView: PropTypes.bool
};
componentWillUnmount() { componentWillUnmount() {
// Dispose of timer before the component goes away. // Dispose of timer before the component goes away.
this._cancelDoubleTapTimer(); this._cancelDoubleTapTimer();
@ -98,6 +107,7 @@ export class GestureView extends React.Component<Types.GestureViewProps, Types.S
this._responder = MouseResponder.create({ this._responder = MouseResponder.create({
id: this._id, id: this._id,
target: container, target: container,
disableWhenModal: !!this.context.isInRxMainView,
shouldBecomeFirstResponder: (event: MouseEvent) => { shouldBecomeFirstResponder: (event: MouseEvent) => {
if (!this.props.onPan && !this.props.onPanHorizontal && !this.props.onPanVertical) { if (!this.props.onPan && !this.props.onPanHorizontal && !this.props.onPanVertical) {
return false; return false;

Просмотреть файл

@ -27,7 +27,7 @@ export class PopupDescriptor {
constructor(public popupId: string, public popupOptions: Types.PopupOptions) {} constructor(public popupId: string, public popupOptions: Types.PopupOptions) {}
} }
export interface RootViewProps { export interface RootViewProps extends Types.CommonProps {
mainView?: React.ReactNode; mainView?: React.ReactNode;
modal?: React.ReactElement<Types.ViewProps>; modal?: React.ReactElement<Types.ViewProps>;
activePopup?: PopupDescriptor; activePopup?: PopupDescriptor;
@ -88,6 +88,32 @@ if (typeof document !== 'undefined') {
document.head.appendChild(style); document.head.appendChild(style);
} }
export interface MainViewContext {
isInRxMainView?: boolean;
}
// This helper class wraps the main view and passes a boolean value
// "isInRxMainView" to all children found within it. This is used to
// prevent gesture handling within the main view when a modal is displayed.
export class MainViewContainer extends React.Component<Types.CommonProps, Types.Stateless>
implements React.ChildContextProvider<MainViewContext> {
static childContextTypes: React.ValidationMap<any> = {
isInRxMainView: PropTypes.bool
};
getChildContext(): MainViewContext {
return {
isInRxMainView: true
};
}
render() {
return (
this.props.children
);
}
}
export class RootView extends React.Component<RootViewProps, RootViewState> { export class RootView extends React.Component<RootViewProps, RootViewState> {
static childContextTypes: React.ValidationMap<any> = { static childContextTypes: React.ValidationMap<any> = {
focusManager: PropTypes.object focusManager: PropTypes.object
@ -255,7 +281,7 @@ export class RootView extends React.Component<RootViewProps, RootViewState> {
} }
render() { render() {
let rootViewStyle = { const rootViewStyle = {
width: '100%', width: '100%',
height: '100%', height: '100%',
display: 'flex', display: 'flex',
@ -285,7 +311,9 @@ export class RootView extends React.Component<RootViewProps, RootViewState> {
style={ rootViewStyle } style={ rootViewStyle }
dir={ this.props.writingDirection } dir={ this.props.writingDirection }
> >
{ this.props.mainView } <MainViewContainer>
{ this.props.mainView }
</MainViewContainer>
{ optionalModal } { optionalModal }
{ optionalPopups } { optionalPopups }
<AccessibilityAnnouncer /> <AccessibilityAnnouncer />

Просмотреть файл

@ -29,6 +29,7 @@ interface Responder {
export interface MouseResponderConfig { export interface MouseResponderConfig {
id: number; id: number;
target: HTMLElement; target: HTMLElement;
disableWhenModal: boolean;
shouldBecomeFirstResponder?: (event: MouseEvent, gestureState: Types.PanGestureState) => boolean; shouldBecomeFirstResponder?: (event: MouseEvent, gestureState: Types.PanGestureState) => boolean;
onMove?: (event: MouseEvent, gestureState: Types.PanGestureState) => void; onMove?: (event: MouseEvent, gestureState: Types.PanGestureState) => void;
onTerminate?: (event: MouseEvent, gestureState: Types.PanGestureState) => void; onTerminate?: (event: MouseEvent, gestureState: Types.PanGestureState) => void;
@ -42,9 +43,14 @@ export default class MouseResponder {
private static _currentResponder: Responder | null = null; private static _currentResponder: Responder | null = null;
private static _pendingGestureState: Types.PanGestureState | null = null; private static _pendingGestureState: Types.PanGestureState | null = null;
private static _initialized = false; private static _initialized = false;
private static _isModalDisplayed = false;
private static _responders: Responder[]; private static _responders: Responder[];
static setModalIsDisplayed(isDisplayed: boolean) {
MouseResponder._isModalDisplayed = isDisplayed;
}
static create(config: MouseResponderConfig): MouseResponderSubscription { static create(config: MouseResponderConfig): MouseResponderSubscription {
MouseResponder._initializeEventHandlers(); MouseResponder._initializeEventHandlers();
@ -54,6 +60,10 @@ export default class MouseResponder {
id: config.id, id: config.id,
target: config.target, target: config.target,
shouldBecomeFirstResponder(event: MouseEvent, gestureState: Types.PanGestureState) { shouldBecomeFirstResponder(event: MouseEvent, gestureState: Types.PanGestureState) {
if (MouseResponder._isModalDisplayed && config.disableWhenModal) {
return false;
}
if (!config.shouldBecomeFirstResponder) { if (!config.shouldBecomeFirstResponder) {
return false; return false;
} }