@osaedasia/oresume
Version:
A user-friendly library for generating complete Single Page Applications (SPAs)
2 lines (1 loc) • 3.01 kB
JavaScript
import{AppAttributes as e}from"../domain/attributes/AppAttributes.js";import{RandomUUID as t,UrlHash as i}from"@osaedasia/std";import{AppRootElementNotFoundError as a,AppNotInitializedError as n,AppInitializedError as r}from"../domain/exceptions/App.js";import{PageManager as o}from"./page/PageManager.js";import{PageDefinitionManager as s}from"./page/PageDefinitionManager.js";class l{static _instance=null;_config;_uuid;_pageManager;_defaultPage;_currentPage;constructor(e){this._uuid=t.v4,this._config=e,this._pageManager=new o(e.urlBasedRouting??!0),this._initializeDom()}get _appNode(){const t=document.querySelector(`[${e.ID}="${this._uuid}"]`);if(!t)throw new a(`There's no 'div' element with the attribute '${e.ID}' in the DOM. (Please add one)'`);return t}static getInstance(e){return null===l._instance&&(l._instance=new l(e)),l._instance}static navigate(e){if(!l._instance)throw new n("App instance not initialized. Call getInstance first.");const t=l._instance,i=t._pageManager.findPageByOriginalUrl(e);t._config.urlBasedRouting?location.hash=i.route.normalizedUrl:t._renderPage(i)}static createPage(e){const t=s.createInternalDefinition(e),i=s.createPublicDefinition(t);return s.storeInternalDefinition(i,t),i}addPages(e){const t=e.map((e=>{const t=s.getInternalDefinition(e);if(!t)throw new r("Invalid page definition");return t}));this._pageManager.registerPages(t),this._start()}_createAppContainer(){const e=document.createElement("div");return this._configureAppContainer(e),e}_configureAppContainer(t){t.setAttribute(e.ID,this._uuid)}_initializeDom(){const e=document.querySelector("body");if(!e)throw new r("Could not find a body node");let t=null;for(const i of e.children){if("div"===i.tagName.toLowerCase()&&""===i.innerHTML.trim()){t=i;break}}if(t)this._configureAppContainer(t);else{const t=this._createAppContainer();"header"===e.firstElementChild?.tagName.toLowerCase()?e.firstElementChild.insertAdjacentElement("afterend",t):e.insertBefore(t,e.firstChild)}}_initializeDefaultPage(){void 0===this._defaultPage&&(this._defaultPage=this._pageManager.findPageByOriginalUrl(this._config.defaultPage))}_start(){this._initializeDefaultPage(),this._config.urlBasedRouting?this._initializeRoutingFromUrl():this._renderPage(this._defaultPage)}_initializeRoutingFromUrl(){"/"===window.location.pathname&&(location.hash=location.hash||this._defaultPage.route.normalizedUrl,this._renderPageFromHash(),window.addEventListener("hashchange",(()=>{this._renderPageFromHash()})))}_renderPageFromHash(){try{const{hash:e}=i.parse();this._currentPage?.fragment.detachAllEvents();const t=this._pageManager.findPageByNormalizedUrl(e);t.route.search.initializeSearchParams(),this._renderPage(t)}catch(e){console.error(e),location.hash=this._defaultPage.route.normalizedUrl}}_renderPage(e){try{this._currentPage?.fragment.detachAllEvents(),this._appNode.innerHTML="",this._appNode.appendChild(e.fragment.fragment),e.fragment.attachAllEvents(),this._currentPage=e}catch(e){console.error(e)}}}export{l as App};