Say Goodbye to 80% of Your Frontend
Transform the DOM over-the-wire with a simple state machine and remove 80% of all that glue code that's making everything a sticky mess. No javascript, no libraries, no transpilers required.
~1.47kb!
Define your DOM transformations in a state machine
const fsm = new Domx({
initialState: "red",
listeners: [["#stoplight", "click", "changeState"]],
states: {
red: {
entry: [
["addClass", "#stoplight__red", "active"],
["removeClass", "#stoplight__green", "active"],
["dispatch", "changeState", 2000],
],
changeState: [["state", "yellow"]],
},
yellow: {
entry: [
["addClass", "#stoplight__yellow", "active"],
["removeClass", "#stoplight__red", "active"],
["dispatch", "changeState", 2000],
],
changeState: [["state", "green"]],
},
green: {
entry: [
["addClass", "#stoplight__green", "active"],
["removeClass", "#stoplight__yellow", "active"],
["dispatch", "changeState", 2000],
],
changeState: [["state", "red"]],
},
},
});
// Alternatively you can use the custom element
// <dom-x>...</dom-x> <== locally defined fsm
// <dom-x src="/my-fsm.json">...</dom-x> <== remotely defined fsm
Render the markup the state machine will operate on.
<div id="stoplight">
<div id="stoplight__red"></div>
<div id="stoplight__yellow"></div>
<div id="stoplight__green"></div>
</div>
Click the stoplight
In Short:
Behavior is local (no more following wires), the state is modeled (the only thing that's actually hard), less client/server coupling (it's all backend) and it's toolable (you can write your own transformers). And in the end, it's just a lot less code and complexity.
All The Things You Can Do
// Name: Append
// Desc: Append to element
// Type: [transformer: "append", selector: string, html: string];
["append", "#list", "<li>new item</li>"],
// Name: Attr
// Desc: Set an attribute
// Type: [transformer: "attr", selector: string, attr: string, value: string];
["attr", "#dialog", "open", "true"],
// Name: Add Event Listener
// Desc: Attach an event handler to trigger an action
// Type: [transformer: "addEventListener", selector: string, event: string, stateEvent:string];
["addEventListener", "#btn", "click", "changeState"],
// Name: Dispatch
// Desc: Dispatch an action
// Type: [transformer: "dispatch", action: string, delay?: number];
["dispatch", "reset", 2000],
// Name: Get
// Desc: Send a get request with the selected data to and endpoint and process the result (a transformation object)
// Type: [transformer: "get", url: string, ...data:
// | [key: string, selector: string, prop: "value"]
// | [key: string, selector: string, prop: "attribute", propKey: string]
// | [key: string, selector: string, prop: "dataset", propKey: string]]
// []];
["get", "/some/endpoint", [
["first-name", "#first-name-field", "value"],
]],
// Name: History
// Desc: Update the browser history
// Type: [transformer: "history", method: string, ...args: (string | number)[]];
["history", "pushState", {}, "Record List", "/records/list"],
// Name: Location
// Desc: Update the browser location
// Type: [transformer: "location", url:string];
["location", "/records/list"],
// Name: Post
// Desc: Post selected data to endpoint and process the result (a transformation object)
// Type: [transformer: "post", url: string, ...data:
// | [key: string, selector: string, prop: "value"]
// | [key: string, selector: string, prop: "attribute", propKey: string]
// | [key: string, selector: string, prop: "dataset", propKey: string]]
// []];
["post", "/some/endpoint", [
["first-name", "#first-name-field", "value"],
]],
// Name: Remove attribute
// Desc: Remove an attribute
// Type: [transformer: "removeAttribute", selector: string, attr: string];
["removeAttribute", "#dialog", "open"],
// Name: Remove class
// Desc: Remove a class
// Type: [transformer: "removeClass", selector: string, className: string];
["removeClass", "#dialog", "open"],
// Name: RemoveEventListener
// Desc: Remove an event listener
// Type: [transformer: "removeEventListener", selector: string, event: string, stateEvent: string];
["removeEventListener", "#btn", "click", "changeState"],
// Name: Replace
// Desc: Replace an element
// Type: [transformer: "replace", selector: string, html: string];
["replace", "#list", "<li>updated item</li>"],
// Name: SetAttribute
// Desc: Set an attribute
// Type: [transformer: "setAttribute", selector: string, attr: string, value: string];
["setAttribute", "#dialog", "open", "true"],
// Name: State
// Desc: Change current state
// Type: [transformer: "state", state: string];
["state", "processing"],
// Name: Submit
// Desc: Submit a form and process the result (a transformation object)
// Type: [transformer: "submit", formSelector: string];
["submit", "#form-id"],
// Name: Text content
// Desc: Set the text content of an element
// Type: [transformer: "textContent", selector: string, text: string];
["textContent", "#dialog", "Processing..."],
// Name: Wait
// Desc: Add a wait between transformations
// Type: [transformer: "wait", ms: number];
["wait", 2000],
// Name: Window
// Desc: Call a method on the window object
// Type: [transformer: "win", method: string, ...args: any[]];
["window", "alert", "Hello World!"],
Take a look at the "customTransformer" transformer
// first define the function
function addCustomTransformer(domxInstance: Domx, arg1: string, arg2: string) {
// ...do your worst...
}
// then register it to your domx instance
myfsm.addTransformer("customTransformer", addCustomTransformer);
// in your state machine you can now use it
["customTransformer", "argument one", "argument two"]
Couple domx with my other library, cap ui
cap ui is a set of cut and paste ui components. The state/behavior of the components are expressed via BEM syntax which means adding/removing classes pretty much controls everything. This makes domx and cap ui a match made in heaven.
©Copyright 2024 All rights reserved. Made in the USA 🇺🇸 by Kevin Lint as a product of Lint Trap Media.