UNPKG

apphouse

Version:

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

220 lines (190 loc) 5.39 kB
import { makeAutoObservable } from 'mobx'; import { AppLayoutType } from '../models/AppLayout'; import { Route } from './routing/route.interface'; import { omit } from '../utils/obj/omit'; import { keys } from '../utils/obj/keys'; const DEFAULT_APP_NAME = 'Apphouse App'; class PartialView { id: string; open: boolean; updatedAt: number; constructor(props: { id: string; open: boolean }) { const { id, open } = props; this.id = id; this.open = open; this.updatedAt = new Date().getTime(); if (open) { this.updatedAt = new Date().getTime(); } makeAutoObservable(this); } setOpen(open: boolean) { this.open = open; this.updatedAt = new Date().getTime(); } } export interface IView { appName?: string; appLayout?: AppLayoutType; routes?: Route[]; } /** * A model to handle views in the app. * This will be similar to a route in a traditional web app. */ class View { path: string; title: string; updated: number; tabIndex?: number; tabId?: string; sectionId?: string; params?: { [key: string]: string }; routes: Route[]; /** * Handles partial views states such as tabs, sections, etc. * To be able to access them from anywhere in the app. */ views: { [key: string]: PartialView }; constructor(props: IView = {}) { this.title = props.appName || DEFAULT_APP_NAME; this.path = '/'; this.updated = 0; this.sectionId = undefined; this.tabId = undefined; this.tabIndex = 0; this.params = {}; this.views = {}; this.routes = props.routes || []; makeAutoObservable(this); } get openedViewsByMostRecent() { return Object.keys(this.views) .map((key) => this.views[key]) .filter((view) => view.open) .sort((a, b) => b.updatedAt - a.updatedAt) .slice() .reverse(); } get currentPath() { let path = this.path; const params = omit(this.params, ['path']); const openParams = keys(params).filter((key) => this.views[key]?.open); openParams.forEach((key) => { if (path.includes(`:${key}`) && params && params[key]) { path = path.replace(`:${key}`, params[key]); } }); const mostRecentViewsPath = this.openedViewsByMostRecent.map((v) => v.id); // We still have a bug here that yields to a wrong path if (mostRecentViewsPath.length > 1) { const tail = path.split('/').pop(); const _mostRecent = mostRecentViewsPath.slice(); const mostRecent = _mostRecent.shift(); if (tail === mostRecent) { return `${path}/${_mostRecent.join('')}`; } return `${path}/${mostRecentViewsPath.join('/')}`; } else { const mostRecent = mostRecentViewsPath.join('/'); if (path.includes(mostRecent) || path.includes(`:${mostRecent}`)) { return path; } return `${path}/${mostRecent}`; } } get currentParams() { if (this.updated > 0) { return this.params || {}; } return this.params || {}; } get crumbs() { return this.path.split('/').filter((crumb) => crumb !== ''); } open = (path: string, title?: string, onOpen?: () => void) => { this.setPath(path); this.setTitle(title || DEFAULT_APP_NAME); this.setTabId(undefined); onOpen && onOpen(); }; openTab = (tabId?: string, tabIndex?: number) => { this.setTabId(tabId); this.setTabIndex(tabIndex || 0); }; closeTab = () => { this.setTabId(undefined); this.setTabIndex(0); }; openSection = () => { this.setSectionId(this.tabId); }; setParams = (params: { [key: string]: string }): void => { this.params = params; }; setRoutes = (routes: Route[]): void => { this.routes = routes; }; /** * Sets a partial view in the app * @param id string the id of the partial view * @param open boolean to set the partial view open or closed */ setView = (id: string, open: boolean) => { if (this.views[id]) { this.views[id].setOpen(open); } else { this.views[id] = new PartialView({ id, open }); } if (!open) { this.setPath(this.params?.path || '/'); } }; toggleView = (id: string) => { if (this.views[id]) { this.views[id].setOpen(!this.views[id].open); } else { this.setView(id, true); } }; closeAllOpenViews = () => { Object.keys(this.views).forEach((key) => { this.setView(key, false); }); }; getViewStatus = (id: string): boolean => { if (this.views[id]) { return this.views[id].open; } return false; }; deleteAllViews = () => { this.views = {}; }; closeMostRecentOpenedView = () => { // Get the most recent opened view const mostRecentOpenedView = Object.values(this.views) .filter((v) => v.open) .sort((a, b) => b.updatedAt - a.updatedAt)[0]; if (mostRecentOpenedView) { this.setView(mostRecentOpenedView.id, false); this.setPath(this.params?.path || '/'); } }; private setSectionId = (sectionId?: string): void => { this.sectionId = sectionId; }; private setTabIndex = (index: number) => { this.tabIndex = index; }; private setTitle = (title: string) => { this.title = title; }; private setTabId = (locationTabId?: string) => { this.tabId = locationTabId; }; private setPath = (value: string) => { this.path = value; }; } export default View;