Quentin Hocdé

@quentinhocde

Who am I and introducing piecesjs

quentinhocde

Hello 👋 I am Quentin Hocdé, a freelance creative and front-end developer with over 10 years of experience. I mainly create animated and interactive websites. I am also ex-lead front-end at Locomotive.

I have always shared a lot about my workflow, my way of building websites, and I am one of the creators of locomotive-scroll.

For years, my front-end stack has been based on the boilerplate we created at Locomotive, using modularJS for component-style management of JavaScript.

With the desire to have a real approach based on web components, with dynamic imports of JS and CSS to improve code optimization and reduce page loading times, I decided to embark on creating a small front-end JavaScript framework.

piecesjs

piecesjs is born, with the idea of keeping the same way of working, tools, utility functions but written in a more modern way and adapted to new needs.

This project will evolve according to the projects I develop.

piecesjs

[alpha] - [1.1kb] - Front-end js framework, using native web components.

Main features

  • Dynamic JS & CSS import.
  • Scoped event manager.
  • Easy access to all elements inside a component with a this.$(yourQuery).
  • Easy communication between active components.
  • Common and global CSS import management.
  • A PiecesManager to access to all active components.

Installation

npm i piecesjs --save

Create your first component

With dynamic attributes (reactive)

<c-add class="c-add" value="0"></c-add>
import { default as Piece } from "piecesjs";

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

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

  unmount() {
    this.removeEvent("click", this.$button, 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-add", Add);

With static content

<c-header class="c-header">
  <h1>Hello world 🫶</h1>
</c-header>
import { default as 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(){}
mount(){}
update(){} //Called if an attribute is changed : unmount(), premount(), mount()
unmount(){}

Query with this.$

Shortcut to query an element

// return an array of elements
this.$('button');

Events

Register an event

/*
You can add an event in the mount().
The called function is automatically binded to this
params: (eventName, HTMLElement or array of HTMLElement, func [, parameters])
*/
mount() {
  this.addEvent('click', this.$button, this.click, {hello: 'world'});
}

Unregister the event

/*
You can remove the event listener in the unmount().
params: (eventName, HTMLElement or array of HTMLElement, func)
*/
unmount() {
  this.removeEvent('click', this.$button, this.click);
}

Call a function of any components, from any components

/*
params: (functionName, args, pieceName [,pieceId])
*/
this.call("increment", {}, "Add", "myAddComponentId");

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>

PiecesManager

Used to manage all active components. To access to the current components active in the page:

import { piecesManager } from "piecesjs";
console.log(piecesManager.currentPieces);

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

/*

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

Utils

Logs

You can log the lifecycle of your component with an attribute log

<c-header log>Hello</c-header>