Skip to content

Instantly share code, notes, and snippets.

@groverburger
Last active January 25, 2023 08:43
Show Gist options
  • Save groverburger/24e267a8f0641f8ff19262d1161fbc3d to your computer and use it in GitHub Desktop.
Save groverburger/24e267a8f0641f8ff19262d1161fbc3d to your computer and use it in GitHub Desktop.
Simple and fast immediate mode HTML gui rendering
let renderStack
let cache
function htmlify (what) {
if (!Array.isArray(what)) {
return what
}
const tagDef = typeof what[0] === 'string' ? what[0] : what[0].tag
const [tag, ...modifiers] = tagDef.split(' ')
const parser = document.createElement('div')
parser.innerHTML = `<${tag} ${modifiers.join(' ')}></${tag}>`
const parent = parser.firstElementChild
if (typeof what[0] !== 'string') {
for (const [key, value] of Object.entries(what[0])) {
if (key === 'tag') { continue }
parent[key] = value
}
}
what.slice(1).map(htmlify).forEach(element => {
if (element instanceof HTMLElement) {
parent.appendChild(element)
} else if (element !== undefined && element !== null) {
parent.append(element)
}
})
return parent
}
export function createComponent (func) {
return (...args) => {
if (!cache[renderStack]) {
cache[renderStack] = {
prevArgs: '',
element: null
}
}
const myCache = cache[renderStack]
renderStack.push(1)
const newArgs = JSON.stringify(args)
if (newArgs !== myCache.prevArgs || !myCache.element) {
myCache.prevArgs = newArgs
myCache.element = htmlify(func(...args))
}
renderStack.pop()
renderStack[renderStack.length - 1] += 1
return myCache.element
}
}
export class Renderer {
renderStack = []
cache = {}
render (element, component, ...args) {
this.renderStack = [0]
renderStack = this.renderStack
cache = this.cache
const result = component(...args)
if (!result) {
element.innerHTML = ''
} else if (result.outerHTML !== element.innerHTML) {
element.innerHTML = ''
element.appendChild(result)
}
return result
}
clear () {
this.renderStack = []
this.cache = {}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment