bulgogi
v1.2.0
Published
non-framework render functions
Downloads
14
Maintainers
Readme
:bowl_with_spoon: bulgogi
bulgogi is the no-framework render function proof of concept.
Features
- minimal footprint
- nest views
- immediate mode GUI
- imperfectly save and restore focus
- supports async view functions
Features (of ommission)
- JSX
- state management
- lifecycle hooks
- props
- cute declarative/imperative attribute syntax (I'm lookin' at you, Vue)
- pages of README about how absolutely f##cking brilliant this amazing new framework is
- sexyness and trendiness
Contribute
Yes, please contribute. But not to "add new awesome feature X", and stretch and extend the poor code until it's all bent out of shape and has a breakdown trying to people please everybody all the time.
Instead, submit thoughtful issues and PRs that aim to improve the code quality, simplicity, portability, developer experience, performance, and so on. Funnily enough, there's a lot of work there that can make a big impact. :smiley:
When evaluating issues/features/fixes, consider the following in order of importance:
Does my issue/PR increase:
- code quality
- simplicity
- portability
- dev experience
- performance
Get it? Just kidding. "How to" get it.
npm i --save bulgogi
In its entirety
const parser = new DOMParser();
const consistentFocus = imperfectlySaveAndRestoreFocus();
function toDOM(markup) {
return parser.parseFromString(markup, 'text/html');
}
export function update(view, state) {
consistentFocus.next();
const docEl = toDOM(view(state)).documentElement;
document.documentElement.replaceWith(docEl);
consistentFocus.next();
}
function *imperfectlySaveAndRestoreFocus() {
while(true) {
const active = document.activeElement;
let selectionStart,selectionEnd;
if ( active ) {
({selectionStart,selectionEnd} = active);
}
yield;
if ( active ) {
const newActive = document.querySelector(imperfectlyGetSelector(active));
if ( newActive ) {
newActive.focus();
try {
Object.assign(newActive,{selectionStart,selectionEnd});
} catch(e) {}
}
}
yield;
}
}
function imperfectlyGetSelector(el) {
// the first html to our selector does not help specificity
return `${
el.parentElement && el.parentElement.localName != 'html' ?
`${imperfectlyGetSelector(el.parentElement)} > ` : ''
}${
el.localName
}${
el.id ? `#${el.id.replace(/\./g, '\\\\.')}` : ''
}${
el.classList.length ? `.${[...el.classList].join('.')}` : ''
}${
el.name ? `[name="${el.name}"]` : ''
}`;
}
Docs update(view, state)
via example
import {update} from './b.js';
let counter = 0;
runTest();
setInterval(runTest, 2000);
function runTest() {
update(Test, {counter:counter++});
}
function Test(state) {
return `
<article class=excellent>
<h1>An Article Title</h1>
${Test2(state)}
</article>
`
}
function Test2({counter}) {
return `
<form method=GET action=/hello>
<input type=number name=xchakka value=${counter}>
<input type=text name=bigloo value=${counter}>
<button onclick=runFormTest(this,event,onclick,name,xchakka);>Do it</button>
</form>
`;
}
function runFormTest(...a) {
return (console.log(...a), a[1].preventDefault(), false)
};
self.runFormTest = runFormTest;
Benefits
- not a framework, just 1 function from a file with < 90 SLOC.
- write HTML with templates
- use inline (HTML attribute) event handlers functions
- use format
oneventname=myHandler(<...my args>)
instead ofoneventname=<...my code>
because you can inject special variables from the attribute handler scope as parameters to your function without having them pollute the scope of your function`- benefits include useful variables available in scope of the attribute value like:
- event: the Event that fired
- this: the event target the handler is attached to
<elprop>
: the property named byelprop
of the element its attached to, overrides<name>
: the form control named byname
of anyform
ancestor, overrides<docprop>
: the property named bydocprop
of any document ancestor
- details about which elements are reflected to the document or window, here and here
- benefits include useful variables available in scope of the attribute value like: