@studiometa/js-toolkit
Version:
A set of useful little bits of JavaScript to boost your project! 🚀
254 lines (253 loc) • 7.76 kB
JavaScript
import { getAllProperties } from "../../utils/object/getAllProperties.js";
import { dashCase, isArray, pascalCase } from "../../utils/index.js";
import { getEventTarget, eventIsNative, eventIsDefinedInConfig } from "../utils.js";
import { AbstractManager } from "./AbstractManager.js";
import { normalizeRefName } from "./RefsManager.js";
import { features } from "../features.js";
const regexes = /* @__PURE__ */ new Map();
function getRegex(regex) {
if (!regexes.has(regex)) {
regexes.set(regex, new RegExp(regex));
}
return regexes.get(regex);
}
function getEventNameByMethod(method, name = "") {
const regex = getRegex(`^on${pascalCase(name)}([A-Z].*)$`);
const [, event] = method.match(regex);
return dashCase(event);
}
function getEventMethodsByName(that, name = "") {
const regex = getRegex(`^on${pascalCase(name)}[A-Z].*$`);
const key = regex.toString();
let methods = that.__methodsCache.get(key);
if (!methods) {
methods = Array.from(
getAllProperties(that.__base, [], (method) => regex.test(method)).reduce(
(set, [method]) => set.add(method),
/* @__PURE__ */ new Set()
)
);
that.__methodsCache.set(key, methods);
}
return methods;
}
function manageRef(that, name, elements, mode = "add") {
const action = `${mode}EventListener`;
const methods = getEventMethodsByName(that, name);
for (const method of methods) {
const event = getEventNameByMethod(method, name);
for (const element of elements) {
element?.[action](event, that.__refsHandler);
}
}
}
function manageChild(that, name, instance, mode = "add") {
const action = mode === "add" ? "$on" : "$off";
const methods = getEventMethodsByName(that, name);
const config = instance.__config;
for (const method of methods) {
const event = getEventNameByMethod(method, name);
if (getEventTarget(instance, event, config)) {
instance[action](event, that.__childrenHandler);
}
}
}
const isDocumentRegex = /^onDocument[A-Z][a-z]+/;
const isWindowRegex = /^onWindow[A-Z][a-z]+/;
const methodIsDocument = (method) => isDocumentRegex.test(method);
const methodIsWindow = (method) => isWindowRegex.test(method);
const methodIsGlobal = (method) => methodIsWindow(method) || methodIsDocument(method);
const getGlobalEventTarget = (method) => methodIsDocument(method) ? document : window;
function manageRootElement(that, mode = "add") {
const modeMethod = `${mode}EventListener`;
const methods = getEventMethodsByName(that);
const { __base: base, __config: config } = that;
for (const method of methods) {
let event = getEventNameByMethod(method);
if (methodIsGlobal(method)) {
event = getEventNameByMethod(method, methodIsDocument(method) ? "document" : "window");
const target = getGlobalEventTarget(method);
target[modeMethod](
event,
methodIsDocument(method) ? that.__documentHandler : that.__windowHandler
);
} else {
const target = getEventTarget(base, event, config);
target?.[modeMethod](event, that.__rootElementHandler);
}
}
}
function normalizeParams(params = {}) {
return {
event: null,
args: [],
index: 0,
target: null,
...params
};
}
class EventsManager extends AbstractManager {
__methodsCache = /* @__PURE__ */ new Map();
/**
* Event listener object for the root element.
*/
__rootElementHandler = {
handleEvent: (event) => {
const normalizedEventName = pascalCase(event.type);
const method = `on${normalizedEventName}`;
const isCustomEvent = event instanceof CustomEvent;
this.__base[method](
normalizeParams({
event,
args: isCustomEvent ? event.detail ?? [] : [],
target: isCustomEvent ? this.__base : this.__element
})
);
}
};
/**
* Event listener object for the root element.
*
* @type {EventListenerObject}
*/
__documentHandler = {
/**
* @param {Event|CustomEvent} event
* @return {void}
*/
handleEvent: (event) => {
const normalizedEventName = pascalCase(event.type);
const method = `onDocument${normalizedEventName}`;
this.__base[method](normalizeParams({ event, target: document }));
}
};
/**
* Event listener object for the root element.
*
* @type {EventListenerObject}
*/
__windowHandler = {
/**
* @param {Event|CustomEvent} event
* @return {void}
*/
handleEvent: (event) => {
const normalizedEventName = pascalCase(event.type);
const method = `onWindow${normalizedEventName}`;
this.__base[method](normalizeParams({ event, target: window }));
}
};
/**
* Event listener object for the refs.
*/
__refsHandler = {
handleEvent: (event) => {
const ref = event.currentTarget;
const attributes = features.get("attributes");
const refName = normalizeRefName(ref.getAttribute(attributes.ref));
const normalizedRefName = pascalCase(refName);
const normalizedEventName = pascalCase(event.type);
const method = `on${normalizedRefName}${normalizedEventName}`;
let index = 0;
if (isArray(this.__base.$refs[refName])) {
index = this.__base.$refs[refName].indexOf(ref);
}
this.__base[method](normalizeParams({ event, index, target: ref }));
}
};
/**
* Event listener object for the children.
*/
__childrenHandler = {
handleEvent: (event) => {
const children = this.__base.$children;
for (const childName of Object.keys(children)) {
let index = -1;
for (const child of children[childName]) {
index++;
if (child.$el === event.currentTarget && (eventIsNative(event.type, child.$el) || eventIsDefinedInConfig(event.type, child.__config))) {
const normalizedEventName = pascalCase(event.type);
const normalizedChildName = pascalCase(childName);
const method = `on${normalizedChildName}${normalizedEventName}`;
const args = isArray(event.detail) ? event.detail : [];
this.__base[method](normalizeParams({ event, index, args, target: child }));
}
}
}
}
};
/**
* Class constructor.
*/
constructor(base) {
super(base);
}
/**
* Bind event methods to the given ref elements.
*
* @param {string} name
* The name of the ref.
* @param {HTMLElement[]} elements
* The elements of the ref.
* @return {void}
*/
bindRef(name, elements) {
manageRef(this, name, elements);
}
/**
* Bind event methods to the given ref elements.
*
* @param {string} name
* The name of the ref.
* @param {HTMLElement[]} elements
* The elements of the ref.
* @return {void}
*/
unbindRef(name, elements) {
manageRef(this, name, elements, "remove");
}
/**
* Bind event methods to the given child instance.
*
* @param {string} name
* The name of the ref.
* @param {Base} instance
* A base instance.
* @return {void}
*/
bindChild(name, instance) {
manageChild(this, name, instance);
}
/**
* Unbind event methods to the given child instance.
*
* @param {string} name
* The name of the ref.
* @param {Base} instance
* A base instance.
* @return {void}
*/
unbindChild(name, instance) {
manageChild(this, name, instance, "remove");
}
/**
* Bind event methods on the root element.
*
* @return {void}
*/
bindRootElement() {
manageRootElement(this);
}
/**
* Unbind event method from the root element.
*
* @return {void}
*/
unbindRootElement() {
manageRootElement(this, "remove");
}
}
export {
EventsManager
};
//# sourceMappingURL=EventsManager.js.map