All Projects → lastmjs → functional-element

lastmjs / functional-element

Licence: MIT license
Functional custom elements

Programming Languages

javascript
184084 projects - #8 most used programming language
typescript
32286 projects
HTML
75241 projects

npm version dependency Status devDependency Status

functional-element

functional-element exposes the custom element API in a functional manner. It allows you to express your custom element's behavior as a function. The custom element lifecycle is exposed through parameters to your function. You simply return a template or props as needed. Templating is currently handled by lit-html. Hook up event listeners with simple functions. No more classes, methods, or inheritance.

Live demo

Installation

npm install functional-element

Use

functional-element produces bonafide custom elements. Use them as follows:

<!DOCTYPE html>

<html>
    <head>
        <script type="module" src="example-element.js"></script>
    </head>

    <body>
        <example-element></example-element>
    </body>
</html>

Create them as follows:

import { html, customElement } from 'functional-element';

customElement('example-element', ({ constructing, hello }) => {
    if (constructing) {
        return {
            hello: 'world!'
        };
    }

    return html`
        <div>${hello}</div>
    `;
});

Lifecycle

import { html, customElement } from 'functional-element';

customElement('example-element', ({ constructing, connecting, disconnecting, adopting }) => {
    if (constructing) {
        console.log(`We're in the constructor!`);
    }

    if (connecting) {
        console.log(`We're in the connectedCallback!`);
    }

    if (disconnecting) {
        console.log(`We're in the disconnectedCallback!`);
    }

    if (adopting) {
        console.log(`We're in the adopted callback!`);
    }

    return html`
        <div>It's the cycle of life!</div>
    `;
});

Properties

import { html, customElement } from 'functional-element';

customElement('example-element', ({ constructing, regularProp, computedProp }) => {
    if (constructing) {
        return {
            regularProp: `Just your average property`,
            computedProp: () => {
                return `This property was made by a function`;
            }
        };
    }

    return html`
        regularProp: <div>${regularProp}</div>
        computedProp: <div>${computedProp()}</div>
    `;
});

Listening to events

import { html, customElement } from 'functional-element';

customElement('example-element', ({ constructing, update, count }) => {
    if (constructing) {
        return {
            count: 0
        };
    }

    return html`
        <button @click=${() => update({ count: count + 1 })}>${count}</button>
    `;
});

Dispatching events

import { html, customElement } from 'functional-element';

customElement('example-element', ({ constructing, element, count }) => {
    if (constructing) {
        return {
            count: 0
        };
    }

    return html`
        <button @click=${() => increment(element, count)}>${count}</button>
    `;
});

function increment(element, count) {
    element.dispatch(new CustomEvent('increment', {
        detail: {
            count: count + 1
        }
    }));
}

Async

import { html, customElement } from 'functional-element';

customElement('example-element', async ({ constructing, update, count }) => {
    if (constructing) {
        return {
            count: 0
        };
    }

    const newCount = await increment(count);

    return html`
        count: ${count}
    `;
});

async function increment(count) {
    await wait(5000);
    return count + 1;
}

async function wait(time) {
    return new Promise((resolve) => setTimeout(resolve, time));
}
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].