@semantic-ui-react/event-stack
Version:
Issues mentioned in `README` should be solved by other approaches: - `.addEventListener()` is blazing fast and is not a real performance issue - to solve issues with ordering in case when regular DOM event propogation is not available consider to use thi
567 lines (475 loc) • 15.3 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var env = require('exenv');
var PropTypes = require('prop-types');
var React = require('react');
function _typeof(obj) {
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function (obj) {
return typeof obj;
};
} else {
_typeof = function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
}
return _typeof(obj);
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
writable: true,
configurable: true
}
});
if (superClass) _setPrototypeOf(subClass, superClass);
}
function _getPrototypeOf(o) {
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o);
};
return _getPrototypeOf(o);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return self;
}
function _possibleConstructorReturn(self, call) {
if (call && (typeof call === "object" || typeof call === "function")) {
return call;
}
return _assertThisInitialized(self);
}
/**
* The current implementation was chosen by performance and compatibility reasons, feel free to play
* with benchmarks and submit PR with faster alternative. Each method contains links to benchmarks.
*/
var EventSet =
/*#__PURE__*/
function () {
/**
* @see https://jsperf.com/suir-eventset-constructor
*/
function EventSet(eventHandlers) {
_classCallCheck(this, EventSet);
_defineProperty(this, "handlers", void 0);
this.handlers = eventHandlers.slice(0);
}
/**
* @see https://jsperf.com/suir-eventset-addhandlers
*/
_createClass(EventSet, [{
key: "addHandlers",
value: function addHandlers(additionalHandlers) {
var newHandlers = this.handlers.slice(0);
var length = additionalHandlers.length; // Heads up!
// Previously we use Set there, it granted uniqueness of handlers, now dispatchEvent() is
// responsible for this.
for (var i = 0; i < length; i += 1) {
newHandlers.push(additionalHandlers[i]);
}
return new EventSet(newHandlers);
}
/**
* @see https://jsperf.com/suir-eventset-dispatchsingle
* @see https://jsperf.com/suir-eventset-dispatchmultiple2
*/
}, {
key: "dispatchEvent",
value: function dispatchEvent(event, dispatchAll) {
var count = this.handlers.length - 1;
if (!dispatchAll) {
// Heads up!
// We don't use .pop() there because it will mutate the array.
var recentHandler = this.handlers[count];
recentHandler(event);
return;
}
for (var i = count; i >= 0; i -= 1) {
if (!this.handlers[i].called) {
this.handlers[i].called = true;
this.handlers[i](event);
}
}
for (var _i = count; _i >= 0; _i -= 1) {
this.handlers[_i].called = false;
}
}
}, {
key: "hasHandlers",
value: function hasHandlers() {
return this.handlers.length > 0;
}
/**
* @see https://jsperf.com/suir-eventset-removehandlers
*/
}, {
key: "removeHandlers",
value: function removeHandlers(removalHandlers) {
var newHandlers = [];
var length = this.handlers.length;
for (var i = 0; i < length; i += 1) {
var handler = this.handlers[i];
if (removalHandlers.indexOf(handler) === -1) {
newHandlers.push(handler);
}
}
return new EventSet(newHandlers);
}
}]);
return EventSet;
}();
/**
* An IE11-compatible function.
*
* @see https://jsperf.com/suir-clone-map
*/
function cloneMap(map) {
var newMap = new Map();
map.forEach(function (value, key) {
newMap.set(key, value);
});
return newMap;
}
function normalizeHandlers(handlers) {
return Array.isArray(handlers) ? handlers : [handlers];
}
/**
* Asserts that the passed value is React.RefObject
*
* @see https://github.com/facebook/react/blob/v16.8.2/packages/react-reconciler/src/ReactFiberCommitWork.js#L665
*/
var isRefObject = function isRefObject(ref // eslint-disable-next-line
) {
return ref !== null && _typeof(ref) === 'object' && ref.hasOwnProperty('current');
};
/**
* Normalizes `target` for EventStack, because `target` can be passed as `boolean` or `string`.
*
* @see https://jsperf.com/suir-normalize-target
*/
function normalizeTarget(target) {
if (target === 'document') return document;
if (target === 'window') return window;
if (isRefObject(target)) return target.current || document;
return target || document;
}
var EventPool =
/*#__PURE__*/
function () {
function EventPool(poolName, handlerSets) {
_classCallCheck(this, EventPool);
_defineProperty(this, "handlerSets", void 0);
_defineProperty(this, "poolName", void 0);
this.handlerSets = handlerSets;
this.poolName = poolName;
}
_createClass(EventPool, [{
key: "addHandlers",
value: function addHandlers(eventType, eventHandlers) {
var handlerSets = cloneMap(this.handlerSets);
if (handlerSets.has(eventType)) {
var eventSet = handlerSets.get(eventType);
handlerSets.set(eventType, eventSet.addHandlers(eventHandlers));
} else {
handlerSets.set(eventType, new EventSet(eventHandlers));
}
return new EventPool(this.poolName, handlerSets);
}
}, {
key: "dispatchEvent",
value: function dispatchEvent(eventType, event) {
var handlerSet = this.handlerSets.get(eventType);
var shouldDispatchAll = this.poolName === 'default';
if (handlerSet) {
handlerSet.dispatchEvent(event, shouldDispatchAll);
}
}
}, {
key: "hasHandlers",
value: function hasHandlers(eventType) {
if (!eventType) {
return this.handlerSets.size > 0;
}
var eventSet = this.handlerSets.get(eventType);
if (eventSet) {
return eventSet.hasHandlers();
}
return false;
}
}, {
key: "removeHandlers",
value: function removeHandlers(eventType, eventHandlers) {
var handlerSets = cloneMap(this.handlerSets);
if (!handlerSets.has(eventType)) {
return new EventPool(this.poolName, handlerSets);
}
var currentSet = handlerSets.get(eventType);
var nextSet = currentSet.removeHandlers(eventHandlers);
if (nextSet.hasHandlers()) {
handlerSets.set(eventType, nextSet);
} else {
handlerSets.delete(eventType);
}
return new EventPool(this.poolName, handlerSets);
}
}]);
return EventPool;
}();
_defineProperty(EventPool, "createByType", function (poolName, eventType, eventHandlers) {
var handlerSets = new Map();
handlerSets.set(eventType, new EventSet(eventHandlers));
return new EventPool(poolName, handlerSets);
});
var EventTarget =
/*#__PURE__*/
function () {
function EventTarget(target) {
var _this = this;
_classCallCheck(this, EventTarget);
_defineProperty(this, "handlers", new Map());
_defineProperty(this, "pools", new Map());
_defineProperty(this, "target", void 0);
_defineProperty(this, "createEmitter", function (eventType) {
return function (event) {
_this.pools.forEach(function (pool) {
pool.dispatchEvent(eventType, event);
});
};
});
this.target = target;
}
_createClass(EventTarget, [{
key: "addHandlers",
value: function addHandlers(poolName, eventType, eventHandlers) {
if (this.pools.has(poolName)) {
var eventPool = this.pools.get(poolName);
this.pools.set(poolName, eventPool.addHandlers(eventType, eventHandlers));
} else {
this.pools.set(poolName, EventPool.createByType(poolName, eventType, eventHandlers));
}
if (!this.handlers.has(eventType)) {
this.addTargetHandler(eventType);
}
}
}, {
key: "hasHandlers",
value: function hasHandlers() {
return this.handlers.size > 0;
}
}, {
key: "removeHandlers",
value: function removeHandlers(poolName, eventType, eventHandlers) {
if (!this.pools.has(poolName)) {
return;
}
var pool = this.pools.get(poolName);
var newPool = pool.removeHandlers(eventType, eventHandlers);
if (newPool.hasHandlers()) {
this.pools.set(poolName, newPool);
} else {
this.pools.delete(poolName);
}
var hasHandlers = false;
this.pools.forEach(function (pool) {
return hasHandlers = hasHandlers || pool.hasHandlers(eventType);
});
if (!hasHandlers) {
this.removeTargetHandler(eventType);
}
}
}, {
key: "addTargetHandler",
value: function addTargetHandler(eventType) {
var handler = this.createEmitter(eventType);
this.handlers.set(eventType, handler);
this.target.addEventListener(eventType, handler, true);
}
}, {
key: "removeTargetHandler",
value: function removeTargetHandler(eventType) {
if (this.handlers.has(eventType)) {
this.target.removeEventListener(eventType, this.handlers.get(eventType), true);
this.handlers.delete(eventType);
}
}
}]);
return EventTarget;
}();
var EventStack =
/*#__PURE__*/
function () {
function EventStack() {
var _this = this;
_classCallCheck(this, EventStack);
_defineProperty(this, "targets", new Map());
_defineProperty(this, "getTarget", function (target) {
var autoCreate = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
var normalized = normalizeTarget(target);
if (_this.targets.has(normalized)) {
return _this.targets.get(normalized);
}
if (!autoCreate) return null;
var eventTarget = new EventTarget(normalized);
_this.targets.set(normalized, eventTarget);
return eventTarget;
});
_defineProperty(this, "removeTarget", function (target) {
_this.targets.delete(normalizeTarget(target));
});
}
_createClass(EventStack, [{
key: "sub",
value: function sub(eventName, eventHandlers) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
if (!env.canUseDOM) return;
var _options$target = options.target,
target = _options$target === void 0 ? document : _options$target,
_options$pool = options.pool,
pool = _options$pool === void 0 ? 'default' : _options$pool;
var eventTarget = this.getTarget(target);
eventTarget.addHandlers(pool, eventName, normalizeHandlers(eventHandlers));
}
}, {
key: "unsub",
value: function unsub(eventName, eventHandlers) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
if (!env.canUseDOM) return;
var _options$target2 = options.target,
target = _options$target2 === void 0 ? document : _options$target2,
_options$pool2 = options.pool,
pool = _options$pool2 === void 0 ? 'default' : _options$pool2;
var eventTarget = this.getTarget(target, false);
if (eventTarget) {
eventTarget.removeHandlers(pool, eventName, normalizeHandlers(eventHandlers));
if (!eventTarget.hasHandlers()) this.removeTarget(target);
}
}
}]);
return EventStack;
}();
var instance = new EventStack();
/**
* This component exposes the EventStack API as public and provides a declarative way to manage it.
*/
var EventStack$1 =
/*#__PURE__*/
function (_React$PureComponent) {
_inherits(EventStack, _React$PureComponent);
function EventStack() {
_classCallCheck(this, EventStack);
return _possibleConstructorReturn(this, _getPrototypeOf(EventStack).apply(this, arguments));
}
_createClass(EventStack, [{
key: "componentDidMount",
value: function componentDidMount() {
this.subscribe(this.props);
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps) {
this.unsubscribe(prevProps);
this.subscribe(this.props);
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
this.unsubscribe(this.props);
}
}, {
key: "subscribe",
value: function subscribe(props) {
var name = props.name,
on = props.on,
pool = props.pool,
target = props.target;
instance.sub(name, on, {
pool: pool,
target: target
});
}
}, {
key: "unsubscribe",
value: function unsubscribe(props) {
var name = props.name,
on = props.on,
pool = props.pool,
target = props.target;
instance.unsub(name, on, {
pool: pool,
target: target
});
}
}, {
key: "render",
value: function render() {
return null;
}
}]);
return EventStack;
}(React.PureComponent);
_defineProperty(EventStack$1, "defaultProps", {
pool: 'default',
target: 'document'
});
EventStack$1.propTypes = {
/** An event name on which we will subscribe. */
name: PropTypes.string.isRequired,
/** An event handler or array of event handlers. */
on: PropTypes.oneOfType([PropTypes.func, PropTypes.arrayOf(PropTypes.func)]).isRequired,
/** A name of pool. */
pool: PropTypes.string,
/** A DOM element on which we will subscribe. */
target: PropTypes.oneOfType([PropTypes.oneOf(['document', 'window']), // Heads up!
// This condition for SSR safety.
PropTypes.instanceOf(env.canUseDOM ? HTMLElement : Object), PropTypes.shape({
current: PropTypes.object
})])
};
exports.instance = instance;
exports.default = EventStack$1;