Skip to content

Instantly share code, notes, and snippets.

@adminy
Last active November 4, 2022 22:43
Show Gist options
  • Save adminy/504f8b6da3ace826f2715b439f132537 to your computer and use it in GitHub Desktop.
Save adminy/504f8b6da3ace826f2715b439f132537 to your computer and use it in GitHub Desktop.
JSX Real DoM Magic Framework
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Site Title</title>
<link href="https://cdn.jsdelivr.net/gh/hung1001/font-awesome-pro@4cac1a6/css/all.css" rel="stylesheet" type="text/css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/css/bulma.min.css">
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<script type="text/babel" data-type="module" src="index.js"></script> <!-- data-plugins="syntax-jsx,transform-modules-commonjs" data-presets="no-strict" -->
</body>
</html>
import React from './magic.mjs'
const [value, setValue] = React.useState(0)
const SubSubComp = ({param}) => {
return <div>{param} - {value}</div>
}
const SubComp = () => (
<h2 class='subtitle'>
<button onClick={() => console.log('lol', setValue(val => val + 1))} class='button'>+</button>
<span>Subtitle Count: {value} </span>
<br />
<SubSubComp param={value} />
</h2>
)
const App = () => (
<div>
<h1 class='title'>Hello ... Good day</h1>
<span class='subtitle'>I say</span>
<SubComp />
</div>
)
React.render(App)
const addProps = (element, props) => {
for (const prop in (props || {})) {
const isEvent = prop.startsWith('on')
const eventName = prop.slice(2, prop.length).toLowerCase()
const value = props[prop]
isEvent && element.addEventListener(eventName, value)
!isEvent && element.setAttribute(prop, value)
}
}
// const getCaller = function x(a,b,c){function d(e,f){d=f}c=(b=Error)[a='prepareStackTrace'];b.captureStackTrace(b[a]=d,x);d.stack;b[a]=c;return d}
// const calls = (new Error().stack).split('\n').slice(2).map(line => line.split(' at ')[1].split('(')[0].trim()).filter(line => !line.startsWith('Object.')).slice(0, -1)
const useState = value => {
const listeners = []
const get = listener => {
typeof listener === 'function' && listeners.push(listener)
// !listener && (hasToUpdateANonListener = true)
return value
}
const update = updateCallback => {
value = updateCallback(value)
listeners.map(fn => fn(value))
return value
}
class MagicValue {
[Symbol.toPrimitive]() {
console.error('You cannot convert useState variable to primitive!')
}
get = get
}
return [new MagicValue(), update]
}
const prepareChild = child => child.nodeName ? child : document.createTextNode(
typeof child === 'string' ? child : JSON.stringify(child)
)
const addChildren = async (element, children) => {
for (const childPromise of children) {
let child = await Promise.resolve(childPromise)
// the child can be a function, in which case we resolve to a "reactive" value
let childElem = null
if (child.constructor.name === 'MagicValue')
child = child.get(newVal => {
const newChild = prepareChild(newVal)
childElem.replaceWith(newChild)
childElem = newChild
})
childElem = element.appendChild(prepareChild(child))
}
}
const createElement = (name, props, ...children) => {
if (typeof name === 'function') {
const elements = name(props)
name.listen = () => elements.replaceWith(name(props))
return elements
}
else {
const element = document.createElement(name)
addProps(element, props)
addChildren(element, children)
return element
}
}
const Render = (() => {
let mainTree = null
let oldTree = null
const render = main => {
mainTree = main
oldTree = main()
document.body.appendChild(oldTree)
}
const cleanup = val => {
document.body.removeChild(oldTree)
render(mainTree)
return val
}
return { render, cleanup }
})()
window.Render = Render
export default { createElement, render: Render.render, useState }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment