1 const componentMapping = {};
3 const definitionFiles = require.context('./', false, /\.js$/);
4 for (const fileName of definitionFiles.keys()) {
5 const name = fileName.replace('./', '').split('.')[0];
6 if (name !== 'index') {
7 componentMapping[name] = definitionFiles(fileName).default;
11 window.components = {};
14 * Initialize components of the given name within the given element.
15 * @param {String} componentName
16 * @param {HTMLElement|Document} parentElement
18 function searchForComponentInParent(componentName, parentElement) {
19 const elems = parentElement.querySelectorAll(`[${componentName}]`);
20 for (let j = 0, jLen = elems.length; j < jLen; j++) {
21 initComponent(componentName, elems[j]);
26 * Initialize a component instance on the given dom element.
27 * @param {String} name
28 * @param {Element} element
30 function initComponent(name, element) {
31 const componentModel = componentMapping[name];
32 if (componentModel === undefined) return;
34 // Create our component instance
37 instance = new componentModel(element);
38 instance.$el = element;
39 instance.$refs = parseRefs(name, element);
40 instance.$opts = parseOpts(name, element);
41 if (typeof instance.setup === 'function') {
45 console.error('Failed to create component', e, name, element);
49 // Add to global listing
50 if (typeof window.components[name] === "undefined") {
51 window.components[name] = [];
53 window.components[name].push(instance);
55 // Add to element listing
56 if (typeof element.components === 'undefined') {
57 element.components = {};
59 element.components[name] = instance;
63 * Parse out the element references within the given element
64 * for the given component name.
65 * @param {String} name
66 * @param {Element} element
68 function parseRefs(name, element) {
70 const prefix = `${name}@`
71 const refElems = element.querySelectorAll(`[refs*="${prefix}"]`);
72 for (const el of refElems) {
73 const refNames = el.getAttribute('refs')
75 .filter(str => str.startsWith(prefix))
76 .map(str => str.replace(prefix, ''));
77 for (const ref of refNames) {
85 * Parse out the element component options.
86 * @param {String} name
87 * @param {Element} element
88 * @return {Object<String, String>}
90 function parseOpts(name, element) {
92 const prefix = `option:${name}:`;
93 for (const {name, value} of element.attributes) {
94 if (name.startsWith(prefix)) {
95 const optName = name.replace(prefix, '');
96 opts[kebabToCamel(optName)] = value || '';
103 * Convert a kebab-case string to camelCase
104 * @param {String} kebab
107 function kebabToCamel(kebab) {
108 const ucFirst = (word) => word.slice(0,1).toUpperCase() + word.slice(1);
109 const words = kebab.split('-');
110 return words[0] + words.slice(1).map(ucFirst).join();
114 * Initialize all components found within the given element.
115 * @param parentElement
117 function initAll(parentElement) {
118 if (typeof parentElement === 'undefined') parentElement = document;
120 // Old attribute system
121 for (const componentName of Object.keys(componentMapping)) {
122 searchForComponentInParent(componentName, parentElement);
125 // New component system
126 const componentElems = parentElement.querySelectorAll(`[component],[components]`);
128 for (const el of componentElems) {
129 const componentNames = `${el.getAttribute('component') || ''} ${(el.getAttribute('components'))}`.toLowerCase().split(' ').filter(Boolean);
130 for (const name of componentNames) {
131 initComponent(name, el);
136 window.components.init = initAll;
138 export default initAll;
142 * @property {HTMLElement} $el
143 * @property {Object<String, HTMLElement>} $refs
144 * @property {Object<String, String>} $opts