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 const allRefs = parseRefs(name, element);
40 instance.$refs = allRefs.refs;
41 instance.$manyRefs = allRefs.manyRefs;
42 instance.$opts = parseOpts(name, element);
43 if (typeof instance.setup === 'function') {
47 console.error('Failed to create component', e, name, element);
51 // Add to global listing
52 if (typeof window.components[name] === "undefined") {
53 window.components[name] = [];
55 window.components[name].push(instance);
57 // Add to element listing
58 if (typeof element.components === 'undefined') {
59 element.components = {};
61 element.components[name] = instance;
65 * Parse out the element references within the given element
66 * for the given component name.
67 * @param {String} name
68 * @param {Element} element
70 function parseRefs(name, element) {
74 const prefix = `${name}@`
75 const selector = `[refs*="${prefix}"]`;
76 const refElems = [...element.querySelectorAll(selector)];
77 if (element.matches(selector)) {
78 refElems.push(element);
81 for (const el of refElems) {
82 const refNames = el.getAttribute('refs')
84 .filter(str => str.startsWith(prefix))
85 .map(str => str.replace(prefix, ''))
87 for (const ref of refNames) {
89 if (typeof manyRefs[ref] === 'undefined') {
92 manyRefs[ref].push(el);
95 return {refs, manyRefs};
99 * Parse out the element component options.
100 * @param {String} name
101 * @param {Element} element
102 * @return {Object<String, String>}
104 function parseOpts(name, element) {
106 const prefix = `option:${name}:`;
107 for (const {name, value} of element.attributes) {
108 if (name.startsWith(prefix)) {
109 const optName = name.replace(prefix, '');
110 opts[kebabToCamel(optName)] = value || '';
117 * Convert a kebab-case string to camelCase
118 * @param {String} kebab
121 function kebabToCamel(kebab) {
122 const ucFirst = (word) => word.slice(0,1).toUpperCase() + word.slice(1);
123 const words = kebab.split('-');
124 return words[0] + words.slice(1).map(ucFirst).join();
128 * Initialize all components found within the given element.
129 * @param parentElement
131 function initAll(parentElement) {
132 if (typeof parentElement === 'undefined') parentElement = document;
134 // Old attribute system
135 for (const componentName of Object.keys(componentMapping)) {
136 searchForComponentInParent(componentName, parentElement);
139 // New component system
140 const componentElems = parentElement.querySelectorAll(`[component],[components]`);
142 for (const el of componentElems) {
143 const componentNames = `${el.getAttribute('component') || ''} ${(el.getAttribute('components'))}`.toLowerCase().split(' ').filter(Boolean);
144 for (const name of componentNames) {
145 initComponent(name, el);
150 window.components.init = initAll;
151 window.components.first = (name) => (window.components[name] || [null])[0];
153 export default initAll;
157 * @property {HTMLElement} $el
158 * @property {Object<String, HTMLElement>} $refs
159 * @property {Object<String, HTMLElement[]>} $manyRefs
160 * @property {Object<String, String>} $opts