turbopug
v1.0.5
Published
No-junk JS component library with insignificant weight. All in one mush: Web-Components, Localization, Routing, Reactive binding, Debounce, Unique IDs, PSW hashing
Downloads
9
Maintainers
Readme
TURBOPUG
No-junk JS component library with insignificant weight. If you're 17.5% too weak or 11.4% too unwilling to use heavy-weight component frameworks, but still want all the stuff in one mush:
- Web-Components
- Localization
- Templates
- Routing
- Reactive binding
- Debounce
- Unique IDs
- PSW hashing
Plus 133.7% of all the quickility.
KEEPING IT SIMPLE SINCE 1903
Handmade master craftsmanship gives you the full-bodied low-fat experience you expect from a component framework:
- No inheritance
- No bundler
- No build-step
- No TypeScript
- No junk
All the folding chair comfort you ever hoped for.
REQUIREMENTS
Because TURBOPUG
is so bare bones, your customer's browser almost needs to be from the JavaScript-future:
- modules & imports
- template literals
- Web Components
- All the words:
let
,const
,...
Yes: That's all the browsers, since a couple of years.
GET OVER YOUR SPEED-ANGST
Running in an up-to-date browsers your SPA doesn't need all the vanity junk. Just use a server that doesn't crack on HTML/2 push and gzipped response compression.
Real quickility comes with not doing stuff. And thats what TURBOPUG
does: not doing stuff. If you want wing chair comfort and a nurse giving you a hand, don't use TURBOPUG
. If you know your JavaScript, get cracking.
FRIENDLY TO CARBONS OUT OF THE BOX
TURBOPUG
is hertz-saving for computers. So somewhere someone doesn't need to burn fatty non-renewables. But keep in mind: what you do is up to you: Run your stuff on a Windows-11-VM through 5 VPNs and 6 SSH-tunnels if you like to roll like a coaler.
Docs
TURBOPUG
happened while writing an SPA in vanilla JavaScript in a reactive, event-driven design by factoring out the accidental complexity.
Component Example
import Comp from '../turbo/comp.js';
import Store from '../turbo/store.js';
export class MyCounter extends Comp {
#store;
static get observedAttributes() {
return ['label'];
}
constructor() {
super();
this.#store = new Store({
label: (value = '', event) => {
switch (event.type) {
case 'set':
return event.value;
default:
return value;
}
},
counter: (value = 0, event) => {
switch (event) {
case 'incremented':
return value + 1;
case 'decremented':
return value - 1;
default:
return value;
}
},
});
this.#store.merge({
label: null,
counter: 1,
});
}
attributeChangedCallback(name, _, value) {
switch (name) {
case 'label':
this.#store.send.label({type: 'set', value});
break;
default:
break;
}
}
get label() {
return this.#store.state.label;
}
set label(value) {
this.#store.send.label({type: 'set', value});
}
render() {
return /*html*/`
<div>
<span class="label">${this.#store.state.label}</span>
<span class="counter">${this.#store.state.counter}</span>
</div>
<button class="decrement">Decrement</button>
<button class="increment">Increment</button>
`;
}
bind(valElem, decBtn, incBtn) {
const labelElem = valElem.querySelector('.label');
this.#store.on.label((_, value) => {
labelElem.innerText = value;
});
const counterElem = valElem.querySelector('.counter');
this.#store.on.counter((_, value) => {
counterElem.innerText = value;
});
decBtn.addEventListener('click', () => {
this.#store.send.counter('decremented');
});
incBtn.addEventListener('click', () => {
this.#store.send.counter('incremented');
});
}
}
customElements.define('my-counter', MyCounter);
- An
TURBOPUG
component is defined by extending the class Comp. - For state management a Store is initialized and assigned onto a private variable
#store
. Stores are inspired by redux und made of variables that can be changed by sending events and reacting to them via reducers. - The 2 methods
static get observedAttributes()
andattributeChangedCallback(name, _, value)
are part of the web-components specification. - A component can have a render-method which must return a rendered HTML-string. JS template strings are used for that.
- After such a HTML-string is turned into DOM elements the bind-method is called. In which event-listeners from the DOM are attached and changes coming from the store are inserted into the DOM.
Components are state machines
How to program with TURBOPUG
An TURBOPUG
comp is a state machine. Programming is done via variable changes over time. I.e. setting one variable through user-input, reacting to a change, setting another variable and reacting to the change, until one change is reflected in HTML via a binding or triggers an outside variable-change-handler.
Localization/i18n bindings
lcz-prop
and lcz-attr
are used to bind to localization keys from the translations file. Syntax is:
[<attrib-or-prop-name>=]<LOCALIZATION_KEY>
Example:
<p lcz-prop="textContent=LOCALIZABLE_KEY:Fallback Translation"></p>
<input lcz-attr="value=LOCALIZABLE_KEY:Fallback Translation" type="text">
The name of the prop or attrib to set the localization on. Can be omitted, defaults to textContent
.
<LOCALIZATION_KEY>
The localization key to use to retrieve the value from the translations file. Fallback is value of <attrib-or-prop-name>
if not found, default value textContent
included.
Localization/i18n setup
insig
must rely on script loading order to make translations available ASAP during page-loading. Translations file and LCZ-module must be setup as follows:
<!doctype html>
<html lang="en">
<head>
<script src="/translations.js"></script> <!-- TRANSLATIONS FILE IS FIRST JS-FILE LOADED -->
<!-- OTHER SCRIPT AFTERWARDS ... -->
</head>
<body>
<!-- CONTENT COMES HERE -->
<script src="/insig/lcz.js" type="module"></script> <!-- LCZ MODULE LOADED DIRECTLY AFTER CONTENT -->
</body>
</html>
The localizations file must set a the property localizations
on the window object. If you only need one central localizations file, use the simple object notation in which each language locale is a key, i.e. de
for german or "de-CH"
for swiss german:
window.localizations = {
de: {
DOGS: 'Hunde',
CATS: 'Katzen',
DOG: 'Hund',
CAT: 'Katze',
DONE: 'Fertig'
}
};
If you want to use several localization files, use the array notation in which each entry is one of the above. Use the following terse notation per file to automatically extend a previously loaded definition:
window.localizations = (l => [...l, {
de: {
DOGS: 'Hunde',
CATS: 'Katzen',
DOG: 'Hund',
CAT: 'Katze',
DONE: 'Fertig'
}
}])(window.localizations || []);
The actual translations objects are merged in this case. The order in which the files are loaded in the HTML is significant: Files loaded later overwrite translation-keys from a file loaded earlier, so you can extend on base localizations.