0.1.0 • Published 5 months ago

@ryanmorr/elemental v0.1.0

Weekly downloads
-
License
Unlicense
Repository
github
Last release
5 months ago

elemental

Version Badge License Build Status

A functional approach to creating autonomous custom elements

Install

Download the CJS, ESM, UMD versions or install via NPM:

npm install @ryanmorr/elemental

Usage

Define an autonomous custom element by providing the tag name and an initialization function:

import elemental from '@ryanmorr/elemental';

// Define a custom element and return the class
const CustomElement = elemental('custom-element', (element) => {
    // Return an HTML string or DOM node to set the shadow root content
    return '<div>Hello World</div>';
});

// Create an element instance
const element = document.createElement('custom-element');

// Initialization function is called when the element is mounted to the DOM
document.body.appendChild(element);

Optionally define default properties:

elemental('custom-element', {msg: 'World'}, (element) => {
    // Default properties are added to the element instance on initialization
    return `Hello ${element.msg}`;
});

Default properties with a primitive value (string/number/boolean/null) are automatically reflected into attributes on initialization and observed for changes. After initialization, attribute changes are reflected into properties, but property changes are not reflected into attributes:

elemental('custom-element', {foo: 'abc', bar: 123, baz: {}}, (element) => {
    // Primitive default properties are reflected to attributes on initialization
    element.getAttribute('foo'); //=> "abc"
    element.getAttribute('bar'); //=> "123"
    element.getAttribute('baz'); //=> null

    // A property assignment is not reflected into an attribute
    element.foo = 'xyz';
    element.getAttribute('foo'); //=> "abc"

    // Setting an attribute will be reflected into a property 
    // and convert the value to its natural type
    element.setAttribute('bar', '789');
    element.bar; //=> 789 (number not string)
});

Subscribe to DOM state changes via the observe method:

elemental('custom-element', {foo: 'abc', bar: 123}, (element) => {
    // Returns a function to stop future calls
    const stop = element.observe('mount', (parentElement) => {
        // Called everytime the element is mounted to the DOM
    });

    element.observe('unmount', () => {
        // Called everytime the element is unmounted from the DOM
    });

    element.observe('prop', (name, newVal, oldVal) => {
        // Called everytime a default property changes
    });

    element.observe('prop:foo', (newVal, oldVal) => {
        // Called everytime the "foo" property changes
    });

    element.observe('attr', (name, newVal, oldVal) => {
        // Called everytime a reflected attribute changes
    });

    element.observe('attr:bar', (newVal, oldVal) => {
        // Called everytime the "bar" attribute changes
    });
});

Use the html property to get and set the shadow root:

elemental('custom-element', (element) => {
    // Supports HTML strings
    element.html = '<div></div>';
    element.html.innerHTML; //=> "<div></div>"

    // Supports DOM nodes
    element.html = document.createElement('span');
    element.html.innerHTML; //=> "<span></span>"
});

Add scoped CSS to the custom element via the css property. It supports CSS strings, <style> elements, CSSStyleSheet instances, or an array of any of those types. Each assignment to the css property appends to the CSS and does not replace it:

elemental('custom-element', (element) => {
    // Add CSS via string
    element.css = `
        .foo {
            color: red;
        }
    `;

    // Appends <style> element to CSS
    const style = document.createElement('style');
    style.textContent = `
        .bar {
            color: blue;
        }
    `;
    element.css = style;

    // Optionally define CSS within the shadow root using <style> and/or <link> elements
    return `
        <link rel="stylesheet" href="custom-element.css">
        <style>
        .baz {
            color: green;
        }
        </style>
        <div class="foo"></div>
        <div class="bar"></div>
        <div class="baz"></div>
    `;
});

License

This project is dedicated to the public domain as described by the Unlicense.