Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 

368 wiersze
11 KiB

  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', { value: true });
  3. var react = require('react');
  4. var reactDom = require('react-dom');
  5. function _inheritsLoose(subClass, superClass) {
  6. subClass.prototype = Object.create(superClass.prototype);
  7. subClass.prototype.constructor = subClass;
  8. subClass.__proto__ = superClass;
  9. }
  10. function _objectWithoutProperties(source, excluded) {
  11. if (source == null) return {};
  12. var target = {};
  13. var sourceKeys = Object.keys(source);
  14. var key, i;
  15. for (i = 0; i < sourceKeys.length; i++) {
  16. key = sourceKeys[i];
  17. if (excluded.indexOf(key) >= 0) continue;
  18. target[key] = source[key];
  19. }
  20. if (Object.getOwnPropertySymbols) {
  21. var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
  22. for (i = 0; i < sourceSymbolKeys.length; i++) {
  23. key = sourceSymbolKeys[i];
  24. if (excluded.indexOf(key) >= 0) continue;
  25. if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
  26. target[key] = source[key];
  27. }
  28. }
  29. return target;
  30. }
  31. /**
  32. * Check whether some DOM node is our Component's node.
  33. */
  34. function isNodeFound(current, componentNode, ignoreClass) {
  35. if (current === componentNode) {
  36. return true;
  37. } // SVG <use/> elements do not technically reside in the rendered DOM, so
  38. // they do not have classList directly, but they offer a link to their
  39. // corresponding element, which can have classList. This extra check is for
  40. // that case.
  41. // See: http://www.w3.org/TR/SVG11/struct.html#InterfaceSVGUseElement
  42. // Discussion: https://github.com/Pomax/react-onclickoutside/pull/17
  43. if (current.correspondingElement) {
  44. return current.correspondingElement.classList.contains(ignoreClass);
  45. }
  46. return current.classList.contains(ignoreClass);
  47. }
  48. /**
  49. * Try to find our node in a hierarchy of nodes, returning the document
  50. * node as highest node if our node is not found in the path up.
  51. */
  52. function findHighest(current, componentNode, ignoreClass) {
  53. if (current === componentNode) {
  54. return true;
  55. } // If source=local then this event came from 'somewhere'
  56. // inside and should be ignored. We could handle this with
  57. // a layered approach, too, but that requires going back to
  58. // thinking in terms of Dom node nesting, running counter
  59. // to React's 'you shouldn't care about the DOM' philosophy.
  60. while (current.parentNode) {
  61. if (isNodeFound(current, componentNode, ignoreClass)) {
  62. return true;
  63. }
  64. current = current.parentNode;
  65. }
  66. return current;
  67. }
  68. /**
  69. * Check if the browser scrollbar was clicked
  70. */
  71. function clickedScrollbar(evt) {
  72. return document.documentElement.clientWidth <= evt.clientX || document.documentElement.clientHeight <= evt.clientY;
  73. }
  74. // ideally will get replaced with external dep
  75. // when rafrex/detect-passive-events#4 and rafrex/detect-passive-events#5 get merged in
  76. var testPassiveEventSupport = function testPassiveEventSupport() {
  77. if (typeof window === 'undefined' || typeof window.addEventListener !== 'function') {
  78. return;
  79. }
  80. var passive = false;
  81. var options = Object.defineProperty({}, 'passive', {
  82. get: function get() {
  83. passive = true;
  84. }
  85. });
  86. var noop = function noop() {};
  87. window.addEventListener('testPassiveEventSupport', noop, options);
  88. window.removeEventListener('testPassiveEventSupport', noop, options);
  89. return passive;
  90. };
  91. function autoInc(seed) {
  92. if (seed === void 0) {
  93. seed = 0;
  94. }
  95. return function () {
  96. return ++seed;
  97. };
  98. }
  99. var uid = autoInc();
  100. var passiveEventSupport;
  101. var handlersMap = {};
  102. var enabledInstances = {};
  103. var touchEvents = ['touchstart', 'touchmove'];
  104. var IGNORE_CLASS_NAME = 'ignore-react-onclickoutside';
  105. /**
  106. * Options for addEventHandler and removeEventHandler
  107. */
  108. function getEventHandlerOptions(instance, eventName) {
  109. var handlerOptions = null;
  110. var isTouchEvent = touchEvents.indexOf(eventName) !== -1;
  111. if (isTouchEvent && passiveEventSupport) {
  112. handlerOptions = {
  113. passive: !instance.props.preventDefault
  114. };
  115. }
  116. return handlerOptions;
  117. }
  118. /**
  119. * This function generates the HOC function that you'll use
  120. * in order to impart onOutsideClick listening to an
  121. * arbitrary component. It gets called at the end of the
  122. * bootstrapping code to yield an instance of the
  123. * onClickOutsideHOC function defined inside setupHOC().
  124. */
  125. function onClickOutsideHOC(WrappedComponent, config) {
  126. var _class, _temp;
  127. var componentName = WrappedComponent.displayName || WrappedComponent.name || 'Component';
  128. return _temp = _class =
  129. /*#__PURE__*/
  130. function (_Component) {
  131. _inheritsLoose(onClickOutside, _Component);
  132. function onClickOutside(props) {
  133. var _this;
  134. _this = _Component.call(this, props) || this;
  135. _this.__outsideClickHandler = function (event) {
  136. if (typeof _this.__clickOutsideHandlerProp === 'function') {
  137. _this.__clickOutsideHandlerProp(event);
  138. return;
  139. }
  140. var instance = _this.getInstance();
  141. if (typeof instance.props.handleClickOutside === 'function') {
  142. instance.props.handleClickOutside(event);
  143. return;
  144. }
  145. if (typeof instance.handleClickOutside === 'function') {
  146. instance.handleClickOutside(event);
  147. return;
  148. }
  149. throw new Error("WrappedComponent: " + componentName + " lacks a handleClickOutside(event) function for processing outside click events.");
  150. };
  151. _this.__getComponentNode = function () {
  152. var instance = _this.getInstance();
  153. if (config && typeof config.setClickOutsideRef === 'function') {
  154. return config.setClickOutsideRef()(instance);
  155. }
  156. if (typeof instance.setClickOutsideRef === 'function') {
  157. return instance.setClickOutsideRef();
  158. }
  159. return reactDom.findDOMNode(instance);
  160. };
  161. _this.enableOnClickOutside = function () {
  162. if (typeof document === 'undefined' || enabledInstances[_this._uid]) {
  163. return;
  164. }
  165. if (typeof passiveEventSupport === 'undefined') {
  166. passiveEventSupport = testPassiveEventSupport();
  167. }
  168. enabledInstances[_this._uid] = true;
  169. var events = _this.props.eventTypes;
  170. if (!events.forEach) {
  171. events = [events];
  172. }
  173. handlersMap[_this._uid] = function (event) {
  174. if (_this.componentNode === null) return;
  175. if (_this.props.preventDefault) {
  176. event.preventDefault();
  177. }
  178. if (_this.props.stopPropagation) {
  179. event.stopPropagation();
  180. }
  181. if (_this.props.excludeScrollbar && clickedScrollbar(event)) return;
  182. var current = event.target;
  183. if (findHighest(current, _this.componentNode, _this.props.outsideClickIgnoreClass) !== document) {
  184. return;
  185. }
  186. _this.__outsideClickHandler(event);
  187. };
  188. events.forEach(function (eventName) {
  189. document.addEventListener(eventName, handlersMap[_this._uid], getEventHandlerOptions(_this, eventName));
  190. });
  191. };
  192. _this.disableOnClickOutside = function () {
  193. delete enabledInstances[_this._uid];
  194. var fn = handlersMap[_this._uid];
  195. if (fn && typeof document !== 'undefined') {
  196. var events = _this.props.eventTypes;
  197. if (!events.forEach) {
  198. events = [events];
  199. }
  200. events.forEach(function (eventName) {
  201. return document.removeEventListener(eventName, fn, getEventHandlerOptions(_this, eventName));
  202. });
  203. delete handlersMap[_this._uid];
  204. }
  205. };
  206. _this.getRef = function (ref) {
  207. return _this.instanceRef = ref;
  208. };
  209. _this._uid = uid();
  210. return _this;
  211. }
  212. /**
  213. * Access the WrappedComponent's instance.
  214. */
  215. var _proto = onClickOutside.prototype;
  216. _proto.getInstance = function getInstance() {
  217. if (!WrappedComponent.prototype.isReactComponent) {
  218. return this;
  219. }
  220. var ref = this.instanceRef;
  221. return ref.getInstance ? ref.getInstance() : ref;
  222. };
  223. /**
  224. * Add click listeners to the current document,
  225. * linked to this component's state.
  226. */
  227. _proto.componentDidMount = function componentDidMount() {
  228. // If we are in an environment without a DOM such
  229. // as shallow rendering or snapshots then we exit
  230. // early to prevent any unhandled errors being thrown.
  231. if (typeof document === 'undefined' || !document.createElement) {
  232. return;
  233. }
  234. var instance = this.getInstance();
  235. if (config && typeof config.handleClickOutside === 'function') {
  236. this.__clickOutsideHandlerProp = config.handleClickOutside(instance);
  237. if (typeof this.__clickOutsideHandlerProp !== 'function') {
  238. throw new Error("WrappedComponent: " + componentName + " lacks a function for processing outside click events specified by the handleClickOutside config option.");
  239. }
  240. }
  241. this.componentNode = this.__getComponentNode(); // return early so we dont initiate onClickOutside
  242. if (this.props.disableOnClickOutside) return;
  243. this.enableOnClickOutside();
  244. };
  245. _proto.componentDidUpdate = function componentDidUpdate() {
  246. this.componentNode = this.__getComponentNode();
  247. };
  248. /**
  249. * Remove all document's event listeners for this component
  250. */
  251. _proto.componentWillUnmount = function componentWillUnmount() {
  252. this.disableOnClickOutside();
  253. };
  254. /**
  255. * Can be called to explicitly enable event listening
  256. * for clicks and touches outside of this element.
  257. */
  258. /**
  259. * Pass-through render
  260. */
  261. _proto.render = function render() {
  262. // eslint-disable-next-line no-unused-vars
  263. var _props = this.props,
  264. excludeScrollbar = _props.excludeScrollbar,
  265. props = _objectWithoutProperties(_props, ["excludeScrollbar"]);
  266. if (WrappedComponent.prototype.isReactComponent) {
  267. props.ref = this.getRef;
  268. } else {
  269. props.wrappedRef = this.getRef;
  270. }
  271. props.disableOnClickOutside = this.disableOnClickOutside;
  272. props.enableOnClickOutside = this.enableOnClickOutside;
  273. return react.createElement(WrappedComponent, props);
  274. };
  275. return onClickOutside;
  276. }(react.Component), _class.displayName = "OnClickOutside(" + componentName + ")", _class.defaultProps = {
  277. eventTypes: ['mousedown', 'touchstart'],
  278. excludeScrollbar: config && config.excludeScrollbar || false,
  279. outsideClickIgnoreClass: IGNORE_CLASS_NAME,
  280. preventDefault: false,
  281. stopPropagation: false
  282. }, _class.getClass = function () {
  283. return WrappedComponent.getClass ? WrappedComponent.getClass() : WrappedComponent;
  284. }, _temp;
  285. }
  286. exports.IGNORE_CLASS_NAME = IGNORE_CLASS_NAME;
  287. exports['default'] = onClickOutsideHOC;