stencil-click-outside
Version:
Decorator for StencilJs to run annotated method on click outside of component.
107 lines (106 loc) • 3.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
var core_1 = require("@stencil/core");
var 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.
}
```
*/
function ClickOutside(opt) {
if (opt === void 0) { opt = ClickOutsideOptionsDefaults; }
return function (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
core_1.Build.connectedCallback = true;
core_1.Build.disconnectedCallback = true;
var connectedCallback = proto.connectedCallback, disconnectedCallback = proto.disconnectedCallback;
proto.connectedCallback = function () {
var host = core_1.getElement(this);
var method = this[methodName];
registerClickOutside(this, host, method, opt);
return connectedCallback && connectedCallback.call(this);
};
proto.disconnectedCallback = function () {
var host = core_1.getElement(this);
var method = this[methodName];
removeClickOutside(this, host, method, opt);
return disconnectedCallback && disconnectedCallback.call(this);
};
};
}
exports.ClickOutside = ClickOutside;
/**
* 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>;
```
*/
function registerClickOutside(component, element, callback, opt) {
if (opt === void 0) { opt = ClickOutsideOptionsDefaults; }
var excludedNodes = getExcludedNodes(opt);
getTriggerEvents(opt).forEach(function (triggerEvent) {
window.addEventListener(triggerEvent, function (e) {
initClickOutside(e, component, element, callback, excludedNodes);
}, false);
});
}
exports.registerClickOutside = registerClickOutside;
/**
* Remove click outside callback function for HTMLElement.
*/
function removeClickOutside(component, element, callback, opt) {
if (opt === void 0) { opt = ClickOutsideOptionsDefaults; }
getTriggerEvents(opt).forEach(function (triggerEvent) {
window.removeEventListener(triggerEvent, function (e) {
initClickOutside(e, component, element, callback);
}, false);
});
}
exports.removeClickOutside = removeClickOutside;
function initClickOutside(event, component, element, callback, excludedNodes) {
var target = event.target;
if (!element.contains(target) && !isExcluded(target, excludedNodes)) {
callback.call(component);
}
}
function getTriggerEvents(opt) {
if (opt.triggerEvents) {
return opt.triggerEvents.split(",").map(function (e) { return 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 (var _i = 0, excudedNodes_1 = excudedNodes; _i < excudedNodes_1.length; _i++) {
var excludedNode = excudedNodes_1[_i];
if (excludedNode.contains(target)) {
return true;
}
}
}
return false;
}