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) {
73 const prefix = `${name}@`
74 const refElems = element.querySelectorAll(`[refs*="${prefix}"]`);
75 for (const el of refElems) {
76 const refNames = el.getAttribute('refs')
78 .filter(str => str.startsWith(prefix))
79 .map(str => str.replace(prefix, ''));
80 for (const ref of refNames) {
82 if (typeof manyRefs[ref] === 'undefined') {
85 manyRefs[ref].push(el);
88 return {refs, manyRefs};
92 * Parse out the element component options.
93 * @param {String} name
94 * @param {Element} element
95 * @return {Object<String, String>}
97 function parseOpts(name, element) {
99 const prefix = `option:${name}:`;
100 for (const {name, value} of element.attributes) {
101 if (name.startsWith(prefix)) {
102 const optName = name.replace(prefix, '');
103 opts[kebabToCamel(optName)] = value || '';
110 * Convert a kebab-case string to camelCase
111 * @param {String} kebab
114 function kebabToCamel(kebab) {
115 const ucFirst = (word) => word.slice(0,1).toUpperCase() + word.slice(1);
116 const words = kebab.split('-');
117 return words[0] + words.slice(1).map(ucFirst).join();
121 * Initialize all components found within the given element.
122 * @param parentElement
124 function initAll(parentElement) {
125 if (typeof parentElement === 'undefined') parentElement = document;
127 // Old attribute system
128 for (const componentName of Object.keys(componentMapping)) {
129 searchForComponentInParent(componentName, parentElement);
132 // New component system
133 const componentElems = parentElement.querySelectorAll(`[component],[components]`);
135 for (const el of componentElems) {
136 const componentNames = `${el.getAttribute('component') || ''} ${(el.getAttribute('components'))}`.toLowerCase().split(' ').filter(Boolean);
137 for (const name of componentNames) {
138 initComponent(name, el);
143 window.components.init = initAll;
144 window.components.first = (name) => (window.components[name] || [null])[0];
146 export default initAll;
150 * @property {HTMLElement} $el
151 * @property {Object<String, HTMLElement>} $refs
152 * @property {Object<String, HTMLElement[]>} $manyRefs
153 * @property {Object<String, String>} $opts