@paperbits/core
Version:
Paperbits core components.
170 lines (136 loc) • 6.17 kB
text/typescript
import * as ko from "knockout";
import template from "./navigation.html";
import { INavigationService, NavigationEvents } from "@paperbits/common/navigation";
import { ViewManager, View } from "@paperbits/common/ui";
import { NavigationItemContract } from "@paperbits/common/navigation/navigationItemContract";
import { NavigationItemViewModel } from "./navigationItemViewModel";
import { Component, OnMounted, OnDestroyed } from "@paperbits/common/ko/decorators";
import { NavigationModelBinder } from "../navigationModelBinder";
import { NavigationViewModelBinder } from "./navigationViewModelBinder";
import { EventManager, Events } from "@paperbits/common/events";
export class NavigationWorkshop {
public readonly selection: ko.Observable<NavigationItemViewModel>;
public readonly root: ko.Observable<NavigationItemViewModel>;
public readonly working: ko.Observable<boolean>;
private placeholderElement: HTMLElement;
constructor(
private readonly navigationService: INavigationService,
private readonly viewManager: ViewManager,
private readonly navigationModelBinder: NavigationModelBinder,
private readonly navigationViewModelBinder: NavigationViewModelBinder,
private readonly eventManager: EventManager
) {
// rebinding...
this.onNavigationUpdate = this.onNavigationUpdate.bind(this);
this.selectNavigationItem = this.selectNavigationItem.bind(this);
this.selection = ko.observable<NavigationItemViewModel>();
this.working = ko.observable(true);
this.root = ko.observable();
this.placeholderElement = document.createElement("div");
this.placeholderElement.className = "placeholder";
this.placeholderElement.onmousemove = this.onNullPointerMove;
document.addEventListener(Events.KeyDown, this.onKeyDown.bind(this), false);
}
public async searchNavigationItems(): Promise<void> {
this.working(true);
const navigationItems = await this.navigationService.getNavigationItems();
const root: NavigationItemContract = {
key: null,
label: "",
navigationItems: navigationItems
};
const rootModel = await this.navigationModelBinder.contractToModel(root);
const rootViewModel = await this.navigationViewModelBinder.modelToViewModel(rootModel);
this.root(rootViewModel);
this.working(false);
rootViewModel.onUpdate.subscribe(this.onNavigationUpdate);
this.eventManager.dispatchEvent(Events.HintRequest, {
key: "b300",
content: `You can manage the navigation structure of the entire website in one place. Specific nodes of this structure can be assigned to navigation widgets like a Menu.`
});
}
public async onNavigationUpdate(): Promise<void> {
const rootModel = this.navigationViewModelBinder.viewModelToModel(this.root());
const rootContract = this.navigationModelBinder.modelToContract(rootModel);
await this.navigationService.updateNavigation(rootContract.navigationItems);
rootModel.nodes.forEach(navigationItem => {
this.eventManager.dispatchEvent(NavigationEvents.onNavigationItemUpdate, navigationItem);
});
}
public addNavigationItem(): void {
const currentNode = this.selection() || this.root();
const node = currentNode.addChild();
this.selectNavigationItem(node);
}
public async selectNavigationItem(navigationItem: NavigationItemViewModel): Promise<void> {
this.selection(navigationItem);
const view: View = {
heading: "Navigation item",
component: {
name: "navigation-details-workshop",
params: {
navigationItem: navigationItem,
onDelete: () => {
this.selection(navigationItem.parent);
}
}
}
};
this.viewManager.openViewAsWorkshop(view);
}
private onNullPointerMove(event: MouseEvent): void {
event.stopPropagation();
}
public onNodeDragStart(sourceData: any, node: HTMLElement): void {
const width = node.clientWidth + "px";
const height = node.clientHeight + "px";
this.placeholderElement.style.width = width;
this.placeholderElement.style.height = height;
node.parentNode.insertBefore(this.placeholderElement, node.nextSibling);
}
/**
* Removes placehoder element.
*/
public onNodeDragEnd(): void {
this.placeholderElement.parentElement.removeChild(this.placeholderElement);
}
/**
* Places placeholder element before target.
*/
public onAcceptNodeBefore(node: HTMLElement, acceptingNode: HTMLElement): void {
acceptingNode.parentNode.insertBefore(this.placeholderElement, acceptingNode);
}
/**
* Places placeholder element after target.
*/
public onAcceptNodeAfter(node: HTMLElement, acceptingNode: HTMLElement): void {
acceptingNode.parentNode.insertBefore(this.placeholderElement, acceptingNode.nextSibling);
}
public onKeyDown(event: KeyboardEvent): void {
if (!this.selection()) {
return;
}
switch (event.keyCode) {
case 37:
this.selection().moveNodeLeft();
break;
case 39:
this.selection().moveNodeRight();
break;
default:
}
}
public dispose(): void {
document.removeEventListener(Events.KeyDown, this.onKeyDown.bind(this), false);
}
public isSelected(page: NavigationItemViewModel): boolean {
const selectedNavItem = this.selection();
return selectedNavItem?.key === page.key;
}
}