This is a primitive and simple component-based declarative rendering framework.
- Simplicity and primitiveness
- Declarative rendering
- Flexibility, and Object-Oriented Architecture
To get started with Signature, simply run this command:
npm create signature@latest
You will be offered several project templates (almost all templates use Vite). After creating the project, follow the instructions to install dependencies and run:
cd <your-project-name>
npm install
npm run dev
Signature uses four classes as its basis:
- Signature - which processes the entire page and components.
- Component - which defines a standard structure for any element.
- Prop - which defines properties for components.
- Library - which defines a set of components.
In short, Signature allows you to create a website using simple and straightforward components that are easy to customize and extend.
Briefly about Signature
The signature is the main class that is responsible for processing the entire page. It is used to add components, register libraries, and essentially render the page.
// Creating a Signature instance
import {Signature} from 'web-signature';
const si = new Signature();
To add a component, use the
Signature.add(component: ComponentConstructor, name?: string)
method, which takes
the
component class and its name. For example:
import {Signature, Component} from 'web-signature';
class MyComponent extends Component {
/* ... */
}
const si = new Signature();
si.add(MyComponent, 'my-component');
// The <my-component> tag will be processed by this class.
Although the component name is not required, it is recommended to specify it to avoid problems when building the project.
To register a library, the
Signature.register(library: Library)
method is used, which takes an
instance of the Library.
For example:
import {Signature} from 'web-signature';
import {MyLibrary} from './my-library';
const si = new Signature();
si.register(new MyLibrary());
For rendering the page there is a
Signature.contact(selector: string, callback?)
method. For example:
import {Signature} from 'web-signature';
const si = new Signature();
si.contact('#app', () => {
console.log('Page rendered successfully!');
});
About Component
This is an abstract class from which all components inherit. It defines a standard structure for any element.
It has a main method
Component.render()
, and
component lifecycle hooks such
as
Component.onMount()
.
To create a component, you need to inherit the
Component
class and implement the
Component.render(): string
method
and the
name
field.
Let's create a simple component that simply displays text:
import {Component, html} from 'web-signature';
class MyComponent extends Component {
name = 'my-component';
render(): string {
return html`<div>Hello, World!</div>`;
}
}
<div id="app">
<my-component></my-component>
</div>
Now let's make a counter component that will increment the value when clicked. To do this,
we will need to update it somehow and track events.
For a component to be able to update, it must have its own ref. Ref - allows you to associate a
component instance with
its DOM
element, if the ref is not specified, the Signature
will not
remember the component instance.
And to track events, we will use the
Component.onMount(el: Element)
hook,
which is
called after the component has been
added to
the page.
import {Signature, Component, html} from 'web-signature';
export class Counter extends Component {
name = "counter";
count = 0;
render(): string {
// Rendering the component as a button with the current count
return html`<button type="button">Count is ${this.count}</button>`;
}
onMount(el: Element): void {
el.addEventListener('click', () => {
// Since a ref is required to update a component, we check if it is defined.
if (this.ref === undefined) {
throw Error();
}
this.count++;
// Update the component
this.ref.update();
});
}
}
const si = new Signature();
si.add(Counter, "Counter");
si.contact("#app");
The HTML should look like this:
<div id="app">
<counter ref></counter>
</div>
It is not necessary to specify a specific ref, the signature will generate it itself, but if you want the ref to have a specific name, you can specify it in the attribute. You can also set the component options to auto-generate the ref if it was not specified.
// ...
class Counter extends Component {
options = {
generateRefIfNotSpecified: true
}
// ...
}
// ...
Let's make it possible to set the number of steps to increment the counter,
using Props. We need to create
a Prop step
with type number
.
import {Signature, Component, Prop, html} from 'web-signature';
export class StepCounter extends Component {
name = "step-counter";
count = 0;
// In this field, we create a Prop step with type number
props = {
// We have specified that this Prop is required and must be a number greater than or equal to 1.
step: new Prop("number", true, (val) => Number(val) >= 1)
}
render(): string {
// Rendering the component as a button with the current count
return html`<button type="button">Count is ${this.count}</button>`;
}
onMount(el: Element): void {
el.addEventListener('click', () => {
// Since a ref is required to update a component, we check if it is defined.
if (this.ref === undefined) {
throw Error();
}
//After processing Props, the specified values are written to this.data
this.count += this.data.step as number;
// Update the component
this.ref.update();
});
}
}
const si = new Signature();
si.add(StepCounter, "Step-counter");
si.contact("#app");
The HTML should look like this:
<div id="app">
<Step-counter ref step="2"></Step-counter>
</div>
Now, when you click the button, the counter will increase by the specified number of steps (in our case, it is 2 steps).
The Library class allows you to create a set of components that can be used in a project. It has no other purpose than to facilitate the registration of components in Signature, and avoiding component name collisions by grouping them.
Let's create a library that will contain our Counter
and
StepCounter
components:
import {Library} from 'web-signature';
// Importing the components
import {Counter} from './Counter';
import {StepCounter} from './StepCounter';
// Creating a library instance
const myLibrary = new Library('ML');
// Adding components to the library
myLibrary.add(Counter, "Counter");
myLibrary.add(StepCounter, "Step-counter");
export default myLibrary;
To register the library
in Signature,
use the
Signature.register(library: Library)
method:
import {Signature} from 'web-signature';
// Importing the library
import myLibrary from './myLibrary';
const si = new Signature();
// Registering the library
si.register(myLibrary);
si.contact('#app');
Now you can use the components from the library in your
HTML, but
all components will be available
with
the prefix
ML-
, i.e. ML-counter
and ML-step-counter
, the pattern is the library name
is used as a prefix for the component name
[libName]-[componentName]
.
<div id="app">
<ML-counter ref></ML-counter>
<ML-step-counter ref step="2"></ML-step-counter>
</div>