@paperbits/core
Version:
Paperbits core components.
163 lines (125 loc) • 5.27 kB
text/typescript
import * as ko from "knockout";
import template from "./pageSelector.html";
import { IResourceSelector } from "@paperbits/common/ui";
import { PageItem, AnchorItem } from "./pageItem";
import { IPageService, PageContract } from "@paperbits/common/pages";
import { Component, Param, Event, OnMounted } from "@paperbits/common/ko/decorators";
import { HyperlinkModel } from "@paperbits/common/permalinks";
import { AnchorUtils } from "../../../text/anchorUtils";
import { ChangeRateLimit } from "@paperbits/common/ko/consts";
import { Query, Operator, Page } from "@paperbits/common/persistence";
export class PageSelector implements IResourceSelector<HyperlinkModel> {
private preSelectedModel: HyperlinkModel;
private currentPage: Page<PageContract>;
public readonly searchPattern: ko.Observable<string>;
public readonly pages: ko.ObservableArray<PageItem>;
public readonly working: ko.Observable<boolean>;
public selectedPage: ko.Observable<PageItem>;
public onSelect: (selection: PageContract) => void;
public onHyperlinkSelect: (selection: HyperlinkModel) => void;
constructor(private readonly pageService: IPageService) {
this.pages = ko.observableArray();
this.selectedPage = ko.observable();
this.searchPattern = ko.observable("");
this.working = ko.observable();
}
public async onMounted(): Promise<void> {
await this.searchPages();
this.searchPattern
.extend(ChangeRateLimit)
.subscribe(this.searchPages);
}
public async searchPages(searchPattern: string = ""): Promise<void> {
this.working(true);
this.pages([]);
const query = Query
.from<PageContract>()
.orderBy(`title`);
if (searchPattern) {
query.where(`title`, Operator.contains, searchPattern);
}
const pageOfResults = await this.pageService.search(query);
this.currentPage = pageOfResults;
const pageItems = pageOfResults.value.map(page => new PageItem(page));
this.pages.push(...pageItems);
this.working(false);
}
public async loadNextPage(): Promise<void> {
if (!this.currentPage?.takeNext) {
return;
}
this.working(true);
this.currentPage = await this.currentPage.takeNext();
const pageItems = this.currentPage.value.map(page => new PageItem(page));
this.pages.push(...pageItems);
if (!this.selectedPage() && this.preSelectedModel) {
const currentPermalink = this.preSelectedModel.href;
const current = pageItems.find(item => item.permalink() === currentPermalink);
if (current) {
await this.selectPage(current);
const currentAnchors = current.anchors();
if (this.preSelectedModel.anchor) {
const currentAnchor = currentAnchors.find(item => item.elementId === this.preSelectedModel.anchor);
if (currentAnchor) {
await this.selectAnchor(currentAnchor);
}
}
}
}
this.working(false);
}
public async selectPage(page: PageItem): Promise<void> {
this.selectedPage(page);
if (!page.anchorsLoaded()) { // expand anchors on first click
const anchors = await this.getAnchors(page);
page.anchors(anchors);
page.anchorsLoaded(true);
if (anchors.length > 0) {
return;
}
}
if (this.onSelect) {
this.onSelect(page.toContract());
}
if (this.onHyperlinkSelect) {
this.onHyperlinkSelect(page.getHyperlink());
}
}
public selectResource(resource: HyperlinkModel): void {
this.preSelectedModel = resource;
}
public async selectAnchor(anchor: AnchorItem): Promise<void> {
if (!this.onHyperlinkSelect) {
return;
}
const selectedPage = this.selectedPage();
anchor.isSelected(true);
selectedPage.selectedAnchor = anchor;
this.onHyperlinkSelect(selectedPage.getHyperlink());
}
private async getAnchors(pageItem: PageItem): Promise<AnchorItem[]> {
const pageContent = await this.pageService.getPageContent(pageItem.key);
const children = AnchorUtils.getHeadingNodes(pageContent, 1, 6);
const anchors = children
.filter(item => item.nodes?.length > 0)
.map(item => {
const anchor = new AnchorItem();
anchor.shortTitle = item.nodes[0]?.text;
anchor.elementId = item.identifier || item.attrs?.id;
return anchor;
});
return anchors;
}
public isSelected(page: PageItem): boolean {
const selectedPage = this.selectedPage();
return selectedPage?.key === page.key;
}
}