npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

custom-web-component

v2.0.0

Published

Simple basis for creating standard custom web components with one little HTML rendering library, lit-html(render, html) should you want to use it!

Downloads

14

Readme

Custom Web Component

Forget angular, forget react, forget vue, lets start building raw custom web components!

using lit-html (render, html), an optional templating binder to offer super simple html templating and targetted updates to the dom with this.updateTemplate(), or simple embed your own raw HTML, your choice.

Look in the example folder for some basic concepts and use case, more docs to follow

DEVELOPMENT IN CHROME (native)

    <!-- Polyfill missing methods -->
    <script src="/node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js"></script>
    <!-- Native project entrypoint -->
    <script type="module" src="./index.mjs"></script>

PRODUCTION THROUGH BUILDS (compiled with babel)

    <!-- Polyfill missing methods -->
    <script src="/node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js"></script>
    <!-- Native project entrypoint -->
    <script type="module" src="./index.mjs"></script>
    <!-- Backup compiled endpoint for browsers that lack module loading -->
    <script nomodule src="./index.js"></script>

The above will give you modular JS loading for those browsers that support it and fallback to compiled build using babel should you wish to support IE11

YOUR ENTRY POINT

import './node_modules/reflect-constructor/reflect-constructor.js'; // IE11 Reflect polyfill
import './node_modules/@webcomponents/custom-elements/custom-elements.min.js'; // custom element class polyfill
import './src/HelloWorldComponent.js'; // your entry component

INSTALLATION

npm install reflect-constructor lit-html custom-web-component --save

BUILDING

// Typical Build script
var fs = require('fs');
var fsx = require('fs-extra');
var replace = require('replace-in-file');
var browserify = require("browserify");
var babelify = require("babelify");

/*************************************************/
/* Build into distributable, production versions */
/*************************************************/

// CUSTOM WEB COMPONENT -- BUILD //
console.log('--------------------------------');
console.log('- Custom Web Component - BUILD -');
console.log('--------------------------------');
console.log('');

// clean up build
console.log('Cleaned Build...');
fsx.remove('./index.js')
.then(() => console.log('Cleaned Build DONE'))
.catch((error) => console.log('Cleaned Build FAILED...', error))

// build src into distributable
.then(() => console.log('Create distributable logic...'))
.then(() => new Promise((resolve, reject) => {
	browserify({ debug: true })
	.transform(babelify.configure({ extensions: [".mjs"] }))
	.require("./index.mjs", { entry: true })
	.bundle()
	.on("error", (err) => reject("Browserify/Babelify compile error: " + err.message))
	.pipe(fs.createWriteStream("./index.js"))
	.on("finish", () => resolve());
}))
.then(() => console.log('Create distributable logic DONE'))
.catch((error) => console.log('Create distributable logic FAILED', error))

.then(() => {
	console.log('');
	console.log('-------');
	console.log('- END -');
	console.log('-------');
	console.log('');
});

COMPILATION (to build the index.js fall back using build.js)

# example in example folder
# build the IE11 fallback
npm run build

Will run the build.js script

SERVE USING EXPRESS

# example in example folder
# serve to localhost:XXXX
npm run serve

PACKAGE SETUP FOR BABEL

{
  "version": "0.0.1",
  "name": "MyApp",
  "description": "My Application",
  "licence": "MIT",
  "author": "Paul Smith (ulsmith)",
  "scripts": {
    "start": "node server.js",
    "serve": "node server.js",
    "build": "node build.js"
  },
  "dependencies": {
    "@webcomponents/custom-elements": "^1.2.1",
    "@webcomponents/webcomponentsjs": "^2.2.1",
    "custom-web-component": "^1.2.0",
    "lit-html": "^0.14.0",
    "reflect-constructor": "^1.0.0"
  },
  "devDependencies": {
    "babel-core": "^6.26.3",
    "babel-plugin-transform-custom-element-classes": "^0.1.0",
    "babel-plugin-transform-es2015-classes": "^6.24.1",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-es2015-loose": "^7.0.0",
    "babel-preset-es2016": "^6.24.1",
    "babel-preset-latest": "^6.24.1",
    "babelify": "^7.3.0",
    "browserify": "^16.2.3",
    "express": "^4.16.4",
    "fs-extra": "^7.0.1",
    "replace-in-file": "^3.4.2"
  },
  "babel": {
    "plugins": [
      "transform-custom-element-classes",
      "transform-es2015-classes"
    ],
    "presets": [
      "latest"
    ]
  },
  "browserify": {
    "transform": [
      [
        "babelify",
        {
          "plugins": [
            "transform-custom-element-classes",
            "transform-es2015-classes"
          ],
          "presets": [
            "latest"
          ]
        }
      ]
    ]
  }
}

YOUR FIRST WEB COMPONENT

import { CustomHTMLElement, html } from "./node_modules/custom-web-component/index.js";

/**
 * HelloWorldComponent
 * A sample Custom HTML Element, to be used in any system that is capable of outputting HTML
 * Build on Web Standards and polyfilled for legacy browsers, using a simple clean lite HTML template rendering called lit-html
 */
class HelloWorldComponent extends CustomHTMLElement {
    
    /**
     * @public constructor()
     * Invoked when instantiation of class happens
     * NOTE: Call super() first!
     * NOTE: Declare local properties here... [this.__private, this._protected, this.public] 
     * NOTE: Declarations and kick starts only... no business logic here!
     */
    constructor() {
        super();

        this.foo = 'FOO!!';
        this.bar;
    }
    
    /**
     * template()
     * Return html TemplateResolver a list of observed properties, that will call propertyChanged() when mutated
     * @return {TemplateResult} Returns a HTML TemplateResult to be used for the basis of the elements DOM structure 
     */
    static template() {
        return html`
            <style>
                /* Style auto encapsulates in shadowDOM or shims for IE */
				:host { display: block; }
                #hello-world-component p { display: block; border: 2px solid red; padding: 20px; color: red; }
            </style>

			<div>
				<p>
					<slot name="main">Hello</slot>
					<br/>
					<br/>
					<strong>FOO:</strong> ${this.foo}
					<br/>
					<strong>BAR:</strong> ${this.bar}
					<br/>
					<br/>
					<slot name="footer">World</slot>
				</p>
			</div>
        `;
    }

    /**
     * @static @get observedProperties()
     * Return a list of observed properties, that will call propertyChanged() when mutated
     * @return {Array} List of properties that will promote the callback to be called on mutation 
     */
    static get observedProperties() { return ['foo', 'bar']; }

    /**
     * @public propertyChanged()
     * Invoked when an observed instantiated property has changed
     * @param {String} property The name of the property that changed 
     * @param {*} oldValue The old value before hte change 
     * @param {*} newValue The new value after the change
     */
    propertyChanged(property, oldValue, newValue) {
        console.log('propertyChanged', property, oldValue, newValue);

        this.updateTemplate();
    }
    
    /**
     * @public propertiesChanged()
     * Invoked when all property changes have taken place for this cycle
     * @param {Array} properties The names of the observed properties
     */
    propertiesChanged(properties) {
        console.log('propertiesChanged', properties);

        this.updateTemplate();
    }

    /**
     * @static @get observedAttributes()
     * Return a list of observed attributes, that will call attributeChanged() when mutated
     * @return {Array} List of attributes that will promote the callback to be called on mutation 
     */
    static get observedAttributes() { return ['bar']; }
    
    /**
     * @public attributeChanged()
     * Invoked when an observed node attribute has changed
     * @param {String} attribute The name of the attribute that changed 
     * @param {*} oldValue The old value before hte change 
     * @param {*} newValue The new value after the change
     */
    attributeChanged(attribute, oldValue, newValue) {
        console.log('attributeChanged', attribute, oldValue, newValue);

        if (attribute === 'bar') this.bar = newValue;

        this.updateTemplate();
    }
    
    /**
     * @public attributesChanged()
     * Invoked when all attribute changes have taken place for this cycle
     * @param {Array} attributes The names of the observed attributes
     */
    attributesChanged(attributes) {
        console.log('attributesChanged', attributes);

        this.updateTemplate();
    }

    /**
     * @public connected()
     * Invoked when node is connected/added to the DOM
     */
    connected() {
        console.log('connected');
    }

    /**
     * @public disconnected()
     * Invoked when node is disconnected/removed from the DOM
     */
    disconnected() {
        console.log('disconnected');
    }

    /**
     * @public templateUpdated()
     * Invoked when the template is updated
     */
    templateUpdated() {
        // this.dom will return you the <div id="hello-world-component"></div> element instance of this specific instance of the web component 
        console.log('Template has been updated via this.update()');
    }
}

customElements.define('hello-world-component', HelloWorldComponent);

use it in your html

<hello-world-component bar="bar!">
    <slot name="main">Hello</slot>
    <slot name="footer">World</slot>
</hello-world-component>