@royalsaltmerchant/salt-lib
v0.1.2
Published
salt-lib: a simple dynamic frontend rendering library.
Downloads
51
Maintainers
Readme
salt-lib
Simple dynamic frontend rendering library.
Install
npm install @royalsaltmerchant/salt-libExports
import { Component, createElement } from "@royalsaltmerchant/salt-lib";Core Model
Componentowns onedomElemroot.render()always clears first, then appends returned output.render()can be async.- If multiple async renders overlap, stale older renders are dropped.
- Compose nested classes with
useChild(key, factory, update?)orchildElem(key, factory, update?).
Component API
Constructor
new Component({
domElem, // optional mount element
tagName, // optional root tag if domElem not provided (default: "div")
className, // optional class for root
state, // optional initial state object
autoInit, // default: true
autoRender, // default: true
})Mount rules:
- If
domElemis provided, it is used. - If not, a detached root is created.
salt-libdoes not auto-query#app.
Lifecycle rules:
autoInitdefaults totrue.autoRenderdefaults totrue.- If a render already started before constructor auto-render runs (manual constructor render or
init()render), auto-render is skipped to avoid duplicate first paint.
Methods
async init()async render()useMemo(key, factoryOrValue, depsOrFactory = [])clearMemo(key?)useChild(key, factory, update?)async childElem(key, factory, update?)dropChild(key, { destroy = true })clearChildren({ destroy = true })async setState(nextStateOrUpdater, { replace = false, render = true })getState()onCleanup(fn)offCleanup(fn)runCleanup()clear({ deep = false })destroy({ clear = true, clearMemo = true, destroyChildren = true })
useChild / childElem Pattern
useChild caches one child instance per key.
childElem is shorthand for: get cached child -> run child render -> return child.domElem.
import { Component, createElement } from "@royalsaltmerchant/salt-lib";
class CounterCard extends Component {
render = async () => {
return [
createElement("article", { className: "card" }, [
createElement("h2", {}, this.props.title),
createElement("div", {}, String(this.props.count)),
]),
];
};
}
class App extends Component {
state = { count: 0, title: "Counter" };
increment = async () => {
await this.setState((prev) => ({ count: prev.count + 1 }));
};
renderCounterCard = async () => {
return this.childElem(
"counter-card",
() =>
new CounterCard({
domElem: createElement("div"),
autoInit: false,
autoRender: false,
title: this.state.title,
count: this.state.count,
}),
(child) => {
child.props.title = this.state.title;
child.props.count = this.state.count;
},
);
};
render = async () => {
const cardElem = await this.renderCounterCard();
return [
createElement("h1", {}, "salt-lib"),
createElement(
"button",
{ type: "button" },
"+1",
{ type: "click", event: this.increment },
),
cardElem,
];
};
}
const root = document.getElementById("app");
if (!root) throw new Error("Missing #app");
new App({ domElem: root });createElement API
createElement(tag, attributes, children, eventListeners, options)Attributes
classNamemaps toclass.stylesupports object syntax.datasetsupports object syntax.- Boolean attrs follow HTML boolean semantics.
Children
- Supports single child or nested arrays.
- Supports
Node, string, number, and other primitives. null,undefined, andfalseare skipped.
Event listeners
- Accepts one listener object or an array.
{ type: "click", event: handler, options: { once: true } }Raw HTML (opt-in)
createElement("div", {}, "<b>trusted html</b>", null, { allowHtml: true });Render Output Types
A render() return value can include:
- DOM nodes
- nested arrays of nodes
- strings / numbers
- component instances (their
domElemis appended)
Example App In Repo
Files:
examples/memo-child-state/index.htmlexamples/memo-child-state/main.jsexamples/memo-child-state/style.css
Run:
python3 -m http.server 4173Open:
http://localhost:4173/examples/memo-child-state/Publish Checklist
- Set package metadata in
package.json:repositorybugshomepage
- Run local validation:
npm test
npm run pack:check- Publish:
npm login
npm publish