0.2.3 • Published 8 months ago

piecesjs v0.2.3

Weekly downloads
-
License
MIT
Repository
-
Last release
8 months ago

Introduction

piecesjs is a lightweight JavaScript framework built upon native web components, offering a suite of tools and utilities tailored for creative websites.


At its core, a “Piece” is a modular component that can live anywhere on your webpage. Each Piece operates independently, with its own encapsulated styles and interactions, making it easy to manage and reuse across your site.

Piecesjs dynamically imports only the necessary JavaScript and CSS for each page, optimizing performance while maintaining flexibility. Unlike larger frameworks, it allows you to build exactly what you need, free from the overhead of unnecessary code or restrictive architectures.

Designed for creative websites that rely heavily on JavaScript logic—handling multiple steps, states, and events—piecesjs offers a streamlined and scalable approach for developers looking to create highly interactive experiences.

Compiled with vitejs.

Main features

  • Dynamic JS & CSS Import: Automatically loads only the necessary JavaScript and CSS for each page, improving performance.
  • Scoped Event Management: Easily manage events within a specific component’s scope using this.on() and this.off() methods.
  • Convenient Access to Scoped HTMLElements: Quickly access elements within the component using this.$() or this.domAttr('slug').
  • Seamless Communication Between Active Components: Components can communicate effortlessly with each other using this.call() or this.emit().
  • Efficient Global CSS Management: Streamlined handling of global CSS imports to keep your styles organized.
  • PiecesManager: Provides centralized access to all active pieces, simplifying component management.

Nav

Installation

npm i piecesjs --save

Create your first Piece

With dynamic attributes (reactive)

<c-counter class="c-counter" value="0"></c-counter>
import { Piece } from 'piecesjs';

export class Counter extends Piece {
  constructor() {
    super('Counter', {
      stylesheets: [() => import('/assets/css/components/counter.css')],
    });
  }

  mount() {
    this.$button = this.$('button')[0];
    this.on('click', this.$button, this.click);
  }

  unmount() {
    this.off('click', this.$button[0], this.click);
  }

  render() {
    return `
      <h2>${this.name} component</h2>
      <p>Value: ${this.value}</p>
      <button class="c-button">Increment</button>
    `;
  }

  click() {
    this.value = parseInt(this.value) + 1;
  }

  set value(value) {
    return this.setAttribute('value', value);
  }

  get value() {
    return this.getAttribute('value');
  }

  // Important to automatically call the update function if attribute is changing
  static get observedAttributes() {
    return ['value'];
  }
}

// Register the custom element
customElements.define('c-counter', Counter);

With static content

<c-header class="c-header">
  <h1>Hello world</h1>
</c-header>
import { Piece } from 'piecesjs';

class Header extends Piece {
  constructor() {
    // Set the name of your component and stylesheets directly with the super();
    super('Header', {
      stylesheets: [() => import('/assets/css/components/header.css')],
    });
  }
}
// Register the custom element
customElements.define('c-header', Header);

Register and load dynamically your component

import { load } from 'piecesjs';

load('c-button', () => import('/assets/js/components/Button.js'));

Lifecycle

premount(firstHit = true){}
render(){} // if you want to do a Javascript rendering
mount(firstHit = true){} // firstHit parameter is set to false if the function is called after an update or if its content is changed.
update(){} //Called if an attribute is changed. Then it will call unmount(), premount() and mount().
unmount(update = false){} // update = true if this unmount() is called after an attribute is changed.

Query with this.$

Shortcut to query an element. this.dom(query, context) is also available.

/**
 * @param { String } query
 * @param { HTMLElement } context (this by default)
 */
this.$('button'); // return a NodeList

Get an element with a slug

<ul>
  <li data-dom="listItem">Item 1</li>
  <li data-dom="listItem">Item 2</li>
</ul>
/**
 * @param { String } slug
 * @param { HTMLElement } context (this by default)
 */
this.domAttr('listItem'); // return a NodeList

Events

Register an event listener with this.on()

/*
* Tips: call event listeners in the mount(), register event for an HTMLElement or an array of HTMLElements
* The called func is automatically binded to this
* @param { String } type
* @param { HTMLElement or HTMLElement[] } el
* @param { function } func
* @param { Object } params
*/
mount() {
  this.on('click', this.$button, this.click, {hello: 'world'});

  // You can also use this.on() to add an event listener on global elements
  // this.on('resize', window, this.resize);
}

// if you have set params, the eventObject will be available after
click(params, event) {}

Unregister an event listener with this.off()

/**
 * Tips: remove event listeners in the unmount(), unegister event for an HTMLElement or an array of HTMLElements
 * @param { String } type
 * @param { HTMLElement } el
 * @param { function } func
 */
unmount() {
  this.off('click', this.$button, this.click);
}

Communication between components

this.call()

Call a function of any components, from any components

/**
 * Call function of a component, from a component
 * @param { String } func
 * @param { Object } args
 * @param { String } pieceName
 * @param { String } pieceId
 */
this.call('increment', {}, 'Counter', 'myCounterComponentId');

If no pieceId are specified, all occurrences of the component will be called. A pieceId can be set directly with an attribute cid

<c-button cid="myButtonUId"></c-button>

this.emit() and custom events

You can also emit a custom event with this.emit()

/**
 * Emit a custom event
 * @param { String } eventName
 * @param { HTMLElement } el, by default the event is emit on document
 * @param { Object } params
 */
this.emit('buttonIsMounted', document, { value: 'A Button is mounted!' });

Then, in a Piece you can use this.on(), like the default events.

mount() {
  this.on('buttonIsMounted', document, this.customEventTrigger);
}

// You can get parameters with event.detail
customEventTrigger(event) {
  console.log(event.detail); // { value: 'A Button is mounted! }
}

unmount() {
  this.off('buttonIsMounted', document, this.customEventTrigger);
}

PiecesManager

PiecesManager manage all active components. Get access of all current components visible in the page:

// From anywhere
import { piecesManager } from 'piecesjs';
console.log(piecesManager.currentPieces);

// In a Piece
console.log(this.piecesManager);

class Header extends Piece {
  mount() {
    console.log(this.piecesManager.currentPieces);
  }
}

/*
{
  Counter: {
    c0: {
      name: 'Counter',
      id: 'c0',
      piece: HTMLElement
    },
    myCounterComponentId: {
      name: 'Counter',
      id: 'myCounterComponentId',
      piece: HTMLElement
    }
  }, 
  Button: {
    c2: {
      name: 'Button',
      id: 'c2',
      piece: HTMLElement
    }
  }, 
  Header: {
    c1: {
      name: 'Header',
      id: 'c1',
      piece: HTMLElement
    }
  }
}
*/

Memo

HTML attributes

AttributeDescription
logYou can log the lifecycle of your Piece with an attribute <c-header log>Hello</c-header>
cidTo override the generated id of your Piece. Usefull to communicate with this specific Piece

Piece instance props

AttributeDescription
this.cidA generated id of the Piece, override if you set a cid attribute
this.nameReturn the name of the Piece

Piece instance methods

MethodDescriptionArguments
this.$Query an HTMLElementquery: String context: HTMLElement, this by default
this.domthis.$ clonequery: String context: HTMLElement, this by default
this.domAttrQuery with a slug. If you have an element like <li> data-dom="mySlug"></li>slug: String

Piece events methods

MethodDescriptionArguments
this.onEvent listener for scoped and binded (this) eventstype: String (example: mouseenter, resize..)el: HTMLElement or HTMLElement[]func: Functionparams: {} (optional)
this.offRemove event listener, the better way is to put it in the unmount().type: String (example: mouseenter, resize..)el: HTMLElement or HTMLElement[]func: Function
this.callCall of a function of a Piece or a specific Piece based on its cidfunc: String, the function nameargs: ObjectpieceName: StringpieceId: String (optional), linked to a cid attribute
this.emitEmit a custom event. Can be listened by the other Pieces with a this.on()eventName: Stringel: HTMLElement, document by default (optional)params: Object (optional)

You want to collaborate ?

Clone the repo and at the root /

npm i

Link your local piecesjs to use it as an npm package

npm link piecesjs

Build piecesjs

npm run build

Test environment : In the folder /test

npm i
npm run dev

Enjoy and feel free to create a pull request!

Support

If you want to support me, and follow the journey of the creation of pieces 👀

Support me on github

0.2.3

8 months ago

0.2.1

10 months ago

0.2.2

10 months ago

0.2.0

1 year ago

0.1.15

1 year ago

0.1.16

1 year ago

0.1.14

1 year ago

0.1.12

1 year ago

0.1.13

1 year ago

0.1.11

1 year ago

0.1.10

1 year ago

0.1.9

1 year ago

0.1.8

1 year ago

0.1.7

1 year ago

0.1.6

1 year ago

0.1.4

1 year ago

0.1.5

1 year ago

0.1.3

1 year ago

0.1.0

1 year ago

0.1.2

1 year ago

0.1.1

1 year ago

0.0.37

1 year ago

0.0.35

1 year ago

0.0.36

1 year ago

0.0.25

1 year ago

0.0.30

1 year ago

0.0.31

1 year ago

0.0.32

1 year ago

0.0.33

1 year ago

0.0.34

1 year ago

0.0.26

1 year ago

0.0.27

1 year ago

0.0.29

1 year ago

0.0.21

1 year ago

0.0.22

1 year ago

0.0.23

1 year ago

0.0.24

1 year ago

0.0.20

1 year ago

0.0.10

1 year ago

0.0.11

1 year ago

0.0.13

1 year ago

0.0.14

1 year ago

0.0.15

1 year ago

0.0.9

1 year ago

0.0.16

1 year ago

0.0.17

1 year ago

0.0.18

1 year ago

0.0.19

1 year ago

0.0.8

1 year ago

0.0.7

1 year ago

0.0.6

1 year ago

0.0.5

1 year ago

0.0.4

1 year ago

0.0.3

1 year ago

0.0.2

1 year ago