Skip to content

Pinbib/Signature

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

60 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Signature

GitHub package.json versionNPM Version

This is a primitive and simple component-based declarative rendering framework.

Features

  • Simplicity and primitiveness
  • Declarative rendering
  • Flexibility, and Object-Oriented Architecture

Quick Start

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

Introduction

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).

Quick about the Library

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>

Releases

No releases published