import { Build as BUILD, getElement } from '@stencil/core';
const ClickOutsideOptionsDefaults = {
  triggerEvents: 'click',
  exclude: '',
};
/**
 * Call this function as soon as the click outside of annotated method's host is done.
 * @example
```
@ClickOutside()
callback() {
  // this will run when click outside of element (host component) is done.
}
```
 */
export default function ClickOutside(opt = ClickOutsideOptionsDefaults) {
  return (proto, methodName) => {
    // this is to resolve the 'compiler optimization issue':
    // lifecycle events not being called when not explicitly declared in at least one of components from bundle
    ;
    BUILD.cmpDidLoad = true;
    BUILD.cmpDidUnload = true;
    const { componentDidLoad, componentDidUnload } = proto;
    proto.componentDidLoad = function () {
      const host = getElement(this);
      const method = this[methodName];
      registerClickOutside(this, host, method, opt);
      return componentDidLoad && componentDidLoad.call(this);
    };
    proto.componentDidUnload = function () {
      const host = getElement(this);
      const method = this[methodName];
      removeClickOutside(this, host, method, opt);
      return componentDidUnload && componentDidUnload.call(this);
    };
  };
}
/**
 * Register callback function for HTMLElement to be executed when user clicks outside of element.
 * @example
```
<span
    ref={spanEl => registerClickOutside(this, spanEl, () => this.test())}>
      Hello, World!
</span>;
```
 */
export function registerClickOutside(component, element, callback, opt = ClickOutsideOptionsDefaults) {
  const excludedNodes = getExcludedNodes(opt);
  getTriggerEvents(opt).forEach(triggerEvent => {
    window.addEventListener(triggerEvent, (e) => {
      initClickOutside(e, component, element, callback, excludedNodes);
    }, false);
  });
}
/**
 * Remove click outside callback function for HTMLElement.
 */
export function removeClickOutside(component, element, callback, opt = ClickOutsideOptionsDefaults) {
  getTriggerEvents(opt).forEach(triggerEvent => {
    window.removeEventListener(triggerEvent, (e) => {
      initClickOutside(e, component, element, callback);
    }, false);
  });
}
function initClickOutside(event, component, element, callback, excludedNodes) {
  const target = event.target;
  if (!element.contains(target) && !isExcluded(target, excludedNodes)) {
    callback.call(component);
  }
}
function getTriggerEvents(opt) {
  if (opt.triggerEvents) {
    return opt.triggerEvents.split(',').map(e => e.trim());
  }
  return ['click'];
}
function getExcludedNodes(opt) {
  if (opt.exclude) {
    try {
      return Array.from(document.querySelectorAll(opt.exclude));
    }
    catch (err) {
      console.warn(`@ClickOutside: Exclude: '${opt.exclude}' will not be evaluated. Check your exclude selector syntax.`, err);
    }
  }
  return;
}
function isExcluded(target, excudedNodes) {
  if (target && excudedNodes) {
    for (let excludedNode of excudedNodes) {
      if (excludedNode.contains(target)) {
        return true;
      }
    }
  }
  return false;
}
//# sourceMappingURL=click-outside.js.map
