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 instance.$emit = (eventName, data = {}) => {
45 const event = new CustomEvent(`${name}-${eventName}`, {
49 instance.$el.dispatchEvent(event);
51 if (typeof instance.setup === 'function') {
55 console.error('Failed to create component', e, name, element);
59 // Add to global listing
60 if (typeof window.components[name] === "undefined") {
61 window.components[name] = [];
63 window.components[name].push(instance);
65 // Add to element listing
66 if (typeof element.components === 'undefined') {
67 element.components = {};
69 element.components[name] = instance;
73 * Parse out the element references within the given element
74 * for the given component name.
75 * @param {String} name
76 * @param {Element} element
78 function parseRefs(name, element) {
82 const prefix = `${name}@`
83 const selector = `[refs*="${prefix}"]`;
84 const refElems = [...element.querySelectorAll(selector)];
85 if (element.matches(selector)) {
86 refElems.push(element);
89 for (const el of refElems) {
90 const refNames = el.getAttribute('refs')
92 .filter(str => str.startsWith(prefix))
93 .map(str => str.replace(prefix, ''))
95 for (const ref of refNames) {
97 if (typeof manyRefs[ref] === 'undefined') {
100 manyRefs[ref].push(el);
103 return {refs, manyRefs};
107 * Parse out the element component options.
108 * @param {String} name
109 * @param {Element} element
110 * @return {Object<String, String>}
112 function parseOpts(name, element) {
114 const prefix = `option:${name}:`;
115 for (const {name, value} of element.attributes) {
116 if (name.startsWith(prefix)) {
117 const optName = name.replace(prefix, '');
118 opts[kebabToCamel(optName)] = value || '';
125 * Convert a kebab-case string to camelCase
126 * @param {String} kebab
129 function kebabToCamel(kebab) {
130 const ucFirst = (word) => word.slice(0,1).toUpperCase() + word.slice(1);
131 const words = kebab.split('-');
132 return words[0] + words.slice(1).map(ucFirst).join('');
136 * Initialize all components found within the given element.
137 * @param parentElement
139 function initAll(parentElement) {
140 if (typeof parentElement === 'undefined') parentElement = document;
142 // Old attribute system
143 for (const componentName of Object.keys(componentMapping)) {
144 searchForComponentInParent(componentName, parentElement);
147 // New component system
148 const componentElems = parentElement.querySelectorAll(`[component],[components]`);
150 for (const el of componentElems) {
151 const componentNames = `${el.getAttribute('component') || ''} ${(el.getAttribute('components'))}`.toLowerCase().split(' ').filter(Boolean);
152 for (const name of componentNames) {
153 initComponent(name, el);
158 window.components.init = initAll;
159 window.components.first = (name) => (window.components[name] || [null])[0];
161 export default initAll;
165 * @property {HTMLElement} $el
166 * @property {Object<String, HTMLElement>} $refs
167 * @property {Object<String, HTMLElement[]>} $manyRefs
168 * @property {Object<String, String>} $opts
169 * @property {function(string, Object)} $emit