diff options
author | bobzel <zzzman@gmail.com> | 2023-01-25 12:59:50 -0500 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2023-01-25 12:59:50 -0500 |
commit | 1763ff090ea734ca275c3c28edc6c303c935b801 (patch) | |
tree | 88630593b7bcdf109fa04f427fe565ce234667d8 | |
parent | 555d67a31adbe7e6d42f7f7805ba1348e6d505f6 (diff) |
added a linkFollow o[ption to choose between opening target, or highest level collection containing target. fixed adding marker annotations to pdf/web/etc. fixed following link to wikipedia pages to not create a new document each time. made searchBox's search function static so that it can be called programmatically. Fixed LinkDocPreview to not flicker when doing a nopreview link follow. changed PlayTrail to restore state of all freeform collections containing source anchor.
-rw-r--r-- | src/client/util/DocumentManager.ts | 9 | ||||
-rw-r--r-- | src/client/util/LinkFollower.ts | 23 | ||||
-rw-r--r-- | src/client/views/MainView.tsx | 6 | ||||
-rw-r--r-- | src/client/views/PropertiesView.tsx | 12 | ||||
-rw-r--r-- | src/client/views/collections/CollectionDockingView.tsx | 9 | ||||
-rw-r--r-- | src/client/views/nodes/LinkDocPreview.tsx | 72 | ||||
-rw-r--r-- | src/client/views/nodes/trails/PresBox.tsx | 16 | ||||
-rw-r--r-- | src/client/views/pdf/AnchorMenu.tsx | 4 | ||||
-rw-r--r-- | src/client/views/search/SearchBox.tsx | 27 | ||||
-rw-r--r-- | src/fields/Types.ts | 90 |
10 files changed, 152 insertions, 116 deletions
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index aa09fb005..7c867d710 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -194,11 +194,16 @@ export class DocumentManager { return toReturn; } - static GetContextPath(doc: Opt<Doc>) { + static GetContextPath(doc: Opt<Doc>, includeExistingViews?: boolean) { if (!doc) return []; const srcContext = Cast(doc.context, Doc, null) ?? Cast(Cast(doc.annotationOn, Doc, null)?.context, Doc, null); var containerDocContext = srcContext ? [srcContext] : []; - while (containerDocContext.length && containerDocContext[0]?.context && DocCast(containerDocContext[0].context)?.viewType !== CollectionViewType.Docking && !DocumentManager.Instance.getDocumentView(containerDocContext[0])) { + while ( + containerDocContext.length && + containerDocContext[0]?.context && + DocCast(containerDocContext[0].context)?.viewType !== CollectionViewType.Docking && + (includeExistingViews || !DocumentManager.Instance.getDocumentView(containerDocContext[0])) + ) { containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext]; } return containerDocContext; diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts index 5bdfca54a..57618b53c 100644 --- a/src/client/util/LinkFollower.ts +++ b/src/client/util/LinkFollower.ts @@ -100,7 +100,7 @@ export class LinkFollower { : linkDoc.anchor1 ) as Doc; if (target) { - const doFollow = async (canToggle?: boolean) => { + const doFollow = (canToggle?: boolean) => { const options: DocFocusOptions = { playAudio: BoolCast(sourceDoc.followLinkAudio), toggleTarget: canToggle && BoolCast(sourceDoc.followLinkToggle), @@ -114,27 +114,14 @@ export class LinkFollower { zoomTextSelections: BoolCast(sourceDoc.followLinkZoomText), }; if (target.type === DocumentType.PRES) { - const containerAnnoDoc = Cast(sourceDoc, Doc, null); - const containerDoc = containerAnnoDoc || sourceDoc; - var containerDocContext = containerDoc?.context ? [Cast(await containerDoc?.context, Doc, null)] : ([] as Doc[]); - while (containerDocContext.length && containerDocContext[0]?.context && DocCast(containerDocContext[0].context)?.viewType !== CollectionViewType.Docking) { - containerDocContext = [Cast(await containerDocContext[0].context, Doc, null), ...containerDocContext]; - } - if (!DocumentManager.Instance.getDocumentView(containerDocContext[0])) { - CollectionDockingView.AddSplit(containerDocContext[0], OpenWhereMod.right); - } + const containerDocContext = DocumentManager.GetContextPath(sourceDoc, true); // gather all views that affect layout of sourceDoc so we can revert them after playing the rail SelectionManager.DeselectAll(); - DocumentManager.Instance.AddViewRenderedCb(target, dv => containerDocContext.length && (dv.ComponentView as PresBox).PlayTrail(containerDocContext[0])); + DocumentManager.Instance.AddViewRenderedCb(target, dv => containerDocContext.length && (dv.ComponentView as PresBox).PlayTrail(containerDocContext)); PresBox.OpenPresMinimized(target, [0, 0]); finished?.(); } else { - const containerAnnoDoc = Cast(target.annotationOn, Doc, null); - const containerDoc = containerAnnoDoc || target; - var containerDocContext = containerDoc?.context ? [Cast(await containerDoc?.context, Doc, null)] : ([] as Doc[]); - while (containerDocContext.length && containerDocContext[0]?.context && !DocumentManager.Instance.getDocumentView(containerDocContext[0]) && DocCast(containerDocContext[0].context)?.viewType !== CollectionViewType.Docking) { - containerDocContext = [Cast(await containerDocContext[0].context, Doc, null), ...containerDocContext]; - } - const targetContexts = LightboxView.LightboxDoc ? [containerAnnoDoc || containerDocContext[0]].filter(a => a) : containerDocContext; + const containerDocContext = DocumentManager.GetContextPath(target); + const targetContexts = !sourceDoc.followLinkToOuterContext && containerDocContext.length ? [containerDocContext.lastElement()] : containerDocContext; DocumentManager.Instance.jumpToDocument(target, options, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, OpenWhere.inPlace), finished), targetContexts, allFinished); } }; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 839db5795..895ed9bda 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -948,6 +948,10 @@ export class MainView extends React.Component { ); } + @computed get linkDocPreview() { + return LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : null; + } + render() { return ( <div @@ -975,7 +979,7 @@ export class MainView extends React.Component { {this._hideUI ? null : <TopBar />} {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null} {DocumentLinksButton.LinkEditorDocView ? <LinkMenu clearLinkEditor={action(() => (DocumentLinksButton.LinkEditorDocView = undefined))} docView={DocumentLinksButton.LinkEditorDocView} /> : null} - {LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : null} + {this.linkDocPreview} {((page: string) => { // prettier-ignore diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index af50478b0..411f51d84 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -1,6 +1,6 @@ import React = require('react'); import { IconLookup } from '@fortawesome/fontawesome-svg-core'; -import { faAnchor, faArrowRight } from '@fortawesome/free-solid-svg-icons'; +import { faAnchor, faArrowRight, faWindowMaximize } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Checkbox, Tooltip } from '@material-ui/core'; import { intersection } from 'lodash'; @@ -1690,6 +1690,16 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { </button> </div> <div className="propertiesView-input inline"> + <p>Toggle Follow to Outer Context</p> + <button + style={{ background: !this.sourceAnchor?.followLinkToOuterContext ? '' : '#4476f7', borderRadius: 3 }} + onPointerDown={e => this.toggleAnchorProp(e, 'followLinkToOuterContext', this.sourceAnchor)} + onClick={e => e.stopPropagation()} + className="propertiesButton"> + <FontAwesomeIcon className="fa-icon" icon={faWindowMaximize as IconLookup} size="lg" /> + </button> + </div> + <div className="propertiesView-input inline"> <p>Toggle Target (Show/Hide)</p> <button style={{ background: !this.sourceAnchor?.followLinkToggle ? '' : '#4476f7', borderRadius: 3 }} diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index ffc004df6..1ead80bd0 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -156,10 +156,13 @@ export class CollectionDockingView extends CollectionSubView() { } const tab = Array.from(instance.tabMap.keys()).find(tab => tab.contentItem.config.props.panelName === panelName); if (tab) { - tab.header.parent.addChild(newConfig, undefined); const j = tab.header.parent.contentItems.indexOf(tab.contentItem); - !addToSplit && j !== -1 && tab.header.parent.contentItems[j].remove(); - return instance.layoutChanged(); + if (newConfig.props.documentId !== tab.header.parent.contentItems[j].config.props.documentId) { + tab.header.parent.addChild(newConfig, undefined); + !addToSplit && j !== -1 && tab.header.parent.contentItems[j].remove(); + return instance.layoutChanged(); + } + return false; } return CollectionDockingView.AddSplit(document, panelName, stack, panelName); } diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index 7eb42994b..1eab06381 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -4,7 +4,7 @@ import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import wiki from 'wikijs'; import { Doc, DocCastAsync, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; -import { Cast, NumCast, StrCast } from '../../../fields/Types'; +import { Cast, DocCast, NumCast, PromiseValue, StrCast } from '../../../fields/Types'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnNone, setupMoveUpEvents } from '../../../Utils'; import { DocServer } from '../../DocServer'; import { Docs, DocUtils } from '../../documents/Documents'; @@ -17,6 +17,8 @@ import { Transform } from '../../util/Transform'; import { DocumentView, DocumentViewSharedProps, OpenWhere } from './DocumentView'; import './LinkDocPreview.scss'; import React = require('react'); +import { SearchUtil } from '../../util/SearchUtil'; +import { SearchBox } from '../search/SearchBox'; interface LinkDocPreviewProps { linkDoc?: Doc; @@ -36,6 +38,8 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { LinkDocPreview.LinkInfo !== info && (LinkDocPreview.LinkInfo = info); } + static _instance: Opt<LinkDocPreview>; + _infoRef = React.createRef<HTMLDivElement>(); _linkDocRef = React.createRef<HTMLDivElement>(); @observable public static LinkInfo: Opt<LinkDocPreviewProps>; @@ -46,6 +50,11 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { @observable _toolTipText = ''; @observable _hrefInd = 0; + constructor(props: any) { + super(props); + LinkDocPreview._instance = this; + } + @action init() { var linkTarget = this.props.linkDoc; this._linkSrc = this.props.linkSrc; @@ -62,6 +71,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { this._markerTargetDoc = this._targetDoc = linkTarget; } this._toolTipText = ''; + this.updateHref(); } componentDidUpdate(props: any) { if (props.linkSrc !== this.props.linkSrc || props.linkDoc !== this.props.linkDoc || props.hrefs !== this.props.hrefs) this.init(); @@ -71,6 +81,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { document.addEventListener('pointerdown', this.onPointerDown, true); } + @action componentWillUnmount() { LinkDocPreview.SetLinkInfo(undefined); document.removeEventListener('pointerdown', this.onPointerDown, true); @@ -80,7 +91,8 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { !this._linkDocRef.current?.contains(e.target as any) && LinkDocPreview.Clear(); // close preview when not clicking anywhere other than the info bar of the preview }; - @computed get href() { + @action + updateHref() { if (this.props.hrefs?.length) { const href = this.props.hrefs[this._hrefInd]; if (href.indexOf(Doc.localServerPath()) !== 0) { @@ -90,33 +102,33 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { .page(href.replace('https://en.wikipedia.org/wiki/', '')) .then(page => page.summary().then(action(summary => (this._toolTipText = summary.substring(0, 500))))); } else { - setTimeout(action(() => (this._toolTipText = 'url => ' + href))); + this._toolTipText = 'url => ' + href; } } else { // hyperlink to a document .. decode doc id and retrieve from the server. this will trigger vals() being invalidated - const anchorDoc = href.replace(Doc.localServerPath(), '').split('?')[0]; - anchorDoc && - DocServer.GetRefField(anchorDoc).then( - action(anchor => { - if (anchor instanceof Doc && DocListCast(anchor.links).length) { - this._linkDoc = this._linkDoc ?? DocListCast(anchor.links)[0]; - const automaticLink = this._linkDoc.linkRelationship === LinkManager.AutoKeywords; - if (automaticLink) { - // automatic links specify the target in the link info, not the source - const linkTarget = anchor; - this._linkSrc = LinkManager.getOppositeAnchor(this._linkDoc, linkTarget); - this._markerTargetDoc = this._targetDoc = linkTarget; - } else { - this._linkSrc = anchor; - const linkTarget = LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc); - this._markerTargetDoc = linkTarget; - this._targetDoc = /*linkTarget?.type === DocumentType.MARKER &&*/ linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget; - } - this._toolTipText = ''; - if (LinkDocPreview.LinkInfo?.noPreview || this._linkSrc?.followLinkToggle || this._markerTargetDoc?.type === DocumentType.PRES) this.followLink(); + const anchorDocId = href.replace(Doc.localServerPath(), '').split('?')[0]; + const anchorDoc = anchorDocId ? PromiseValue(DocCast(DocServer.GetCachedRefField(anchorDocId) ?? DocServer.GetRefField(anchorDocId))) : undefined; + anchorDoc?.then?.( + action(anchor => { + if (anchor instanceof Doc && DocListCast(anchor.links).length) { + this._linkDoc = this._linkDoc ?? DocListCast(anchor.links)[0]; + const automaticLink = this._linkDoc.linkRelationship === LinkManager.AutoKeywords; + if (automaticLink) { + // automatic links specify the target in the link info, not the source + const linkTarget = anchor; + this._linkSrc = LinkManager.getOppositeAnchor(this._linkDoc, linkTarget); + this._markerTargetDoc = this._targetDoc = linkTarget; + } else { + this._linkSrc = anchor; + const linkTarget = LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc); + this._markerTargetDoc = linkTarget; + this._targetDoc = /*linkTarget?.type === DocumentType.MARKER &&*/ linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget; } - }) - ); + this._toolTipText = 'link to ' + this._targetDoc?.title; + if (LinkDocPreview.LinkInfo?.noPreview || this._linkSrc?.followLinkToggle || this._markerTargetDoc?.type === DocumentType.PRES) this.followLink(); + } + }) + ); } return href; } @@ -158,11 +170,14 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { }; followLink = () => { + LinkDocPreview.Clear(); if (this._linkDoc && this._linkSrc) { - LinkDocPreview.Clear(); LinkFollower.FollowLink(this._linkDoc, this._linkSrc, this.props.docProps, false); } else if (this.props.hrefs?.length) { - this.props.docProps?.addDocTab(Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, useCors: true }), OpenWhere.addRight); + const webDoc = + Array.from(SearchBox.staticSearchCollection(Doc.MyFilesystem, this.props.hrefs[0]).keys()).lastElement() ?? + Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, useCors: true }); + this.props.docProps?.addDocTab(webDoc, OpenWhere.lightbox); } }; @@ -208,7 +223,6 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { } @computed get docPreview() { - const href = this.href; // needs to be here to trigger lookup of web pages and docs on server return (!this._linkDoc || !this._targetDoc || !this._linkSrc) && !this._toolTipText ? null : ( <div className="linkDocPreview-inner"> {!this.props.showHeader ? null : this.previewHeader} @@ -285,7 +299,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { className="linkDocPreview" ref={this._linkDocRef} onPointerDown={this.followLinkPointerDown} - style={{ left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}> + style={{ display: !this._toolTipText ? 'none' : undefined, left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}> {this.docPreview} </div> ); diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 4a60c99ec..ff05dcdcb 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -624,14 +624,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { }; _exitTrail: Opt<() => void>; - PlayTrail = (doc: Doc) => { - const savedState = { c: doc, x: NumCast(doc.panX), y: NumCast(doc.panY), s: NumCast(doc.viewScale) }; + PlayTrail = (docs: Doc[]) => { + const savedStates = docs.map(doc => (doc._viewType !== CollectionViewType.Freeform ? undefined : { c: doc, x: NumCast(doc.panX), y: NumCast(doc.panY), s: NumCast(doc.viewScale) })); this.startPresentation(0); this._exitTrail = () => { - const { x, y, s, c } = savedState; - c._panX = x; - c._panY = y; - c._viewScale = s; + savedStates + .filter(savedState => savedState) + .map(savedState => { + const { x, y, s, c } = savedState!; + c._panX = x; + c._panY = y; + c._viewScale = s; + }); LightboxView.SetLightboxDoc(undefined); Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, this.rootDoc); return PresStatus.Edit; diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index 9af686d83..c53cc608c 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -50,7 +50,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { public OnAudio: (e: PointerEvent) => void = unimplementedFunction; public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction; public StartCropDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction; - public Highlight: (color: string, isTargetToggler: boolean) => Opt<Doc> = (color: string, isTargetToggler: boolean) => undefined; + public Highlight: (color: string, isTargetToggler: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => Opt<Doc> = (color: string, isTargetToggler: boolean) => undefined; public GetAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => undefined; public Delete: () => void = unimplementedFunction; public PinToPres: () => void = unimplementedFunction; @@ -120,7 +120,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { @action highlightClicked = (e: React.MouseEvent) => { - if (!this.Highlight(this.highlightColor, false) && this.Pinned) { + if (!this.Highlight(this.highlightColor, false, undefined, true) && this.Pinned) { this.Highlighting = !this.Highlighting; } AnchorMenu.Instance.fadeOut(true); diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 7e2a5a237..4c4275ce7 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -2,7 +2,7 @@ import { Tooltip } from '@material-ui/core'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field } from '../../../fields/Doc'; +import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field, Opt } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { DocCast, StrCast } from '../../../fields/Types'; import { StopEvent } from '../../../Utils'; @@ -205,6 +205,13 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() { */ @action searchCollection(query: string) { + this._selectedResult = undefined; + this._results = SearchBox.staticSearchCollection(CollectionDockingView.Instance?.rootDoc, query); + + this.computePageRanks(); + } + @action + static staticSearchCollection(rootDoc: Opt<Doc>, query: string) { const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.KVP, DocumentType.FILTER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING]; const blockedKeys = [ 'x', @@ -240,18 +247,15 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() { 'panY', 'viewScale', ]; - const collection = CollectionDockingView.Instance; query = query.toLowerCase(); - this._results.clear(); - this._selectedResult = undefined; - - if (collection !== undefined) { - const docs = DocListCast(collection.rootDoc[Doc.LayoutFieldKey(collection.rootDoc)]); + const results = new Map<Doc, string[]>(); + if (rootDoc) { + const docs = DocListCast(rootDoc[Doc.LayoutFieldKey(rootDoc)]); const docIDs: String[] = []; SearchBox.foreachRecursiveDoc(docs, (depth: number, doc: Doc) => { - const dtype = StrCast(doc.type, 'string') as DocumentType; - if (dtype && !blockedTypes.includes(dtype) && !docIDs.includes(doc[Id]) && depth > 0) { + const dtype = StrCast(doc.type) as DocumentType; + if (dtype && !blockedTypes.includes(dtype) && !docIDs.includes(doc[Id]) && depth >= 0) { const hlights = new Set<string>(); SearchBox.documentKeys(doc).forEach( key => @@ -262,14 +266,13 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() { blockedKeys.forEach(key => hlights.delete(key)); if (Array.from(hlights.keys()).length > 0) { - this._results.set(doc, Array.from(hlights.keys())); + results.set(doc, Array.from(hlights.keys())); } } docIDs.push(doc[Id]); }); } - - this.computePageRanks(); + return results; } /** diff --git a/src/fields/Types.ts b/src/fields/Types.ts index bf40a0d7b..3ef7cb1de 100644 --- a/src/fields/Types.ts +++ b/src/fields/Types.ts @@ -1,48 +1,50 @@ -import { Field, Opt, FieldResult, Doc } from "./Doc"; -import { List } from "./List"; -import { RefField } from "./RefField"; -import { DateField } from "./DateField"; -import { ScriptField } from "./ScriptField"; -import { URLField, WebField, ImageField } from "./URLField"; -import { TextField } from "@material-ui/core"; -import { RichTextField } from "./RichTextField"; - -export type ToType<T extends InterfaceValue> = - T extends "string" ? string : - T extends "number" ? number : - T extends "boolean" ? boolean : - T extends ListSpec<infer U> ? List<U> : - // T extends { new(...args: any[]): infer R } ? (R | Promise<R>) : never; - T extends DefaultFieldConstructor<infer _U> ? never : - T extends { new(...args: any[]): List<Field> } ? never : - T extends { new(...args: any[]): infer R } ? R : - T extends (doc?: Doc) => infer R ? R : never; - -export type ToConstructor<T extends Field> = - T extends string ? "string" : - T extends number ? "number" : - T extends boolean ? "boolean" : - T extends List<infer U> ? ListSpec<U> : - new (...args: any[]) => T; +import { Field, Opt, FieldResult, Doc } from './Doc'; +import { List } from './List'; +import { RefField } from './RefField'; +import { DateField } from './DateField'; +import { ScriptField } from './ScriptField'; +import { URLField, WebField, ImageField } from './URLField'; +import { TextField } from '@material-ui/core'; +import { RichTextField } from './RichTextField'; + +export type ToType<T extends InterfaceValue> = T extends 'string' + ? string + : T extends 'number' + ? number + : T extends 'boolean' + ? boolean + : T extends ListSpec<infer U> + ? List<U> + : // T extends { new(...args: any[]): infer R } ? (R | Promise<R>) : never; + T extends DefaultFieldConstructor<infer _U> + ? never + : T extends { new (...args: any[]): List<Field> } + ? never + : T extends { new (...args: any[]): infer R } + ? R + : T extends (doc?: Doc) => infer R + ? R + : never; + +export type ToConstructor<T extends Field> = T extends string ? 'string' : T extends number ? 'number' : T extends boolean ? 'boolean' : T extends List<infer U> ? ListSpec<U> : new (...args: any[]) => T; export type ToInterface<T extends Interface> = { - [P in Exclude<keyof T, "proto">]: T[P] extends DefaultFieldConstructor<infer F> ? Exclude<FieldResult<F>, undefined> : FieldResult<ToType<T[P]>>; + [P in Exclude<keyof T, 'proto'>]: T[P] extends DefaultFieldConstructor<infer F> ? Exclude<FieldResult<F>, undefined> : FieldResult<ToType<T[P]>>; }; // type ListSpec<T extends Field[]> = { List: ToContructor<Head<T>> | ListSpec<Tail<T>> }; export type ListSpec<T extends Field> = { List: ToConstructor<T> }; export type DefaultFieldConstructor<T extends Field> = { - type: ToConstructor<T>, - defaultVal: T + type: ToConstructor<T>; + defaultVal: T; }; // type ListType<U extends Field[]> = { 0: List<ListType<Tail<U>>>, 1: ToType<Head<U>> }[HasTail<U> extends true ? 0 : 1]; export type Head<T extends any[]> = T extends [any, ...any[]] ? T[0] : never; -export type Tail<T extends any[]> = - ((...t: T) => any) extends ((_: any, ...tail: infer TT) => any) ? TT : []; -export type HasTail<T extends any[]> = T extends ([] | [any]) ? false : true; +export type Tail<T extends any[]> = ((...t: T) => any) extends (_: any, ...tail: infer TT) => any ? TT : []; +export type HasTail<T extends any[]> = T extends [] | [any] ? false : true; export type InterfaceValue = ToConstructor<Field> | ListSpec<Field> | DefaultFieldConstructor<Field> | ((doc?: Doc) => any); //TODO Allow you to optionally specify default values for schemas, which should then make that field not be partial @@ -58,14 +60,14 @@ export function Cast<T extends CastCtor>(field: FieldResult, ctor: T): FieldResu export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal: WithoutList<WithoutRefField<ToType<T>>> | null): WithoutList<ToType<T>>; export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal?: ToType<T> | null): FieldResult<ToType<T>> | undefined { if (field instanceof Promise) { - return defaultVal === undefined ? field.then(f => Cast(f, ctor) as any) as any : defaultVal === null ? undefined : defaultVal; + return defaultVal === undefined ? (field.then(f => Cast(f, ctor) as any) as any) : defaultVal === null ? undefined : defaultVal; } if (field !== undefined && !(field instanceof Promise)) { - if (typeof ctor === "string") { + if (typeof ctor === 'string') { if (typeof field === ctor) { return field as ToType<T>; } - } else if (typeof ctor === "object") { + } else if (typeof ctor === 'object') { if (field instanceof List) { return field as any; } @@ -81,15 +83,15 @@ export function DocCast(field: FieldResult, defaultVal?: Doc) { } export function NumCast(field: FieldResult, defaultVal: number | null = 0) { - return Cast(field, "number", defaultVal); + return Cast(field, 'number', defaultVal); } -export function StrCast(field: FieldResult, defaultVal: string | null = "") { - return Cast(field, "string", defaultVal); +export function StrCast(field: FieldResult, defaultVal: string | null = '') { + return Cast(field, 'string', defaultVal); } export function BoolCast(field: FieldResult, defaultVal: boolean | null = false) { - return Cast(field, "boolean", defaultVal); + return Cast(field, 'boolean', defaultVal); } export function DateCast(field: FieldResult) { return Cast(field, DateField, null); @@ -113,7 +115,7 @@ type WithoutList<T extends Field> = T extends List<infer R> ? (R extends RefFiel export function FieldValue<T extends Field, U extends WithoutList<T>>(field: FieldResult<T>, defaultValue: U): WithoutList<T>; export function FieldValue<T extends Field>(field: FieldResult<T>): Opt<T>; export function FieldValue<T extends Field>(field: FieldResult<T>, defaultValue?: T): Opt<T> { - return (field instanceof Promise || field === undefined) ? defaultValue : field; + return field instanceof Promise || field === undefined ? defaultValue : field; } export interface PromiseLike<T> { @@ -121,5 +123,9 @@ export interface PromiseLike<T> { } export function PromiseValue<T extends Field>(field: FieldResult<T>): PromiseLike<Opt<T>> { if (field instanceof Promise) return field as Promise<Opt<T>>; - return { then(cb: ((field: Opt<T>) => void)) { return cb(field); } }; -}
\ No newline at end of file + return { + then(cb: (field: Opt<T>) => void) { + return cb(field); + }, + }; +} |