BemtvJS:
Template-based reactivity,
Brackethtml,
CSS-In-Template,
SuperComponent,
CSS-In-JS,
Hooks,
DOM events,
CompVars,
Transformation functions,
Automatic routing,
and Code-Splitting.
BemtvJS is a UI library that takes an approach contrary to most libraries and frameworks. Usually UI tools observe the state (variables and properties) and when it changes it updates the template and applies the result to the DOM, Bemtv makes the template itself check if the state has changed and update itself.
Brackethtml
Brackethtml is a markup language built into Bemtv that allows you to reduce HTML redundancy, support it and look like it:
const btn = `button[Click me!]`;
Brackethtml also allows insertion of CSS-In-Template, which is simply CSS-In-JS inserted directly into the template:
const btn = `button[color:blue; ~ Click me!]`;
SuperComponent
Creating a component:
import { _ } from "bemtv";
_`App`();
The returned value is called a SuperComponent. Internally, each time the component is used, a lightweight instance is created that manages this new rendering of the component.
These instances assume “control” whenever an action occurs in them, where the reaction is the execution of a previously passed callback, normally in Hooks and DOM events.
CSS-In-JS
In addition to the style that can be applied directly to the template, a great option is to use the css() method:
import { _ } from "bemtv";
const { css, template } = _`App`();
css`
color: blue;
font-size: 20px;
`;
template`h1[Hello world!]`;
Hooks
Each component instance goes through a series of steps, we can run functions called hooks in each of these steps:
import { _ } from "bemtv";
const { onInit, onMount, onUpdate, onUnmount } = _`App`();
onInit(() => {
// Called(only once) when the instance is initialized.
});
onMount(() => {
// Called(only once) after the component template has been mounted in the DOM.
});
onUpdate(() => {
// Called after the template update is applied to the DOM.
});
onUnmount(() => {
// Called(only once) after the component is removed/unmounted from the template it was in.
});
DOM Events
We can use DOM events directly from SuperComponent:
import { _ } from "bemtv";
const { click$, mouseover$ } = _`App`();
click$(() => {}, { capture: true });
mouseover$(() => console.log("Hey!"));
CompVars
We can pass an object as an argument to the component, this object is called compVars
and all properties added to it are isolated to each rendering of the component, including data structures like Array, Set, Map and Object.
Knowing our “variables”, Bemtv manages to provide really sweet syntactic sugars.
An example is that we can add the path to the property directly in the template using the $
symbol:
import { _ } from "bemtv";
const { template, render } = _`Message`({ message: "Hey!" });
template`button[Clicked: $message ]`;
render();
If we have a property with the same name as an HTML attribute, we can use the @
symbol to tell Bemtv to treat the property as a key and value:
import { _ } from "bemtv";
const { template, render } = _`Img`({
src: "avatar.png",
alt: "My avatar",
});
template`img[ @src @alt ]`;
render();
Transformation Functions
The Transformation functions allow us to add a “mark” to a data structure and tell Bemtv to transform the data structure only when necessary, this allows us to focus only on the data structure.
import { tFn } from "bemtv";
const tListJoin = tFn((list) => list.join(" br[] "));
The above example creates a transform function that can be used with lists, and when the list is requested by the template it executes that function and adds the result to the template:
import { _, tFn} from "bemtv";
const tListJoin = tFn((list) => list.join(" br[] "));
const { template } = _`List`({
list: tListJoin(["user1", "user2", "user3"]),
});
template`div[Users: $list ]
Whenever this list is changed (eg $.list.push(item)
), Bemtv will detect and use the transform function again and render the change.
Automatic routing
Bemtv uses an innovative automatic route creation system, this is possible because the components can behave like routes/pages.
Bemtv is able to automatically figure out when a “normal” component should also be treated as a route:
A regular component:
import { _ } from "bemtv";
const { template } = _`AboutUs`();
template`We are cool!`;
The component responsible for rendering the App:
import { _ } from "bemtv";
const { template, render } = _`App`();
template`
Welcome! br[]br[]
#[] br[]br[]
#AboutUs[ Link to about us ]`;
render();
The second component uses the #[]
symbol, it is within it that the routes are rendered.
Note the #AboutUs[...]
, this is where the “magic” happens.
First, Bemtv will read the App
component template and find that the AboutUs
component
is also a route (thanks to the #
before it), and when the template is rendered, everything inside the #AboutUs[...]
component will be wrapped in an a
tag with the href
attribute pointing to the route.
The route address will be the component name in kebab-case: /about-us
.
When the user clicks on the link, the AboutUs
component will be rendered in #[]
.
Bemtv will also figure out that the component is a route whenever we access some method of the component that targets routes, even if it is not called (thanks to Proxies).
Code-Splitting
To automate the component import process, Bemtv offers the autoImportComponents() function which accepts an object where the properties name must be the components name and their values must be a function that imports the component using dynamic imports(dynamic import):
import { autoImportComponents } from "bemtv";
autoImportComponents({
Counter() {
import("./components/Counter");
},
});
Bemtv will import the component as soon as the component is used in a template, however, it will ignore the component until it is available.
Closure
These are not all the functionalities offered by Bemtv, for example, we are not talking about the innovative data sharing system between the components.
You can check everything in the project repository:
https://github.com/diogoneves07/bemtvjs.
Bemtv is a recent project, you are welcome to be part of the construction of this tool.
Any questions or if you want to contribute to the project, you can contact me or open a PR.
Thanks for getting this far!
Top comments (0)