UNPKG

@paperbits/core

Version:
163 lines (125 loc) 5.27 kB
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"; @Component({ selector: "page-selector", template: template }) 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>; @Param() public selectedPage: ko.Observable<PageItem>; @Event() public onSelect: (selection: PageContract) => void; @Event() 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(); } @OnMounted() 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; } }