NEWS: Welcome to my new homepage! <3

Restructured code - figure - Unnamed repository; edit this file 'description' to name the repository.

figure

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 0cd4b7e389e852cb230031c3b53ad236448a71f0
parent 71bcd6376c673c2b386604e00d1e55a030d892b3
Author: typable <contact@typable.dev>
Date:   Sat, 17 Sep 2022 01:26:43 +0200

Restructured code

Diffstat:
MREADME.md | 4++--
Mexamples/picker.html | 11+++++------
Amods.js | 32++++++++++++++++++++++++++++++++
Asegment.js | 141+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dtlx.js | 138-------------------------------------------------------------------------------
5 files changed, 180 insertions(+), 146 deletions(-)

diff --git a/README.md b/README.md @@ -1,2 +1,2 @@ -# tlx -Template literals syntax for React Hooks +# segment +Reactive template literals for React diff --git a/examples/picker.html b/examples/picker.html @@ -4,9 +4,7 @@ import {useState, createElement} from 'https://cdn.skypack.dev/react'; import {render} from 'https://cdn.skypack.dev/react-dom'; - import {createTlx, css} from '../tlx.js'; - - const tlx = createTlx(createElement); + import {segment, html, css} from '../segment.js'; const Range = ({index, value, setValue}) => { const style = css` @@ -30,7 +28,7 @@ } `; - return tlx` + return html` <div class="range v-${index}-${value}"> ${style} <input @@ -73,7 +71,7 @@ const hex = (dec) => dec.toString(16).toUpperCase().padStart(2, '0'); - return tlx` + return html` <div class="picker"> ${style} <div class="preview"></div> @@ -85,8 +83,9 @@ </div> `; } - + const app = document.querySelector('#app'); + segment({createElement}); render(createElement(Picker), app); </script> diff --git a/mods.js b/mods.js @@ -0,0 +1,32 @@ +export const defCss = ({html, compose, feed}) => { + return (parts, ...props) => { + const [css, refs] = compose(parts, props); + return html` + <style type="text/css"> + ${feed(css, refs).join('')} + </style> + `; + }; +} + +export const defStyle = ({compose, feed}) => { + return (parts, ...props) => { + const [css, refs] = compose(parts, props); + const styles = {}; + for(const item of css.split(';')) { + if(item.trim().length === 0) { + break; + } + let [key, value] = item.split(':'); + key = key.trim(); + let match = null; + if((match = /-(\w)/.exec(key)) !== null) { + const char = match[1]; + key = key.replace(`-${char}`, char.toUpperCase()); + } + value = value.trim(); + styles[key] = feed(value, refs).join(''); + } + return styles; + }; +} diff --git a/segment.js b/segment.js @@ -0,0 +1,141 @@ +const parser = new DOMParser(); +let count = 1000; +let create = null; + +const segment = ({createElement}) => { + create = createElement; +} + +const html = (strings, ...props) => { + const [html, refs] = compose(strings, props); + let dom; + try { + dom = parser.parseFromString(html, 'text/html'); + } + catch(error) { + console.error(error); + throw 'Invalid DOM structure!'; + } + const node = dom.body.childNodes[0] ?? dom.head.childNodes[0]; + const elements = render(node, refs); + if(elements.length === 0) { + throw 'No DOM element was returned!'; + } + if(elements.length > 1) { + console.warn('Only one DOM element can be returned!'); + } + return elements[0]; +} + +/** + * Joins the template literal strings together and replaces the properties with references. + * The properties are being mapped to there corresponding references and with the populated + * HTML string returned. + * + * @param {string[]} strings - The template literal strings + * @param {any[]} props - The template literal properties + * @return {any[]} The joined HTML string and the properties mapped to there references. + */ +const compose = (strings, props) => { + const refs = {}; + let string = ''; + for(let i = 0; i < strings.length; i++) { + string += strings[i]; + if(props[i] !== undefined) { + const uid = `$tlx-${count++}`; + refs[uid] = props[i]; + string += uid ?? ''; + } + } + return [string.trim(), refs]; +} + +/** + * Injects the properties into the corresponding reference locations of the string. + * + * @param {string} string - The string containing references + * @param {any[]} refs - The properties mapped to there references + * @return {any[]} The string populate with the passed properties + */ +const feed = (string, refs) => { + const expr = /\$tlx-\d+/g; + let values = []; + let match = null; + let last = 0; + while((match = expr.exec(string)) !== null) { + const index = match.index; + const uid = match[0]; + values.push(string.substring(last, index)); + let value = refs[uid]; + if(value instanceof Function) { + value = value(); + } + values.push(value); + last = index + uid.length; + } + values.push(string.substring(last)); + return values; +} + +const render = (node, refs) => { + if(node.nodeType === Node.TEXT_NODE) { + return feed(node.textContent, refs); + } + if(node.nodeType === Node.COMMENT_NODE) { + return []; + } + const tag = node.tagName; + const attributes = {}; + for(const attribute of node.attributes) { + const key = attribute.name; + const value = attribute.textContent; + let isDynamic = false; + for(const ref in refs) { + if(value === ref) { + let match = null; + if((match = /^@(\w+)$/.exec(key)) !== null) { + const event = match[1]; + attributes[`on${event.substring(0, 1).toUpperCase()}${event.substring(1)}`] = refs[ref]; + } + else { + attributes[key] = refs[ref]; + } + isDynamic = true; + break; + } + } + if(!isDynamic) { + if(key === 'style') { + const styles = {}; + for(const item of value.split(';')) { + if(item.trim().length === 0) { + break; + } + let [key, value] = item.split(':'); + key = key.trim(); + let match = null; + if((match = /-(\w)/.exec(key)) !== null) { + const char = match[1]; + key = key.replace(`-${char}`, char.toUpperCase()); + } + value = value.trim(); + styles[key] = feed(value, refs).join(''); + } + attributes[key] = styles; + } + else { + attributes[key] = feed(value, refs).join(''); + } + } + } + const children = []; + (node.childNodes ?? []).forEach((child) => children.push(...render(child, refs))); + return [create(tag, attributes, ...children)]; +} + +import {defCss, defStyle} from './mods.js'; + +const css = defCss({html, compose, feed}); +const style = defStyle({html, compose, feed}); + +export { segment, html, css, style }; diff --git a/tlx.js b/tlx.js @@ -1,138 +0,0 @@ -const parser = new DOMParser(); -let counter = 1000; -let tlx = null; - -export const createTlx = (createElement) => { - function parse(parts, ...props) { - const [html, refs] = ltr(parts, props); - const dom = parser.parseFromString(html, 'text/html'); - const node = dom.body.childNodes[0] ?? dom.head.childNodes[0]; - const elements = render(node, refs); - if(elements.length !== 1) { - throw 'invalid VDOM structure!'; - } - return elements[0]; - } - - function render(node, refs) { - if(node.nodeType === Node.TEXT_NODE) { - return apply(node.textContent, refs); - } - if(node.nodeType === Node.COMMENT_NODE) { - return []; - } - const tag = node.tagName; - const attributes = {}; - for(const attribute of node.attributes) { - const key = attribute.name; - const value = attribute.textContent; - let isDynamic = false; - for(const ref in refs) { - if(value === ref) { - let match = null; - if((match = /^@(\w+)$/.exec(key)) !== null) { - const event = match[1]; - attributes[`on${event.substr(0, 1).toUpperCase()}${event.substr(1)}`] = refs[ref]; - } - else { - attributes[key] = refs[ref]; - } - isDynamic = true; - break; - } - } - if(!isDynamic) { - if(key === 'style') { - const styles = {}; - for(const item of value.split(';')) { - if(item.trim().length === 0) { - break; - } - let [key, value] = item.split(':'); - key = key.trim(); - let match = null; - if((match = /-(\w)/.exec(key)) !== null) { - const char = match[1]; - key = key.replace(`-${char}`, char.toUpperCase()); - } - value = value.trim(); - styles[key] = apply(value, refs).join(''); - } - attributes[key] = styles; - } - else { - attributes[key] = apply(value, refs).join(''); - } - } - } - const children = []; - (node.childNodes ?? []).forEach((child) => children.push(...render(child, refs))); - return [createElement(tag, attributes, ...children)]; - } - - tlx = parse; - return tlx; -} - -export const css = (parts, ...props) => { - const [css, refs] = ltr(parts, props); - return tlx` - <style type="text/css"> - ${apply(css, refs).join('')} - </style> - `; -} - -export const style = (parts, ...props) => { - const [css, refs] = ltr(parts, props); - const styles = {}; - for(const item of css.split(';')) { - if(item.trim().length === 0) { - break; - } - let [key, value] = item.split(':'); - key = key.trim(); - let match = null; - if((match = /-(\w)/.exec(key)) !== null) { - const char = match[1]; - key = key.replace(`-${char}`, char.toUpperCase()); - } - value = value.trim(); - styles[key] = apply(value, refs).join(''); - } - return styles; -} - -function apply(value, refs) { - let values = []; - const expr = /\$tlx-\d+/g; - let match = null; - let last = 0; - while((match = expr.exec(value)) !== null) { - const index = match.index; - values.push(value.substring(last, index)); - let refValue = refs[match[0]]; - if(refValue instanceof Function) { - refValue = refValue(); - } - values.push(refValue); - last = index + match[0].length; - } - values.push(value.substring(last)); - return values; -} - -function ltr(parts, props) { - let string = ''; - const refs = {}; - for(let i = 0; i < parts.length; i++) { - string += parts[i]; - if(props[i] !== undefined) { - const id = `$tlx-${counter}`; - refs[id] = props[i]; - string += id ?? ''; - counter++; - } - } - return [string.trim(), refs]; -}