X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/4310d34135243e91fdacd960a825f60f0231621e..refs/pull/3918/head:/resources/js/services/components.js diff --git a/resources/js/services/components.js b/resources/js/services/components.js index 04fd0dcf4..d1503db4d 100644 --- a/resources/js/services/components.js +++ b/resources/js/services/components.js @@ -1,5 +1,23 @@ +import {kebabToCamel, camelToKebab} from "./text"; + +/** + * A mapping of active components keyed by name, with values being arrays of component + * instances since there can be multiple components of the same type. + * @type {Object} + */ const components = {}; -const componentMap = {}; + +/** + * A mapping of component class models, keyed by name. + * @type {Object>} + */ +const componentModelMap = {}; + +/** + * A mapping of active component maps, keyed by the element components are assigned to. + * @type {WeakMap>} + */ +const elementComponentMap = new WeakMap(); /** * Initialize a component instance on the given dom element. @@ -8,7 +26,7 @@ const componentMap = {}; */ function initComponent(name, element) { /** @type {Function|undefined} **/ - const componentModel = componentMap[name]; + const componentModel = componentModelMap[name]; if (componentModel === undefined) return; // Create our component instance @@ -33,11 +51,10 @@ function initComponent(name, element) { } components[name].push(instance); - // Add to element listing - if (typeof element.components === 'undefined') { - element.components = {}; - } - element.components[name] = instance; + // Add to element mapping + const elComponents = elementComponentMap.get(element) || {}; + elComponents[name] = instance; + elementComponentMap.set(element, elComponents); } /** @@ -92,17 +109,6 @@ function parseOpts(name, element) { return opts; } -/** - * Convert a kebab-case string to camelCase - * @param {String} kebab - * @returns {string} - */ -function kebabToCamel(kebab) { - const ucFirst = (word) => word.slice(0,1).toUpperCase() + word.slice(1); - const words = kebab.split('-'); - return words[0] + words.slice(1).map(ucFirst).join(''); -} - /** * Initialize all components found within the given element. * @param {Element|Document} parentElement @@ -125,7 +131,7 @@ export function init(parentElement = document) { export function register(mapping) { const keys = Object.keys(mapping); for (const key of keys) { - componentMap[camelToKebab(key)] = mapping[key]; + componentModelMap[camelToKebab(key)] = mapping[key]; } } @@ -143,10 +149,17 @@ export function first(name) { * @param {String} name * @returns {Component[]} */ -export function get(name = '') { +export function get(name) { return components[name] || []; } -function camelToKebab(camelStr) { - return camelStr.replace(/[A-Z]/g, (str, offset) => (offset > 0 ? '-' : '') + str.toLowerCase()); +/** + * Get the first component, of the given name, that's assigned to the given element. + * @param {Element} element + * @param {String} name + * @returns {Component|null} + */ +export function firstOnElement(element, name) { + const elComponents = elementComponentMap.get(element) || {}; + return elComponents[name] || null; } \ No newline at end of file