Skip to content

Instantly share code, notes, and snippets.

@jamiewinder
Created November 22, 2018 16:05
Show Gist options
  • Save jamiewinder/26e0ef41aa04b6d038c4c633f26d275c to your computer and use it in GitHub Desktop.
Save jamiewinder/26e0ef41aa04b6d038c4c633f26d275c to your computer and use it in GitHub Desktop.
Portal-friendly external event detection
import React from 'react';
import { DetectExternalEvents } from './detectexternalevents';
const EXTERNAL_REACT_EVENTS = ['onMouseDown', 'onTouchStart'];
const EXTERNAL_DOM_EVENTS = ['mousedown', 'touchstart'];
export interface IDetectExternalClickProps {
component?: string;
onExternalClick: (event: Event) => void;
}
export class DetectExternalClick extends React.Component<IDetectExternalClickProps> {
// Methods (React.Component)
public render() {
const { children, component, onExternalClick } = this.props;
return (
<DetectExternalEvents component={component}
reactEvents={EXTERNAL_REACT_EVENTS} domEvents={EXTERNAL_DOM_EVENTS}
onExternalEvent={onExternalClick}>
{children}
</DetectExternalEvents>
);
}
}
import React from 'react';
export interface IDetectExternalEventsProps {
component?: string;
reactEvents: Array<string>;
domEvents: Array<string>;
onExternalEvent: (event: Event) => void;
}
export class DetectExternalEvents extends React.Component<IDetectExternalEventsProps> {
// Fields
private _disposeTopLevelEvents: (() => void) | null = null;
private _lastInterceptedEvent: Event | null = null;
// Methods (React.Component)
public render() {
const {
children,
component = 'div',
reactEvents
} = this.props;
const events = reactEvents.reduce((obj, eventName) => {
obj[eventName] = this._handleWrapperEvent;
return obj;
}, {} as { [key: string]: React.EventHandler<React.SyntheticEvent<Element>>});
return React.createElement(component, events, children);
}
public componentDidMount() {
this._setupTopLevelEvents();
}
public componentDidUpdate() {
this._setupTopLevelEvents();
}
public componentWillUnmount() {
if (this._disposeTopLevelEvents) {
this._disposeTopLevelEvents();
this._disposeTopLevelEvents = null;
}
}
// Methods
private _setupTopLevelEvents() {
if (this._disposeTopLevelEvents) {
this._disposeTopLevelEvents();
this._disposeTopLevelEvents = null;
}
const { domEvents } = this.props;
domEvents.forEach((eventName) => {
window.addEventListener(eventName, this._handleTopLevelEvent);
});
this._disposeTopLevelEvents = () => {
domEvents.forEach((eventName) => {
window.removeEventListener(eventName, this._handleTopLevelEvent);
});
};
}
// Event Handlers
private _handleWrapperEvent = (event: React.SyntheticEvent<Element>) => {
this._lastInterceptedEvent = event.nativeEvent;
}
private _handleTopLevelEvent = (event: Event) => {
const { onExternalEvent } = this.props;
const wasEventIntercepted = this._lastInterceptedEvent === event;
if (!wasEventIntercepted) {
onExternalEvent(event);
}
this._lastInterceptedEvent = null;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment