0.0.6 • Published 4 years ago

kami-infinitelist v0.0.6

Weekly downloads
1
License
MIT
Repository
github
Last release
4 years ago

<kami-infinitelist>

An infinite scroller. Just add a datasource provider and a delegate web components and it's work !

Demo

Installation

npm install --save kami-infinitelist

Or use the CDN :

<script src="https://cdn.jsdelivr.net/npm/kami-infinitelist"></script>

Usage

<script src="https://cdn.jsdelivr.net/npm/kami-infinitelist"></script>
<script>
    window.onload = function(){
        customElements.define('kami-infinitelist', KamiInfiniteList);
    }
</script>

Or with webpack / rollup

// ES6 Modules or TypeScript
import KamiInfiniteList from 'kami-infinitelist'

// register the component with the name you want
window.customElements.define('kami-infinitelist', KamiInfiniteList);

Example

For use the infinite list you need two prerequire.

  • a datasource
  • a delegate

    Datasource

    The datasource should be an JSON endpoint with query pagination. For exemple see the jsonplaceholder api work as well. Into the request, the component will add a query with some paramters :

  • page : the number of the current page

  • limit : the number of item by page

    You can update the param name with the properties pageQuery and limiQuery. For more information see the Props section.

    Delegate

    The delegate should be a web component. The property of this component will be map to the data by the infinte list component.

    This component should be into the main slot of the list. For more information see the Data Binding section.

    Simple list

    This the source code to create a simple list with this component. This is the final result.

index.html

<!-- infinite list component -->
<kami-infinitelist
    datasource="https://jsonplaceholder.typicode.com/posts/"
    delegate="post-custom"
    limitQuery="_limit"
    pageQuery="_start"
    limit='22'
    width="700px"
>
    <!-- delegate component -->
    <post-custom
        titleprops="title"
        bodyprops="body"
    >
    </post-custom>
</kami-infinitelist>

<!-- import iron icon if necessary -->
<script type="module" src="https://cdn.jsdelivr.net/npm/@polymer/iron-icons@3.0.1/iron-icons.min.js"></script>

<!-- infinite list lib -->
<script src="./KamiInfiniteList.umd.js"></script>

<!-- base component lib -->
<script src="./KamiComponent.umd.js"></script>

<!-- delegate component lib -->
<script src="./Post.js"></script>

<script>
    window.onload = function(){
        //init component
        customElements.define('post-custom',Post);
        customElements.define('kami-infinitelist', KamiInfiniteList);
    }
</script>

Post.js

class Post extends KamiComponent 
{
    constructor()
    {
        super();
    }

    static get observedAttributes() {
        return [
            'titleprops', 
            'bodyprops',
        ];
    }

    //set your properties to the parent
    //necessary for the render() method
    setProperties()
    {
        this.props = this.observe({
            titleprops: this.getAttribute('titleprops'),
            bodyprops: this.getAttribute('bodyprops')
        })
    }


    //init all your event listener
    initEventListener()
    {
        this.wrapper.querySelector('.post').addEventListener('click',()=>{
            alert(`title: ${this.props.titleprops} body: ${this.props.bodyprops}`)
        })
    }

    connectedCallback()
    {
        this.observeWindows = new IntersectionObserver(this.display.bind(this),{
            root: null,
            rootMargin: '0px',
            threshold: 0.1
        });

        this.observeWindows.observe(this)
        this.wrapper.style.position = 'relative';

    }

    display(changes)
    {
        changes.forEach(change => {
            if (change.intersectionRatio > 0) {
                this.wrapper.querySelector('.post').classList.add('post--display');
            }
        });
        
    }


    //render the dom structure
    renderHtml()
    {
        return `
            <div class="post">
                <div class="post__title">${this.props.titleprops}</div>
                <div class="post__body">${this.props.bodyprops}</div>
            </div>
        `;        
    }

    //render the style component
    renderStyle()
    {
        return `
            .post{
                margin: 5px;
                padding: 5px;
                background-color: ghostwhite;
                font-family: sans-serif;
                cursor: pointer;
                transform: translateY(20px);
                opacity: 0;
                transition: all 1s ease;
            }

            .post--display{
                transform: translateY(0px);
                opacity: 1;
                transition: all 1s ease;
            }

            .post:hover{
                color: white;
                background-color: grey;
            }

            .post__title{
                font-size: 20px;
                margin: 5px 0px;
            }

            .post__title:first-letter{
                text-transform: capitalize;
            }
            
        `;
    }

}

Data Binding

Simple data

To bind data to the infinite list you should create a delegate component. Schema of the data should be write into the delegate props.

For example we have this data :

[
    {
        "username": "test",
        "fullname": {
            "firstname": "test",
            "lastname": "test"
        },
        "posts": [
            {
                "title": "post1",
                "content":"content1"
            },
            {
                "title": "post2",
                "content":"content2"
            },
        ]
    }
]

To bind this you can create a user component :

<!-- infinite list component -->
<kami-infinitelist
    delegate="user-element"
    otherProps...
>
    <!-- delegate component -->
    <user-element
        username="username"
        firstname="fullname.firstname"
        lastname="fullname.lastname"
        posts="posts"
    >
    </user-element>
</kami-infinitelist>

Data with array

To bind array the component will stringify your data. You should to parse manually into your delegate like this :

// try catch are necessary if your delegate component extends KamiComponent.
try {
    return JSON.parse(this.getAttribute('posts'));
} catch (error) {
    return [];
}

See also the issues example for more details.

Nested

If your request response with an object and your data to bind are into a nested field. You can use the nested prop to bind this field and not the root object.

{
    info: {
        status: 200
    },
    // nested field
    results: [
        {
            "username": "test",
            "fullname": {
                "firstname": "test",
                "lastname": "test"
            },
            "posts": [
                {
                    "title": "post1",
                    "content":"content1"
                },
                {
                    "title": "post2",
                    "content":"content2"
                },
            ]
        }
    ]
}
<!-- infinite list component -->
<kami-infinitelist
    nested="results"
    otherProps...
>
    <!-- delegate component -->
</kami-infinitelist>

See also the nested example for more details.

Icons

Kami-infinitlist use iron-icons for display icons. If you want use this icone you need to import it manually.

npm install --save @polymer/iron-icons
<!-- import iron icon if necessary -->
<script type="module">
    import './node_modules/@polymer/iron-icon/iron-icon.js';
    import './node_modules/@polymer/iron-icons/iron-icons.js';
</script>

<!-- import before import kami infinite list -->
<script src="./KamiInfiniteList.umd.js"></script>

Or with webpack / rollup

import '@polymer/iron-icon/iron-icon.js';
import '@polymer/iron-icons/iron-icons.js';

// import before init kami infinite list
import KamiInfiniteList from 'kami-infinitelist'

See also search example into the example folder.

Props

nametypedescriptionrequireddefault value
datasourceStringDatasource providertrue
delegateStringDelegate web componenttrue
widthStringWidth of the listfalse100%
heightStringHeight of the listfalse100vh
useSearchBooleanDiplay the search barfalsefalse
searchQueryStringName of the search param in the queryfalsesearch
sortQueryStringName of the sort param in the queryfalsesort
pageQueryStringName of the page param in the queryfalsepage
limitQueryStringName of the limit param in the queryfalselimit
pageNumberNumber of the current pagefalse1
limitNumberNumber of item displayfalse10
flexBooleanUse the flex propertyfalsefalse
nestedstringParse an array into a nested field see the data binding sectionfalsenull

Events

nametriggerevent.detail
clickElementWhen a element of the list is clicked{ element: HTMLElement, index: number }

Contribute

We would love you for the contribution to kami-infinitelist project, check the CONTRIBUTING file for more info.