SantiagoPagnone
10/30/2019 - 2:28 PM

VirtualList example

const virtualListTemplate = document.createElement('template');
virtualListTemplate.innerHTML = /*template*/ `
    <div class="list virtual-list media-list searchbar-found no-hairlines-md">
    </div>
`;

class VirtualListControl extends HTMLElement {
    constructor() {
        super();
        this.controlType = "VirtualListControl";
        console.log("%c VirtualListControl loaded", LOG.Debug);
        this.appendChild(virtualListTemplate.content.cloneNode(true));

        // set references to the DOM elements from the component's template
        //this.$virtualList = this.querySelector("#view-controltest > div > div.page-content > div > virtuallist-control > div");
        this.$virtualList = this.querySelector("virtuallist-control > div.list.virtual-list.media-list.searchbar-found.no-hairlines-md");
    }

    static get observedAttributes() {
        return ["table", "table-fields", "item-title", "item-subtitle", "label-count-description"];
    }

    attributeChangedCallback(attrName, oldValue, newValue) {
        console.log(`%c ${this.controlType} -  ${attrName} ha cambiado de ${oldValue} a ${newValue}`, "font-size:15px;color:white; background-color: blue");
        //debugger;
        if (attrName == 'table') {
            this.table = newValue;
            console.log(`%c TABLE: ${this.table}`, LOG.Debug);
        }
        if (attrName == 'table-fields') {
            this.tableFields = newValue;
        }
        if (attrName == 'item-title') {
            this.itemTitle = newValue;
        }
        if (attrName == 'item-subtitle') {
            this.itemSubtitle = newValue;
        }
        if (attrName == 'label-count-description') {
            this.labelCountDescription = newValue;
        }
    }


    connectedCallback() {
        console.log(" ${controlType} Conected");
        this.pageInit();
        this.listLodaded();
    }

    listLodaded() {
        this.dispatchEvent(new CustomEvent('loaded'));
        /*this.dispatchEvent(new CustomEvent('custom', {
            detail: {
                message: "un mensaje custom"
            }
        }));*/
    }

    disconnectedCallback() {
        console.log(" ${controlType} disconnected/removed");
    }

    itemClick() {
        this._list.on('click', 'a', function (e) {
            var id;
            if ($(this).attr('doc_id')) {
                id = 'doc_id=' + $(this).attr('doc_id');
            } else {
                id = 'guid=' + $(this).attr('guid');
            }
            app.req2List.params.lastClick = id;
            f7Page.view.router.navigate('/generic/?page=requerimientos_editar&' + id);
        });
    }

    create() {

        this._list = app7.virtualList.create({
            el: this.$virtualList,

            rowsBefore: 100,
            rowsAfter: 100,
            dynamicHeightBufferSize: 2,

            // Se llenan despues
            items: [],

            height: function (item) {
                // Determina el alto segun el contenido
                // todo: Si text tiene 2 lineas necesita mas espacio
                // http://forum.framework7.io/t/virtual-list-with-dynamic-elements/3991/4
                if (item.divider) {
                    return app7.theme == 'ios' ? 31 : 48;
                } else {
                    var cont = 't';
                    if (item.subtitle) cont += 's';
                    if (item.text) cont += 't';

                    if (app7.theme == 'ios') {
                        if (cont == 't') return 44;
                        else if (cont == 'ts') return 64.55;
                        else if (cont == 'tt') return 64.55;
                        else if (cont == 'tst') return 85.46;
                    } else {
                        if (cont == 't') return 51.62;
                        else if (cont == 'ts') return 72.53;
                        else if (cont == 'tt') return 71.62;
                        else if (cont == 'tst') return 92.53;
                    }
                }
            },

            // Custom search function for searchbar
            searchAll: function (query, items) {
                var found = [];
                var str;
                for (var i = 0; i < items.length; i++) {
                    if (!items[i].divider) {
                        if (query.trim() === '') {
                            found.push(i);
                        } else {
                            str = items[i].title;
                            if (items[i].subtitle) str += '||' + items[i].subtitle;
                            if (items[i].text) str += '||' + items[i].text;
                            if (items[i].search) str += '||' + items[i].search;
                            str = str.toLowerCase();
                            if (str.indexOf(query.toLowerCase()) >= 0) found.push(i);
                        }

                    }
                }
                return found; //return array with matched indexes
            },

            renderItem: function (item) {
                //debugger;
                var $li, $a, $itemInner, $itemTitleRow;

                if (item.divider) {
                    $li = $('<li/>', {
                        class: 'item-divider',
                    }).append(item.title);

                } else {
                    $li = $('<li/>');

                    $a = $('<a/>', {
                        href: '#',
                        class: 'item-link item-content',
                        doc_id: item.doc_id,
                        guid: item.guid,
                    }).appendTo($li);

                    $itemInner = $('<div/>', {
                        class: 'item-inner',
                    }).appendTo($a);

                    $itemTitleRow = $('<div/>', {
                        class: 'item-title-row',
                    }).appendTo($itemInner);

                    $('<div/>', {
                        class: 'item-title',
                    }).append(item.title).appendTo($itemTitleRow);

                    if (item.subtitle) {
                        $('<div/>', {
                            class: 'item-subtitle',
                        }).append(item.subtitle).appendTo($itemInner);
                    };

                    if (item.text) {
                        $('<div/>', {
                            class: 'item-text',
                        }).append(item.text).appendTo($itemInner);
                    };
                };

                return $li.get(0).outerHTML;
            },
        });

        var vList = this._list;
        this._list.params.refresh = function () {
            var self = vList;
            var par = self.params;
            var lastLoad = par.lastLoad;
            // Recarga si nunca se cargo
            if (!lastLoad) {
                par.loadItems();
            } else {
                // Recarga si hubo un fullSync dps de la ult carga
                var tableName = sync.tableName(par.tableName);
                var lastFull = window.localStorage.getItem('lastFullSync_' + tableName.substr(7))
                if (lastFull > lastLoad) {
                    par.loadItems();
                } else {
                    var sql = 'select doc_id from ' + tableName + ' where modified_local >= ? or modified >= ?';
                    dbRead(sql, [lastLoad, lastLoad],
                        function (rs) {
                            // Recarga si hay algun registro modificado desde la ult carga
                            if (rs.rows.length > 0) {
                                par.loadItems();
                            }
                        }
                    );
                }
            }
            //debugger;
            //this.itemClick();
        };

        this._list.params.loadItems = function () {
            var self = vList;
            var items = [];
            var sql = 'select  doc_id, guid, ' + self.params.tableFields + ' modified_local' +
                ' from ' + sync.tableName(self.params.tableName) + '  order by modified desc LIMIT 3';

            dbRead(sql, [], function (rs) {
                var row;
                debugger;
                var titleLabelDescription = (self.params.labelCountDescription) ? self.params.labelCountDescription : '';
                items.push({
                    divider: true,
                    title: '<b>' + rs.rows.length + ' ' + titleLabelDescription + '</b>',
                });
                for (var i = 0; i < rs.rows.length; i++) {
                    row = rs.rows.item(i);

                    items.push({
                        doc_id: row['doc_id'],
                        guid: row['guid'],
                        title: row[self.params.itemTitle] + (row['modified_local'] ? ' (!)' : ''),
                        subtitle: row[self.params.itemSubtitle],
                        //subtitle: row['contrato'] + ' (' + row['estado'] + ')',
                        //text: '...',
                        //search: 'texto adicional para el search',
                    });
                };
                self.params.loadItemsCallback(items);
            });
        };
        this._list.params.loadItemsCallback = function (pItems) {
            var self = vList;
            var par = self.params;

            // Reemplaza los items
            self.replaceAllItems(pItems);

            // Restaura la busqueda
            if (par.searchBar) {
                var query = par.searchBar.query;
                if (query) {
                    var filtered = par.searchAll(query, pItems);
                    self.filterItems(filtered);

                    // Scrolla al ult item clickeado (esto no hace falta si no hay filtro)
                    if (par.lastClick) {
                        var arr = par.lastClick.split('=');
                        var key = arr[0];
                        var val = arr[1];
                        var j, scrollTo;
                        // Busca el index del ult doc_id o guid clickeado
                        for (var i = 0; i < pItems.length; i++) {
                            if (pItems[i][key] + '' == val) {
                                j = i;
                                break;
                            };
                        };
                        // Busca j en la lista de filtrados
                        if (j != undefined) {
                            for (var i = 0; i < filtered.length; i++) {
                                if (filtered[i] == j) {
                                    scrollTo = i;
                                    break;
                                };
                            };
                        };
                        // Scrolla a ese item
                        if (scrollTo != undefined) {
                            if (scrollTo > 0) scrollTo--; // -1 para que no quede justo arriba
                            self.scrollToItem(scrollTo);
                        };
                    };;
                }
                par.lastLoad = (new Date).toJSON();
            };
        };
        return this._list;
    }

    getList() {
        return this._list;
    }



    refresh() {
        this._list.params.refresh();
    }

    pageInit() {
        console.log("%c VirtualListControl PageInit START", LOG.Debug);
        var vList = this.create();
        this._list = vList;
        this._list.params.labelCountDescription = this.labelCountDescription;
        this._list.params.tableName = this.table;
        this._list.params.tableFields = this.tableFields;
        this._list.params.itemTitle = this.itemTitle;
        this._list.params.itemSubtitle = this.itemSubtitle;
    }
}
window.customElements.define('virtuallist-control', VirtualListControl);