UNPKG

@paperbits/core

Version:
178 lines (141 loc) 6.69 kB
import * as ko from "knockout"; import template from "./pageHost.html"; import { Contract } from "@paperbits/common"; import { EventManager } from "@paperbits/common/events"; import { Component, OnDestroyed, OnMounted, Param } from "@paperbits/common/ko/decorators"; import { ILayoutService } from "@paperbits/common/layouts"; import { IPageService } from "@paperbits/common/pages"; import { Route, Router } from "@paperbits/common/routing"; import { StyleCompiler, StyleManager } from "@paperbits/common/styles"; import { ViewManager, ViewManagerMode } from "@paperbits/common/ui"; import { ContentViewModel, ContentViewModelBinder } from "../../../content/ko"; import { PopupHostViewModelBinder } from "../../../popup/ko/popupHostViewModelBinder"; import { PopupHost } from "../../../popup/ko/popupHost"; @Component({ selector: "page-host", template: template }) export class PageHost { private readonly styleManager: StyleManager; public readonly contentViewModel: ko.Observable<ContentViewModel>; public readonly popupHostViewModel: ko.Observable<PopupHost>; constructor( private readonly contentViewModelBinder: ContentViewModelBinder, private readonly popupHostViewModelBinder: PopupHostViewModelBinder, private readonly router: Router, private readonly eventManager: EventManager, private readonly viewManager: ViewManager, private readonly layoutService: ILayoutService, private readonly pageService: IPageService, private readonly styleCompiler: StyleCompiler ) { this.contentViewModel = ko.observable(); this.popupHostViewModel = ko.observable(); this.pageKey = ko.observable(); this.styleManager = new StyleManager(this.eventManager); } @Param() public pageKey: ko.Observable<string>; @OnMounted() public async initialize(): Promise<void> { await this.refreshContent(); this.router.addRouteChangeListener(this.onRouteChange); this.eventManager.addEventListener("onDataPush", () => this.onDataPush()); this.eventManager.addEventListener("onLocaleChange", () => this.onLocaleUpdate()); this.eventManager.addEventListener("onPopupUpdate", () => this.onPopupUpdate()); } /** * This event occurs when data gets pushed to the storage. For example, "Undo" command restores the previous state. */ private async onDataPush(): Promise<void> { if (this.viewManager.mode === ViewManagerMode.selecting || this.viewManager.mode === ViewManagerMode.selected) { await this.refreshContent(); } } private async onLocaleUpdate(): Promise<void> { await this.refreshContent(); } private async onPopupUpdate(): Promise<void> { await this.refreshPopupContent(); } private async refreshContent(): Promise<void> { this.viewManager.setShutter(); const route = this.router.getCurrentRoute(); let pageContract = await this.pageService.getPageByPermalink(route.path); if (!pageContract) { pageContract = await this.pageService.getPageByPermalink("/404"); if (!pageContract) { this.viewManager.removeShutter(); return; } } const pageContentContract = await this.pageService.getPageContent(pageContract.key); this.pageKey(pageContract.key); const styleSheet = await this.styleCompiler.getStyleSheet(); this.styleManager.setStyleSheet(styleSheet); const activeLayer = "page"; this.viewManager.setActiveLayer(activeLayer); const pageBindingContext = { contentItemKey: pageContract.key, styleManager: this.styleManager, navigationPath: route.path, activeLayer: activeLayer, template: { page: { value: pageContentContract, onValueUpdate: async (updatedContentContract: Contract, changeDescription: string) => { await this.pageService.updatePageContent(pageContract.key, updatedContentContract, null, changeDescription); } } } }; const layoutContract = await this.layoutService.getLayoutByPermalink(route.path); if (!layoutContract) { throw new Error(`No matching layouts found for page with permalink "${route.path}".`); } const layoutContentContract = await this.layoutService.getLayoutContent(layoutContract.key); const layoutContentViewModel = await this.contentViewModelBinder.contractToViewModel(layoutContentContract, pageBindingContext); layoutContentViewModel["widgetBinding"].provides = ["html", "js", "interaction"]; this.contentViewModel(layoutContentViewModel); await this.refreshPopupContent(); this.viewManager.removeShutter(); this.jumpToAnchor(route); } private async refreshPopupContent(): Promise<void> { const route = this.router.getCurrentRoute(); const popupBindingContext = { styleManager: this.styleManager, navigationPath: route.path, contentType: "popup", layer: "page", getHostedDocument: () => { return this.viewManager.getHostDocument(); } }; const popupHostViewModel = await this.popupHostViewModelBinder.contractToViewModel(popupBindingContext); popupHostViewModel["widgetBinding"].provides = ["html", "js", "interaction"]; this.popupHostViewModel(popupHostViewModel); } private async onRouteChange(route: Route): Promise<void> { if (route.previous && route.previous.path === route.path) { return; } await this.refreshContent(); } private jumpToAnchor(route: Route): void { const hostDocument = this.viewManager.getHostDocument(); if (!route.hash) { hostDocument.defaultView.scrollTo({ top: 0 }); return; } const anchorElementSelector = `[id="${route.hash}"]`; // Hash may not be proper "id" selector. const anchorElement = hostDocument.querySelector(anchorElementSelector); if (anchorElement) { anchorElement.scrollIntoView(); } } @OnDestroyed() public dispose(): void { this.router.removeRouteChangeListener(this.onRouteChange); } }