import { BaseView } from "./base-view"; import { PouchService } from "../services/pouch-service"; import { EventsManager } from "../base/events/event-manager"; /** * * ██████╗ █████╗ ███████╗███████╗██╗ ██╗███████╗████████╗██╗ ██╗██╗███████╗██╗ ██╗ * ██╔══██╗██╔══██╗██╔════╝██╔════╝██║ ██║██╔════╝╚══██╔══╝██║ ██║██║██╔════╝██║ ██║ * ██████╔╝███████║███████╗█████╗ ██║ ██║███████╗ ██║ ██║ ██║██║█████╗ ██║ █╗ ██║ * ██╔══██╗██╔══██║╚════██║██╔══╝ ██║ ██║╚════██║ ██║ ╚██╗ ██╔╝██║██╔══╝ ██║███╗██║ * ██████╔╝██║ ██║███████║███████╗███████╗██║███████║ ██║ ╚████╔╝ ██║███████╗╚███╔███╔╝ * ╚═════╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝╚═╝╚══════╝ ╚═╝ ╚═══╝ ╚═╝╚══════╝ ╚══╝╚══╝ * * Clase que reúne todas las capacidades para mostrar e interactuar con una vista de lista. * */ export abstract class BaseListView extends BaseView{ items: T[]; itemsShown: T[]; itemsMatched: T[]; selectedItem: T; filters: Array<[string, string, any]>; search: boolean; range: { start: number, end: number }; constructor(c: { new (): T; }, ...filters: Array<[string, string, any]>) { super(c, PouchService); this.items = []; this.itemsShown = []; this.itemsMatched = []; this.selectedItem = null; this.filters = filters; this.search = false; this.range = { start: 0, end: 10 } this.initialize(); } // ------------------------------------------------------------ // ██████╗ ███████╗████████╗████████╗███████╗██████╗ ███████╗ // ██╔════╝ ██╔════╝╚══██╔══╝╚══██╔══╝██╔════╝██╔══██╗██╔════╝ // ██║ ███╗█████╗ ██║ ██║ █████╗ ██████╔╝███████╗ // ██║ ██║██╔══╝ ██║ ██║ ██╔══╝ ██╔══██╗╚════██║ // ╚██████╔╝███████╗ ██║ ██║ ███████╗██║ ██║███████║ // ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚══════╝ // ------------------------------------------------------------ /** * */ getItems(): T[] { return this.items; } /** * */ getItemsShown(): T[] { return this.itemsShown; } /** * */ getItemsMatched(): T[] { return this.itemsMatched; } /** * */ getSelectedItem(): T { return this.selectedItem; } /** * */ getFilters(): Array<[string, string, any]> { return this.filters; } /** * */ getRange(): { start: number, end: number } { return this.range; } // ----------------------------------------------------------- // ███████╗███████╗████████╗████████╗███████╗██████╗ ███████╗ // ██╔════╝██╔════╝╚══██╔══╝╚══██╔══╝██╔════╝██╔══██╗██╔════╝ // ███████╗█████╗ ██║ ██║ █████╗ ██████╔╝███████╗ // ╚════██║██╔══╝ ██║ ██║ ██╔══╝ ██╔══██╗╚════██║ // ███████║███████╗ ██║ ██║ ███████╗██║ ██║███████║ // ╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚══════╝ // ----------------------------------------------------------- /** * * @param items */ setItems(items: T[]): void { this.items = items; this.setItemsShown(this.getItems().slice(this.getRange().start, this.getRange().end)); } /** * * @param itemsShown */ setItemsShown(itemsShown: T[]): void { this.itemsShown = itemsShown; } /** * * @param itemsMatched */ setItemsMatched(itemsMatched: T[]): void { this.itemsMatched = itemsMatched; } /** * * @param item */ setSelectedItem(item: T): void { this.selectedItem = item; } /** * * @param filters */ setFilters(filters: Array<[string, string, any]>): void { this.filters = filters; this.applyFilter(this.getItems()); } /** * */ isSearch(): boolean { return this.search; } /** * * @param search */ setSearch(search: boolean): void { this.search = search; } /** * * @param range */ setRange(range: { start: number, end: number }): void { this.range = range; } // ------------------------------------------------------------------------------------------------------------------- // ███████╗██████╗ ███████╗ ██████╗██╗ █████╗ ██╗ ██████╗ ███████╗████████╗████████╗███████╗██████╗ ███████╗ // ██╔════╝██╔══██╗██╔════╝██╔════╝██║██╔══██╗██║ ██╔════╝ ██╔════╝╚══██╔══╝╚══██╔══╝██╔════╝██╔══██╗██╔════╝ // ███████╗██████╔╝█████╗ ██║ ██║███████║██║ ██║ ███╗█████╗ ██║ ██║ █████╗ ██████╔╝███████╗ // ╚════██║██╔═══╝ ██╔══╝ ██║ ██║██╔══██║██║ ██║ ██║██╔══╝ ██║ ██║ ██╔══╝ ██╔══██╗╚════██║ // ███████║██║ ███████╗╚██████╗██║██║ ██║███████╗ ╚██████╔╝███████╗ ██║ ██║ ███████╗██║ ██║███████║ // ╚══════╝╚═╝ ╚══════╝ ╚═════╝╚═╝╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚══════╝ // ------------------------------------------------------------------------------------------------------------------- /** * */ getStartRange(): number { return this.getRange().start; } /** * */ getEndRange(): number { return this.getRange().end; } /** * */ hasItems(): boolean { return this.getItems().length > 0; } /** * */ hasItemsShown(): boolean { return this.getItemsShown().length > 0; } /** * */ isExpandableRange(): boolean { return this.getRange().end < this.getItemsShown().length; } /** * */ getSelectedIndexInAll(): number { if (!this.getSelectedItem()) { return -1; } return this.getItems().indexOf(this.getSelectedItem()); } /** * */ getSelectedIndexInShown(): number { if (!this.getSelectedItem()) { return -1; } return this.getItemsShown().indexOf(this.getSelectedItem()); } /** * */ getSelectedIndexInMatched(): number { if (!this.getSelectedItem()) { return -1; } return this.getItemsMatched().indexOf(this.getSelectedItem()); } // ------------------------------------------------------------------------------------------------------------------ // ███████╗██████╗ ███████╗ ██████╗██╗ █████╗ ██╗ ███████╗███████╗████████╗████████╗███████╗██████╗ ███████╗ // ██╔════╝██╔══██╗██╔════╝██╔════╝██║██╔══██╗██║ ██╔════╝██╔════╝╚══██╔══╝╚══██╔══╝██╔════╝██╔══██╗██╔════╝ // ███████╗██████╔╝█████╗ ██║ ██║███████║██║ ███████╗█████╗ ██║ ██║ █████╗ ██████╔╝███████╗ // ╚════██║██╔═══╝ ██╔══╝ ██║ ██║██╔══██║██║ ╚════██║██╔══╝ ██║ ██║ ██╔══╝ ██╔══██╗╚════██║ // ███████║██║ ███████╗╚██████╗██║██║ ██║███████╗ ███████║███████╗ ██║ ██║ ███████╗██║ ██║███████║ // ╚══════╝╚═╝ ╚══════╝ ╚═════╝╚═╝╚═╝ ╚═╝╚══════╝ ╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚══════╝ // ------------------------------------------------------------------------------------------------------------------ /** * * @param start */ setStartRange(start: number): void { this.setRange({ start: start, end: this.getRange().end }); } /** * * @param end */ setEndRange(end: number): void { this.setRange({ start: this.getRange().start, end: end }); } //--------------------------------------------------------------- // ███████╗███████╗██████╗ ██╗ ██╗██╗ ██████╗███████╗███████╗ // ██╔════╝██╔════╝██╔══██╗██║ ██║██║██╔════╝██╔════╝██╔════╝ // ███████╗█████╗ ██████╔╝██║ ██║██║██║ █████╗ ███████╗ // ╚════██║██╔══╝ ██╔══██╗╚██╗ ██╔╝██║██║ ██╔══╝ ╚════██║ // ███████║███████╗██║ ██║ ╚████╔╝ ██║╚██████╗███████╗███████║ // ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═══╝ ╚═╝ ╚═════╝╚══════╝╚══════╝ //--------------------------------------------------------------- /** * Inicializa la lista. * Emite una evento al empezar y terminar el proceso. */ initialize() { EventsManager.publish("app:loading", true); super.getInjectable(PouchService).getAll(this.getModelName()).subscribe(result => { this.applyFilter(result.docs); this.subcribeToEvents(); EventsManager.publish("app:loading", false); }, error => { console.log(error); EventsManager.publish("app:loading", false); EventsManager.publish("app:error", error); }); } /** * Aplica los filtros correspondientes a la lista */ applyFilter(records: T[]): void { if (records.length == 0) { return; } if (this.getFilters().length == 0) { this.setItems(records); return; } this.setItems(records.filter(record => { let expressions = this.getFilters().map(filter => { let operator = "==="; let leftOperand = "record." + filter[0]; let rightOperand = typeof(filter[2]) === "string" ? "'" + filter[2] + "'" : filter[2]; if (filter[1] === "!=") { operator = "!=="; } if (filter[1] === ">") { operator = ">"; } if (filter[1] === "<") { operator = "<"; } if (filter[1] === ">=") { operator = "<="; } if (filter[1] === "<=") { operator = "<="; } return leftOperand + operator + rightOperand; }); return eval(expressions.join("&&")); })); } /** * Suscribe a los eventos de la lista */ private subcribeToEvents(): void { EventsManager.subscribe("app:changed", data => { if (data.action === "add") { this.add(data.item); } if (data.action === "update") { this.update(data.item); } if (data.action === "delete") { this.remove(); } this.unselectItem(); }); } /** * Adicioana a la lista un nuevo objeto guardado * @param obj */ add(item: T): void { this.items.unshift(item); this.itemsShown.unshift(item); } /** * Actualiza el objeto modificado * @param item */ update(item: T): void { this.itemsShown[this.getSelectedIndexInShown()] = item; if (this.isSearch()) { this.itemsMatched[this.getSelectedIndexInShown()] = item; } this.items[this.getSelectedIndexInAll()] = item; } /** * Quita el objeto borrado de la lista */ remove(): void { this.itemsShown.splice(this.getSelectedIndexInShown(), 1); if (this.isSearch()) { this.itemsMatched.splice(this.getSelectedIndexInShown(), 1); } this.items.splice(this.getSelectedIndexInAll(), 1); } /** * Deselecciona el elemento que haya sido previamente seleccionado. */ unselectItem(): void { this.setSelectedItem(null); } /** * Limpia las coincidencias de búsqueda. */ clearItemsMatched(): void { this.setItemsMatched([]); } /** * Alterna entre el modo de lista y el modo de búsqueda de la lista. */ toggleSearch(): void { this.setSearch(!this.isSearch()); this.setRange({ start: 0, end: 10 }); if (!this.isSearch()) { this.setItemsMatched([]); this.setItemsShown(this.getItems().slice(this.getRange().start, this.getRange().end)); } } /** * Realiza la expansión del rango para la vista. */ expandRange(): void { this.setRange({ start: this.getRange().end, end: this.getRange().end + 10 }); if (this.isSearch()) { this.setItemsShown(this.getItemsShown().concat(this.getItemsMatched().slice(this.getRange().start, this.getRange().end))); } else { this.setItemsShown(this.getItemsShown().concat(this.getItems().slice(this.getRange().start, this.getRange().end))); } } /** * Realiza la expasión de la vista de la lista. * * @param event */ expand(event: any): void { this.expandRange(); if (this.isExpandableRange()) { event.enable(false); } event.complete(); } /** * Realiza la busqueda cada campo de los objetos en la lista de items mientras este campo sea de tipo string. * Emite un evento cuando comienza y termina el proceso. * @param text */ performSearch(event: any): void { let value: string = event.target.value; if (!value || value.length < 3) { return; } EventsManager.publish("app:loading", true); this.clearItemsMatched(); let regExp = new RegExp(value, "g"); let item: T = null; let keys: Array = null; for (let i = 0; i < this.getItems().length; i++) { item = this.getItems()[i]; keys = Object.keys(item); for (let j = 0; j < keys.length; j++) { if (typeof item[keys[j]] !== "string") { continue; } if (!!item[keys[j]].toLowerCase().match(regExp)) { this.getItemsMatched().push(item); break; } } } this.setItemsShown(this.getItemsMatched().slice(this.getRange().start, this.getRange().end)); EventsManager.publish("app:loading", false); } /** * Realiza el borrado del item seleccionado. * Emite un evento al terminar el proceso. */ performDelete(): void { super.getInjectable(PouchService).remove(this.getSelectedItem()).subscribe(result => { EventsManager.publish("app:changed", { action: "delete", details: result }); }, error => { EventsManager.publish("app:error", error); }); } }