UNPKG

react-composite-events

Version:

A collection of higher-order components (HOCs) to easily create composite events in React components

176 lines (142 loc) 7.64 kB
import React, { Component } from 'react'; import { getDisplayName } from './utils'; var _omit = function _omit(props, propToOmit) { var propsCopy = babelHelpers.extends({}, props); delete propsCopy[propToOmit]; return propsCopy; }; var _eventNamesToHandlerLookup = function _eventNamesToHandlerLookup(eventNames, handler) { return (eventNames || []).reduce(function (lookup, eventName) { return babelHelpers.extends({}, lookup, babelHelpers.defineProperty({}, eventName, handler.bind(null, eventName))); }, {}); }; var _isValidDuration = function _isValidDuration(duration) { return duration > 0; }; export default (function (_ref) { var eventPropName = _ref.eventPropName, triggerEvent = _ref.triggerEvent, _ref$defaultDuration = _ref.defaultDuration, defaultDuration = _ref$defaultDuration === undefined ? 0 : _ref$defaultDuration, cancelEvent = _ref.cancelEvent, _ref$shouldResetTimer = _ref.shouldResetTimerOnRetrigger, shouldResetTimerOnRetrigger = _ref$shouldResetTimer === undefined ? true : _ref$shouldResetTimer, _ref$beforeHandle = _ref.beforeHandle, beforeHandle = _ref$beforeHandle === undefined ? function () { return true; } : _ref$beforeHandle; // istanbul ignore next if (process.env.NODE_ENV !== 'production') { if (!eventPropName) { throw new Error('`eventPropName` configuration must be specified'); } if (!triggerEvent) { throw new Error('`triggerEvent` configuration must be specified'); } } var triggerEvents = Array.isArray(triggerEvent) ? triggerEvent : [triggerEvent]; var cancelEvents = void 0; if (cancelEvent) { cancelEvents = Array.isArray(cancelEvent) ? cancelEvent : [cancelEvent]; } return function () { var duration = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; var timeoutDuration = defaultDuration; var durationSuffix = ''; // if defaultDuration is specified and there's a duration override, // use the override. Otherwise there's no timeout happening if (_isValidDuration(defaultDuration) && _isValidDuration(duration)) { timeoutDuration = duration; durationSuffix = '-' + duration; } // if the duration is passed the composite even prop name needs to be parameterized var compositeEventPropName = '' + eventPropName + durationSuffix; return function (Element) { var _class, _temp2; if (!Element && process.env.NODE_ENV !== 'production') { throw new Error('Component/element to enhance must be specified'); } var elementDisplayName = getDisplayName(Element); return _temp2 = _class = function (_Component) { babelHelpers.inherits(CompositeEventWrapper, _Component); function CompositeEventWrapper() { var _ref2; var _temp, _this, _ret; babelHelpers.classCallCheck(this, CompositeEventWrapper); for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return _ret = (_temp = (_this = babelHelpers.possibleConstructorReturn(this, (_ref2 = CompositeEventWrapper.__proto__ || Object.getPrototypeOf(CompositeEventWrapper)).call.apply(_ref2, [this].concat(args))), _this), _this._delayTimeout = null, _this._callSpecificHandler = function (eventName, e) { var onEvent = _this.props[eventName]; if (onEvent) { onEvent(e); } }, _this._clearTimeout = function () { _this._delayTimeout = clearTimeout(_this._delayTimeout); }, _this._callCompositeEvent = function (e) { var onCompositeEvent = _this.props[compositeEventPropName]; // If a before handle function is defined, call it and check to see what the function returns // truthy - means that it wants the HOC to to call the final handler with the event object // falsy - means that it doesn't want the HOC to do anything // The function receives the composite event handler + event object to make it's decision and // can call the handler directly if (beforeHandle(onCompositeEvent, e)) { onCompositeEvent(e); } }, _this._handleTriggerEvent = function (eventName, e) { var onCompositeEvent = _this.props[compositeEventPropName]; // If a specific handler was passed, call that one first _this._callSpecificHandler(eventName, e); if (!onCompositeEvent) { // istanbul ignore next if (process.env.NODE_ENV !== 'production') { // eslint-disable-next-line no-console console.warn('No handler was found for `' + compositeEventPropName + '` in `<' + elementDisplayName + ' />`! Was this a typo? If not, you should consider avoiding using the composite event HOC to improve performance'); } return; } // Call the composite event handler if (_isValidDuration(timeoutDuration)) { // If shouldResetTimerOnRetrigger flag is not explicitly turned off, we need to // clear any existing timeout and start a fresh timer because a retrigger happened. if (shouldResetTimerOnRetrigger !== false) { _this._clearTimeout(); } // And we can start a timeout as long as we don't have an active one going if (!_this._delayTimeout) { _this._delayTimeout = setTimeout(function () { return _this._callCompositeEvent(e); }, timeoutDuration); } } else { _this._callCompositeEvent(e); } }, _this._handleCancelEvent = function (eventName, e) { // If a specific handler was passed, call that one first _this._callSpecificHandler(eventName, e); // just cancel the timeout so composite handler won't be called _this._clearTimeout(); }, _temp), babelHelpers.possibleConstructorReturn(_this, _ret); } babelHelpers.createClass(CompositeEventWrapper, [{ key: 'render', value: function render() { // Want to pass all the props through to the underlying Component except the passed // compositeEventPropName, which we need to handle specially. // This will also include separate specific handlers matching trigger & cancel events var passThruProps = _omit(this.props, compositeEventPropName); // Create an object mapping of the trigger/cancel events to handlers. // The handler needs to bind the event name so that we can check to see if // a specific handler was specified so we can fire that too var triggerEventHandlers = _eventNamesToHandlerLookup(triggerEvents, this._handleTriggerEvent); var cancelEventHandlers = _eventNamesToHandlerLookup(cancelEvents, this._handleCancelEvent); // As a result of cancelEventHandlers going after triggerEventHandlers below, if // a cancel event matches a trigger event, the composite event will never be triggered return React.createElement(Element, babelHelpers.extends({}, passThruProps, triggerEventHandlers, cancelEventHandlers)); } }]); return CompositeEventWrapper; }(Component), _class.displayName = elementDisplayName + '-' + eventPropName + durationSuffix, _temp2; }; }; });