@paperbits/core
Version:
Paperbits core components.
173 lines (140 loc) • 5.9 kB
text/typescript
import * as ko from "knockout";
import * as Utils from "@paperbits/common";
import { NavigationItemModel } from "@paperbits/common/navigation";
export class NavigationItemViewModel {
public key: string;
public label: ko.Observable<string>;
public displayLabel: ko.Computed<string>;
public targetKey: ko.Observable<string>;
public targetUrl: ko.Observable<string>;
public targetWindow: ko.Observable<string>;
public triggerEvent: ko.Observable<string>;
public anchor: ko.Observable<string>;
public parent: NavigationItemViewModel;
public nodes: ko.ObservableArray<NavigationItemViewModel>;
public collapsed: ko.Observable<boolean>;
public dragged: ko.Observable<boolean>;
public onUpdate: ko.Subscribable<void>;
constructor(navitem: NavigationItemModel) {
this.moveNodeLeft = this.moveNodeLeft.bind(this);
this.moveNodeRight = this.moveNodeRight.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
this.canAccept = this.canAccept.bind(this);
this.insertBefore = this.insertBefore.bind(this);
this.insertAfter = this.insertAfter.bind(this);
this.key = navitem.key;
this.label = ko.observable<string>(navitem.label);
this.label.subscribe(() => this.notify());
this.displayLabel = ko.pureComputed(() => this.label() || "< No label >");
this.nodes = ko.observableArray<NavigationItemViewModel>([]);
this.collapsed = ko.observable<boolean>(false);
this.dragged = ko.observable<boolean>(false);
this.onUpdate = new ko.subscribable<void>();
this.targetKey = ko.observable<string>(navitem.targetKey);
this.targetKey.subscribe(() => this.notify());
this.targetUrl = ko.observable<string>(navitem.targetUrl);
this.targetWindow = ko.observable<string>(navitem.targetWindow);
this.targetWindow.subscribe(() => this.notify());
this.anchor = ko.observable<string>(navitem.anchor);
this.anchor.subscribe(() => this.notify());
this.triggerEvent = ko.observable<string>(navitem.triggerEvent);
this.triggerEvent.subscribe(() => this.notify());
}
private isSiblingNode(node: NavigationItemViewModel): boolean {
return this.parent && this.parent.nodes.indexOf(node) >= 0;
}
private isChildNode(node: NavigationItemViewModel): boolean {
return this.nodes.indexOf(node) >= 0;
}
private isUncleNode(node: NavigationItemViewModel): boolean {
return this.parent && this.parent.parent && this.parent.parent.nodes.indexOf(node) >= 0 && this.parent !== node;
}
public moveNodeLeft(): void {
if (!this.parent.parent) {
return;
}
this.parent.nodes.remove(this);
const ownIndex = this.parent.parent.nodes.indexOf(this.parent);
this.parent.parent.nodes.splice(ownIndex + 1, 0, this);
this.parent = this.parent.parent;
this.notify();
}
public moveNodeRight(): void {
const index = this.parent.nodes().indexOf(this);
if (index === 0) {
return;
}
const previousSibling = this.parent.nodes()[index - 1];
this.parent.nodes.remove(this);
this.parent = previousSibling;
previousSibling.nodes.push(this);
this.notify();
}
public canAccept(node: NavigationItemViewModel): boolean {
return this.isSiblingNode(node) || this.isChildNode(node) || this.isUncleNode(node);
}
public insertBefore(node: NavigationItemViewModel): void {
if (this.parent && this.isSiblingNode(node) || this.isUncleNode(node) || this.isChildNode(node)) {
node.parent.nodes.remove(node);
const ownIndex = this.parent.nodes.indexOf(this);
this.parent.nodes.splice(ownIndex, 0, node);
node.parent = this.parent;
this.notify();
}
}
public insertAfter(node: NavigationItemViewModel): void {
if (this.parent && this.isSiblingNode(node) || this.isUncleNode(node)) {
node.parent.nodes.remove(node);
const ownIndex = this.parent.nodes.indexOf(this);
this.parent.nodes.splice(ownIndex + 1, 0, node);
node.parent = this.parent;
this.notify();
}
if (this.parent && this.isChildNode(node)) {
node.parent.nodes.remove(node);
const ownIndex = this.parent.nodes.indexOf(this);
this.parent.nodes.splice(ownIndex, 0, node);
node.parent = this.parent;
this.notify();
}
}
public toggleCollapsed(): void {
this.collapsed(!this.collapsed());
}
public onKeyDown(event: KeyboardEvent): void {
// if (!this.hasFocus()) {
// return;
// }
switch (event.keyCode) {
case 37:
this.moveNodeLeft();
break;
case 39:
this.moveNodeRight();
break;
default:
}
}
public remove(): void {
this.parent.nodes.remove(this);
this.notify();
}
public addChild(): NavigationItemViewModel {
const navitemModel = new NavigationItemModel();
navitemModel.key = Utils.guid();
navitemModel.label = "< New >";
const node = new NavigationItemViewModel(navitemModel);
node.parent = this;
this.nodes.push(node);
return node;
}
private notify(): void {
this.onUpdate.notifySubscribers();
if (this.parent) {
this.parent.notify();
}
}
public toString(): string {
return this.label();
}
}