UNPKG

@studiometa/js-toolkit

Version:

A set of useful little bits of JavaScript to boost your project! 🚀

254 lines (253 loc) • 7.76 kB
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