зеркало из https://github.com/microsoft/reactxp.git
Added code to disable mouse gestures in main view when a modal dialog is overlaying the main view. (#846)
This commit is contained in:
Родитель
b1c84e932e
Коммит
42183e21cd
|
@ -33,12 +33,39 @@ const _styles = {
|
|||
height: 50,
|
||||
width: 50,
|
||||
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 _shades = ['#000', '#333', '#666', '#999', '#CCC', '#FFF'];
|
||||
|
||||
const modal1Id = 'modal1';
|
||||
|
||||
interface GestureViewState {
|
||||
test1ColorIndex: number;
|
||||
test2ColorIndex: number;
|
||||
|
@ -196,10 +223,39 @@ class GestureViewView extends RX.Component<RX.CommonProps, GestureViewState> {
|
|||
style={ [_styles.smallBox, test4ColorStyle, this._test4AnimatedStyle] }
|
||||
/>
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
||||
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) {
|
||||
// Determine ratio of distance to initial distance.
|
||||
let zoomRatio = state.distance / state.initialDistance;
|
||||
|
|
|
@ -13,6 +13,7 @@ import * as ReactDOM from 'react-dom';
|
|||
import { PopupDescriptor, RootView } from './RootView';
|
||||
import { Types } from '../common/Interfaces';
|
||||
import Timers from '../common/utils/Timers';
|
||||
import MouseResponder from './utils/MouseResponder';
|
||||
|
||||
const MAX_CACHED_POPUPS = 4;
|
||||
|
||||
|
@ -75,6 +76,10 @@ export class FrontLayerViewManager {
|
|||
this._activePopupOptions!!!.getAnchor() === options.getAnchor();
|
||||
}
|
||||
|
||||
private _updateModalDisplayedState() {
|
||||
MouseResponder.setModalIsDisplayed(this.isModalDisplayed());
|
||||
}
|
||||
|
||||
showPopup(options: Types.PopupOptions, popupId: string, showDelay?: number): boolean {
|
||||
// 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
|
||||
|
@ -180,6 +185,8 @@ export class FrontLayerViewManager {
|
|||
const activePopup = (!this._activePopupOptions || this._activePopupShowDelay > 0) ? undefined :
|
||||
{ popupOptions: this._activePopupOptions, popupId: this._activePopupId!!! };
|
||||
|
||||
this._updateModalDisplayedState();
|
||||
|
||||
let rootView = (
|
||||
<RootView
|
||||
mainView={ this._mainView }
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
* It provides support for the scroll wheel, clicks and double clicks.
|
||||
*/
|
||||
|
||||
import * as PropTypes from 'prop-types';
|
||||
import * as React from 'react';
|
||||
|
||||
import { clone, isUndefined } from './utils/lodashMini';
|
||||
|
@ -43,6 +44,10 @@ enum GestureType {
|
|||
PanHorizontal
|
||||
}
|
||||
|
||||
export interface GestureViewContext {
|
||||
isInRxMainView?: boolean;
|
||||
}
|
||||
|
||||
let _idCounter = 1;
|
||||
|
||||
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 _skipNextTap = false;
|
||||
|
||||
static contextTypes: React.ValidationMap<any> = {
|
||||
isInRxMainView: PropTypes.bool
|
||||
};
|
||||
|
||||
componentWillUnmount() {
|
||||
// Dispose of timer before the component goes away.
|
||||
this._cancelDoubleTapTimer();
|
||||
|
@ -98,6 +107,7 @@ export class GestureView extends React.Component<Types.GestureViewProps, Types.S
|
|||
this._responder = MouseResponder.create({
|
||||
id: this._id,
|
||||
target: container,
|
||||
disableWhenModal: !!this.context.isInRxMainView,
|
||||
shouldBecomeFirstResponder: (event: MouseEvent) => {
|
||||
if (!this.props.onPan && !this.props.onPanHorizontal && !this.props.onPanVertical) {
|
||||
return false;
|
||||
|
|
|
@ -27,7 +27,7 @@ export class PopupDescriptor {
|
|||
constructor(public popupId: string, public popupOptions: Types.PopupOptions) {}
|
||||
}
|
||||
|
||||
export interface RootViewProps {
|
||||
export interface RootViewProps extends Types.CommonProps {
|
||||
mainView?: React.ReactNode;
|
||||
modal?: React.ReactElement<Types.ViewProps>;
|
||||
activePopup?: PopupDescriptor;
|
||||
|
@ -88,6 +88,32 @@ if (typeof document !== 'undefined') {
|
|||
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> {
|
||||
static childContextTypes: React.ValidationMap<any> = {
|
||||
focusManager: PropTypes.object
|
||||
|
@ -255,7 +281,7 @@ export class RootView extends React.Component<RootViewProps, RootViewState> {
|
|||
}
|
||||
|
||||
render() {
|
||||
let rootViewStyle = {
|
||||
const rootViewStyle = {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
|
@ -285,7 +311,9 @@ export class RootView extends React.Component<RootViewProps, RootViewState> {
|
|||
style={ rootViewStyle }
|
||||
dir={ this.props.writingDirection }
|
||||
>
|
||||
{ this.props.mainView }
|
||||
<MainViewContainer>
|
||||
{ this.props.mainView }
|
||||
</MainViewContainer>
|
||||
{ optionalModal }
|
||||
{ optionalPopups }
|
||||
<AccessibilityAnnouncer />
|
||||
|
|
|
@ -29,6 +29,7 @@ interface Responder {
|
|||
export interface MouseResponderConfig {
|
||||
id: number;
|
||||
target: HTMLElement;
|
||||
disableWhenModal: boolean;
|
||||
shouldBecomeFirstResponder?: (event: MouseEvent, gestureState: Types.PanGestureState) => boolean;
|
||||
onMove?: (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 _pendingGestureState: Types.PanGestureState | null = null;
|
||||
private static _initialized = false;
|
||||
private static _isModalDisplayed = false;
|
||||
|
||||
private static _responders: Responder[];
|
||||
|
||||
static setModalIsDisplayed(isDisplayed: boolean) {
|
||||
MouseResponder._isModalDisplayed = isDisplayed;
|
||||
}
|
||||
|
||||
static create(config: MouseResponderConfig): MouseResponderSubscription {
|
||||
MouseResponder._initializeEventHandlers();
|
||||
|
||||
|
@ -54,6 +60,10 @@ export default class MouseResponder {
|
|||
id: config.id,
|
||||
target: config.target,
|
||||
shouldBecomeFirstResponder(event: MouseEvent, gestureState: Types.PanGestureState) {
|
||||
if (MouseResponder._isModalDisplayed && config.disableWhenModal) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!config.shouldBecomeFirstResponder) {
|
||||
return false;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче