commit 9fd405d39a6715963afb362c7f856db19c22fac0 Author: Geoff Doty Date: Sat Jun 15 13:57:04 2024 -0400 initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..adfdf4c --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Alps + +A Alpine Inspired Framework Learning Experiment \ No newline at end of file diff --git a/src/alp.js b/src/alp.js new file mode 100644 index 0000000..808d9a6 --- /dev/null +++ b/src/alp.js @@ -0,0 +1,91 @@ + +function saferEval(expression, dataContext, additionalHelperVariables = {}) { + return (new Function(['$data', ...Object.keys(additionalHelperVariables)], `var result; with($data) { result = ${expression} }; return result`))( + dataContext, ...Object.values(additionalHelperVariables) + ) +} + +const Alps = { + directives: { + 'x-text': (el, value) => { + el.innerText = value; + }, + 'x-show': (el, value) => { + el.style.display = value ? 'block' : 'none'; + }, + }, + start() { + // root scope + this.root = document.querySelector('[x-data]'); + + // initialize proxy data + this.data = this.initialize(); + + // add event listeners + this.registerListeners(); + + // refresh dom + this.refresh(); + }, + observe(data) { + let self = this; + + return new Proxy(data, { + set(target, key, value) { + target[key] = value; + + self.refresh(); + } + }); + }, + refresh() { + // dom tree walker + this.walkDom(this.root, (el) => { + // loop through attributes and look for directives + Array.from(el.attributes).forEach((attribute) => { + if(!Object.keys(this.directives).includes(attribute.name)) return; + + this.directives[attribute.name](el, eval(`with (this.data) {(${attribute.value})}`)); + }); + }); + }, + registerListeners() { + this.walkDom(this.root, (el) => { + // loop through attributes and look for events + Array.from(el.attributes).forEach((attribute) => { + // exit early (only events here) + if(!attribute.name.startsWith('@')) return; + + // find event + let event = attribute.name.replace('@', ''); + + // register event + el.addEventListener((event), () => { + eval(`with (this.data) {(${attribute.value})}`); + }); + }); + }); + }, + initialize() { + // root scope data on x-data + let raw = this.root.getAttribute('x-data'); + + // eval that + return this.observe(eval(`(${raw})`)); + }, + walkDom(el, callback) { + // root element + callback(el); + + // roots child + el = el.firstElementChild; + + while(el) { + // get all the children's children + this.walkDom(el, callback); + + // next + el = el.nextElementSibling; + } + } +}