Creating a library of web component with lit dev

Creating a library of web component with lit dev

Completing the first lit dev playground

Lit dev

"Lit is a simple library for building fast, lightweight web components.

At Lit's core is a boilerplate-killing component base class that provides reactive state, scoped styles, and a declarative template system that's tiny, fast and expressive."

What are lit web components?

Interoperable & future-ready

Every Lit component is a native web component, with the superpower of interoperability. Web components work anywhere you use HTML, with any framework or none at all. This makes Lit ideal for building shareable components, design systems, or maintainable, future-ready sites and apps.

Check: Lit dev docs

The first component is a timer useful to enter in the flow state and measure time.

Side note: While following the first tutorial you may encounter an incomplete code, click the three dots "..." in the code to expanse the full version.

This is my modified version for better display, and as a rule of thumb always write the full code, wheter for yourself or by writing the example in your editor.

Code - Timer

Start by installing with yarn or npm lit:

npm i lit

Timer:

import { LitElement, html, css } from "lit";
import {replay, pause, play} from "./icons.js"


export class MyTimer extends LitElement {
    static properties = {
        duration: {},
        end: {state: true},
        remaining: {state: true},
    }
    static styles = css`
    :host {
      display: inline-block;
      min-width: 4em;
      text-align: center;
      padding: 0.2em;
      margin: 0.2em 0.1em;
    }
    footer {
      user-select: none;
      font-size: 0.6em;
    }`;

    constructor() {
        super();
        this.duration = 60;
        this.end = null;
        this.remaining = 0;
    }
    start() {
        this.end = Date.now() + this.remaining;
        this.tick();
    }

    pause() {
        this.end = null;
    }

    reset() {
        const running = this.running;
        this.remaining = this.duration * 1000;
        this.end = running ? Date.now() + this.remaining : null;
    }

    tick() {
        if (this.running) {
            this.remaining = Math.max(0, this.end - Date.now());
            requestAnimationFrame(() => this.tick());
        }
    }

    get running() {
        return this.end && this.remaining;
    }

    connectedCallback() {
        super.connectedCallback();
        this.reset();
    }

    render() {
        const {remaining, running} = this;
        const min = Math.floor(remaining / 60000);
        const sec = Math.floor((remaining / 1000) % 60).toString().padStart(2, "0");
        const hun = Math.floor((remaining % 1000) / 10).toString().padStart(2, "0");
        return html`
        ${min ? `${min}:${sec}` : `${sec}.${hun}`}
        <footer>
        ${
          remaining === 0
            ? ''
            : running
            ? html`<span @click=${this.pause}>${pause}</span>`
            : html`<span @click=${this.start}>${play}</span>`
        }
        <span @click=${this.reset}>${replay}</span>
        </footer>
        `;
    }
}
customElements.define('my-timer', MyTimer);

HTML

<!doctype html>
<head><!-- playground-fold -->
  <link rel="preconnect" href="https://fonts.gstatic.com">
  <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@1,800&display=swap" rel="stylesheet">
  <script type="module" src="./my-timer.js"></script>
  <style>
    body {
      font-family: 'JetBrains Mono', monospace;
      font-size: 36px;
    }
  </style>
  <!-- playground-fold-end -->
</head>
<body>
  <my-timer duration="900"></my-timer>
  <my-timer duration="1800"></my-timer>
  <my-timer duration="3600"></my-timer>
  <my-timer duration="10800"></my-timer>
</body>

Timer icons

import {html} from 'lit';

export const replay = html<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><title>Replay</title><g><rect fill="none" height="24" width="24"/><rect fill="none" height="24" width="24"/><rect fill="none" height="24" width="24"/></g><g><g/><path d="M12,5V1L7,6l5,5V7c3.31,0,6,2.69,6,6s-2.69,6-6,6s-6-2.69-6-6H4c0,4.42,3.58,8,8,8s8-3.58,8-8S16.42,5,12,5z"/></g></svg>; 
export const pause = html<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><title>Pause</title><path d="M0 0h24v24H0V0z" fill="none"/><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>; 
export const play = html<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><title>Play</title><path d="M0 0h24v24H0V0z" fill="none"/><path d="M10 8.64L15.27 12 10 15.36V8.64M8 5v14l11-7L8 5z"/></svg>;

Note: If you try to use the html file directly in the browser you will find a cors olicy error.

To serve the component run the following commands:

npm i @web/dev-serve --save-dev

Your package json should look like this:

{
  "type": "module",
  "scripts": {
    "start": "web-dev-server"
  },
  "dependencies": {
    "lit": "^3.1.2"
  },
  "devDependencies": {
    "@web/dev-server": "^0.4.2"
  }
}

Write the next web-dev-server.config.js:

export default {
    open: true,
    watch: true,
    appIndex: 'index.html',
    nodeResolve: {
      exportConditions: ['development'],
    },
  };

Execute the next command:

npm run start

In localhost:8000 your timer will appear with this look

(Image)

Lit playground

For further exploration use the playground and go throught the different samples to build up your knowledge.