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

@iosio/elemental

v1.0.2

Published

web component essential ingredients

Downloads

5

Readme

Elemental 🧠

Web Component Essentials with built-in adoptable styleSheets (including fallback), static template caching, and more...

@TODO: write better docs

Usage

import {Elemental} from "the/path/to/elemental";

export class MyComponent extends Elemental{
    //set to true to apply shadowdom
    static shadow = true;
    /* 
     Component will check for adopted stylesheets 
     and if not available will append a style tag to the root/shadowRoot.
        
     optionally set static styles as a string
     static styles = ':host{background:aliceblue}';
    */
    //or an object with options
    static styles = {
        // styles to adopt to each instance of this component 
        // (efficiently achieved with adoptedStyleSheets),
        css: ':host{color:red}',
        // async: true, // constructable style sheet option. async true uses replace else replaceSync 
 
        // you can force the usage of the style tag over adoptedStyleSheets
        // useStyleTag: true,
 
        // optionally include global styles that will be set once to the document head
        global: 'x-element{visibility:hidden}',
        
        //if using shadowRoot, some resets are included by default
        //:host, *, *::before, *::after {box-sizing: border-box;}
        //set this to true to disable using the resets
        //noDefaultResets: true,
    };

    /*  
        If your component is simple and doesn't update much, it might be more performant 
        to use a template instead of JSX, since a copy is cached and cloned per instance.
        this is is a static value but dom may be updated manually in beforeInitialUpdate and after didMount.
        or with propLogic
    */
    static template = '<h1 id="my_ref">hello elemental</h1><input id="checkbox" type="checkbox"/>';
    
    /*
        proxyRefs

        (this makes sense if using a template and not vdom bacuse you can just pass a function to the ref prop)

        to automatically proxy elements references, 
        set this to true or an options object to override defaults.
        * keep your ref names camel or snake cased
        
        when the component mounts, reference to the h1 tag above will be available on:
        this.refs.my_ref.
        
        (in a case where you are completely wiping out the dom or updating nodes, you can call:
            this.refs.refreshRefsCache()
        to pull new references)
    */
    static proxyRefs = {
        //(default) likely there will be shadowDom so the default selector is set to get by id
        selector: ref => `#${ref}`, // can change to something like (ref) => `[data-${ref}]`
        selectorMethod: 'querySelector', // can change to something like querySelectorAll
    };   
    
    /*
        propTypes
        define all your properties with types, optionally with a default value 
        and optional reflect property to reflect your props as attrs.

        (if reflected:true) 
        When a prop is set, it will update the corresponding attribute to kabob-case version 
        
        in addition, properties on the class will be updated when attributes are set 
        (this is by default without setting the reflect option)
        
    */
   
    static propTypes = {

        myStringProp: String,
        myBooleanProp: Boolean,
        myNumberProp: Number,
        myObjectProp: Object,
        myArrayProp: Array,
        anyValueGoesProp: 'any',

        toBeReflected: {
            type: String,
            reflect: true
        },

        toBeReflectedWithDefaultValue: {
            type: Boolean,
            reflect: true,
            value: true
        },
        checked: {
            type: Boolean,
            reflect: true,
        }
    };

    /*
        state should always be an object
        use this.setState({someProp: 'newValue'}) 
        or this.setState(({someNum})=>({someNum: someNum + 1})
        
       setState will call the method onStateChange(){
            then this.update then didUpdate()
       }  
       if you override onStateChange, then you will need to manually call this.update
    */
    state = {
        count: 0
    };   
    
    // before didMount gets called 
    // (if using a template) this gets called after the template is applied, but before didMount and propLogic
    beforeInitialUpdate(){
    }
    
    //example handle click without jsx
    handleClick = (e) => {
        e.stopPropagation();
        let c = this.refs.checkbox.checked;
        this.checked = c;
        this.emit('change', {checked: c});
    };

    didMount() {
        // this.eventListener will automatically remove the event listener when the component unmounts
        this.eventListener(this.refs.checkbox, 'click', this.handleClick);
        // all subscriptions (like the event listener above) are pushed into this.unsubs.
        /*
            //here is an example of doing the above manually

            this.checkbox = this.shadowRoot.querySelector("#checkbox");
            this.checkbox.addEventListener("click", this.handleClick);
            this.unlisten = () => this.checkbox.removeEventListener("click", this.handleClick);
            // then either push it into unsubs 
            this.unsubs.push(this.unlisten)
            // or call unlisten inside willUnmount() 
        */ 
    }
    
    willUnmount(){
        // this.unlisten();
    }

    onStateChange(state, changedPaths = ['nested.value']){ //array of values that have changed on the object

        //if including this method, it will override calling update (thus wont call render, propLogic and didUpdate)
        // so manually calling this.update() here may be necessary (must do so if using the render function with vdom)
        // otherwise, omit this method and render will be called onChange
    }

    // called when props or state changes (unless onStateChange is overriden like above)
    didUpdate(props = {}, prevProps = {}, changedProps = ['myStringProp', 'example']){
    }   

    //****** using propLogic makes sense if using a template and you don't have a lot of changes happening.
    // Make precise updates based on which prop changes
    propLogic = (init)=>({ // initially runs after didMount (init===true) then is triggered for every update (init===false) 
        myStringProp: (value, refs) => {
            if(init){ //upon didMount
                refs.my_ref.textContent = value || 'default name'; //value is the value from the prop
            }else{ 
                // subsequent updates 
            }
        },
        // this both initializes and updates the checkbox when the prop changes
        checked: (checked, {checkbox}) => checkbox.checked = checked
    });
    
    // if you'd like to use jsx / preact / lit-html
    // you can hook into the render cycle here. 
    // this.render is called passing the following arguments
    //     (this.props, this.state, this.setState, this)
    // results from render are passed here, including the shadowRoot (if available) or the host element
    // as the second argument
    renderer = (resultsFromRender, shadowRootOrHost /* this.shadowRoot || this */)=>{
        // renderer provides an extra layer of control if you'd like to create
        // an abstracted layer on top of elemental,
        // thus, having the results from render here provides a prehook before
        // anything actually gets rendered
    
        //example with preact render
        render(resultsFromRender, shadowRootOrHost)
    };   
    
    render(props, state, setState, self /* self = this */){
        
        return (
            <Fragment>
                <h1 ref={r => this.my_ref = r} style={{color: 'red', fontSize: 50 /*no need for pixel vaue*/}}>
                    hello: {props.myStringProp} 
                </h1>
                 <h2 style={{color: 'blue'}} className={'some className'}>
                        {state.count}
                 </h2>
                <button onClick={()=> state.count++}> inc count +</button>
            </Fragment>
        )   
    }   
}

customElements.define('my-component', MyComponent);