UNPKG

apphouse

Version:

Component library for React that uses observable state management and theme-able components.

225 lines (205 loc) 5.65 kB
import { makeAutoObservable } from 'mobx'; import { swapItems } from '../utils/list/swapItems'; /** * Minimum requirements values for an item in the list */ export interface IListItem { /** * Unique id for the item */ id: string; } /** * A model to handle lists of items */ export class List<T extends IListItem> { /** * Key value pairs of all items in the list */ private stack: { [id: string]: T } = {}; /** * Array of ids only, it keeps track of the order of the items */ private order: string[]; /** * Initializes the list * The list is initialized empty by default */ constructor(list?: T[]) { // the order of the items is the array of ids // this needs to be initialized first this.order = list?.map((item) => item.id) || []; this.set(list || []); makeAutoObservable(this); } /** * Get the array of all items in the list, the list will be ordered * @returns array of items */ get values(): T[] { // note that the item is not find it will return null return this.order.map((id) => this.stack[id]); } /** * returns the count of items in the list */ get length(): number { return this.order.length; } /** * Update the list with an array of items * use this method to update the list with an array of items * this method resets the list with theses items * use addValues instead if you want to append to the list * @param values array of items to initialize the list with */ private updateValues(values?: T[]) { values?.forEach((item) => { this.updateValue(item); }); } /** * Update the list with an item * If the item already exists it will be updated * If the item does not exist it will be created * @param value item to be added/updated in the list */ private updateValue(value?: T) { if (value) { this.stack[value.id] = value; if (!this.order.includes(value.id)) { this.order = [...this.order, value.id]; } } } /** * Utility method to get the index of an item in the list * @param id id of the item * @returns the index of the item or -1 if not found */ private getIndex = (id: string): number => { const list = this.order; return list.findIndex((item) => item === id); }; /** * Get an item from the list by id * @param id id of the item or the index of the item in the list * @returns the item or undefined if not found */ get = (id: string | number): T | undefined => { if (typeof id === 'number') { return this.getByIndex(id); } return this.stack[id]; }; /** * Get an item from the list by index * @param index the index of the item in the list * @returns the item or undefined if not found */ private getByIndex = (index: number): T | undefined => { const id = this.order[index]; return this.get(id); }; /** * Add an item to the list or update it if it already exists * @param item item to be added/updated in the list */ set = (item: T | T[]) => { if (typeof item === 'object' && Array.isArray(item)) { this.updateValues(item); } else { this.updateValue(item); } }; /** * Deletes the last item in the list and returns that item * @returns the last item in the list */ pop = (): T | undefined => { const lastItemId = this.order[this.order.length - 1]; if (lastItemId) { const lastItem = this.stack[lastItemId]; this.delete(lastItemId); return lastItem; } return undefined; }; /** * Deletes the first item in the list and returns that item * @returns the first item in the list */ shift = (): T | undefined => { const firstItemId = this.order[0]; if (firstItemId) { const firstItem = this.stack[firstItemId]; this.delete(firstItemId); return firstItem; } return undefined; }; /** * Get the last item of the list * @returns the last item in the list */ peakLast = () => { const lastItemId = this.order[this.order.length - 1]; if (lastItemId) { return this.stack[lastItemId]; } return undefined; }; /** * Get the first item of the list * @returns the first item in the list */ peakFirst = () => { const firstItemId = this.order[0]; if (firstItemId) { return this.stack[firstItemId]; } return undefined; }; /** * Delete an item from the list * @param id id of the item to be removed * @returns true if item was removed, false if item was not found */ delete = (id: string): boolean => { // first remove the id from the order this.order = this.order.filter((itemId) => itemId !== id); // then delete the item from the stack if (this.stack[id]) { delete this.stack[id]; return true; } return false; }; /** * Swaps two items in the list * @param index1 index of the first item to be swapped * @param index2 index of the second item to be swapped */ swap = (index1: number, index2: number): void => { const list = this.order; const updatedList = swapItems<string>(list, index1, index2); this.order = updatedList; }; /** * Swaps two items in the list by id * @param id1 the id of the first item to be swapped * @param id2 the id of the second item to be swapped */ swapById = (id1: string, id2: string): void => { const index1 = this.getIndex(id1); const index2 = this.getIndex(id2); this.swap(index1, index2); }; /** * clears the list */ reset = () => { this.order = []; this.stack = {}; }; }