diff options
Diffstat (limited to 'src/client/util')
| -rw-r--r-- | src/client/util/DocumentManager.ts | 150 | ||||
| -rw-r--r-- | src/client/util/DragManager.ts | 7 | ||||
| -rw-r--r-- | src/client/util/DropConverter.ts | 5 | ||||
| -rw-r--r-- | src/client/util/InteractionUtils.tsx | 14 | ||||
| -rw-r--r-- | src/client/util/RichTextSchema.tsx | 7 | ||||
| -rw-r--r-- | src/client/util/Scripting.ts | 16 | ||||
| -rw-r--r-- | src/client/util/SearchUtil.ts | 4 | ||||
| -rw-r--r-- | src/client/util/SelectionManager.ts | 8 |
8 files changed, 102 insertions, 109 deletions
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index e0ffaf7e0..3d5306841 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,16 +1,16 @@ import { action, computed, observable } from 'mobx'; import { Doc, DocListCastAsync, DocListCast, Opt } from '../../new_fields/Doc'; import { Id } from '../../new_fields/FieldSymbols'; -import { List } from '../../new_fields/List'; import { Cast, NumCast, StrCast } from '../../new_fields/Types'; import { CollectionDockingView } from '../views/collections/CollectionDockingView'; import { CollectionView } from '../views/collections/CollectionView'; -import { DocumentView } from '../views/nodes/DocumentView'; +import { DocumentView, DocFocusFunc } from '../views/nodes/DocumentView'; import { LinkManager } from './LinkManager'; import { Scripting } from './Scripting'; import { SelectionManager } from './SelectionManager'; import { DocumentType } from '../documents/DocumentTypes'; +export type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => void) => void; export class DocumentManager { @@ -84,24 +84,20 @@ export class DocumentManager { return this.getDocumentViewById(toFind[Id], preferredCollection); } - public getFirstDocumentView(toFind: Doc, originatingDoc: Opt<Doc> = undefined): DocumentView | undefined { - const views = this.getDocumentViews(toFind); - return views?.find(view => view.props.Document !== originatingDoc); + public getFirstDocumentView = (toFind: Doc, originatingDoc: Opt<Doc> = undefined): DocumentView | undefined => { + return this.getDocumentViews(toFind)?.find(view => view.props.Document !== originatingDoc); } public getDocumentViews(toFind: Doc): DocumentView[] { const toReturn: DocumentView[] = []; + const docViews = DocumentManager.Instance.DocumentViews; - // heurstic to return the "best" documents first: + // heuristic to return the "best" documents first: // choose an exact match over an alias match // choose documents that have a PanelWidth() over those that don't (the treeview documents have no panelWidth) - DocumentManager.Instance.DocumentViews.map(view => - view.props.Document.presBox === undefined && view.props.PanelWidth() > 1 && view.props.Document === toFind && toReturn.push(view)); - DocumentManager.Instance.DocumentViews.map(view => - view.props.Document.presBox === undefined && view.props.PanelWidth() <= 1 && view.props.Document === toFind && toReturn.push(view)); - DocumentManager.Instance.DocumentViews.map(view => - view.props.Document.presBox === undefined && view.props.PanelWidth() > 1 && view.props.Document !== toFind && Doc.AreProtosEqual(view.props.Document, toFind) && toReturn.push(view)); - DocumentManager.Instance.DocumentViews.map(view => - view.props.Document.presBox === undefined && view.props.PanelWidth() <= 1 && view.props.Document !== toFind && Doc.AreProtosEqual(view.props.Document, toFind) && toReturn.push(view)); + docViews.map(view => !view.props.Document.presBox && view.props.PanelWidth() > 1 && view.props.Document === toFind && toReturn.push(view)); + docViews.map(view => !view.props.Document.presBox && view.props.PanelWidth() <= 1 && view.props.Document === toFind && toReturn.push(view)); + docViews.map(view => !view.props.Document.presBox && view.props.PanelWidth() > 1 && view.props.Document !== toFind && Doc.AreProtosEqual(view.props.Document, toFind) && toReturn.push(view)); + docViews.map(view => !view.props.Document.presBox && view.props.PanelWidth() <= 1 && view.props.Document !== toFind && Doc.AreProtosEqual(view.props.Document, toFind) && toReturn.push(view)); return toReturn; } @@ -133,107 +129,107 @@ export class DocumentManager { return pairs; } - public jumpToDocument = async (targetDoc: Doc, willZoom: boolean, dockFunc?: (doc: Doc) => void, docContext?: Doc, linkId?: string, closeContextIfNotFound: boolean = false, originatingDoc: Opt<Doc> = undefined): Promise<void> => { + static addRightSplit = (doc: Doc, finished?: () => void) => { + CollectionDockingView.AddRightSplit(doc); + finished?.(); + } + public jumpToDocument = async ( + targetDoc: Doc, + willZoom: boolean, + createViewFunc = DocumentManager.addRightSplit, + docContext?: Doc, + linkId?: string, + closeContextIfNotFound: boolean = false, + originatingDoc: Opt<Doc> = undefined, + finished?: () => void + ): Promise<void> => { + const getFirstDocView = DocumentManager.Instance.getFirstDocumentView; + const focusAndFinish = () => { finished?.(); return false; }; const highlight = () => { - const finalDocView = DocumentManager.Instance.getFirstDocumentView(targetDoc); - finalDocView && (finalDocView.Document.scrollToLinkID = linkId); - finalDocView && Doc.linkFollowHighlight(finalDocView.props.Document); + const finalDocView = getFirstDocView(targetDoc); + if (finalDocView) { + finalDocView.Document.scrollToLinkID = linkId; + Doc.linkFollowHighlight(finalDocView.props.Document); + } }; - const docView = DocumentManager.Instance.getFirstDocumentView(targetDoc, originatingDoc); + const docView = getFirstDocView(targetDoc, originatingDoc); let annotatedDoc = await Cast(targetDoc.annotationOn, Doc); if (annotatedDoc) { - const first = DocumentManager.Instance.getFirstDocumentView(annotatedDoc); + const first = getFirstDocView(annotatedDoc); if (first) annotatedDoc = first.props.Document; } if (docView) { // we have a docView already and aren't forced to create a new one ... just focus on the document. TODO move into view if necessary otherwise just highlight? - docView.props.focus(docView.props.Document, willZoom); + docView.props.focus(docView.props.Document, willZoom, undefined, focusAndFinish); highlight(); } else { const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined; - const contextDoc = contextDocs && contextDocs.find(doc => Doc.AreProtosEqual(doc, targetDoc)) ? docContext : undefined; - const targetDocContext = (annotatedDoc ? annotatedDoc : contextDoc); + const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc)) ? docContext : undefined; + const targetDocContext = annotatedDoc || contextDoc; if (!targetDocContext) { // we don't have a view and there's no context specified ... create a new view of the target using the dockFunc or default - (dockFunc || CollectionDockingView.AddRightSplit)(Doc.BrushDoc(Doc.MakeAlias(targetDoc))); + createViewFunc(Doc.BrushDoc(targetDoc), finished); // bcz: should we use this?: Doc.MakeAlias(targetDoc))); highlight(); - } else { - const targetDocContextView = DocumentManager.Instance.getFirstDocumentView(targetDocContext); + } else { // otherwise try to get a view of the context of the target + const targetDocContextView = getFirstDocView(targetDocContext); targetDocContext.scrollY = 0; // this will force PDFs to activate and load their annotations / allow scrolling - if (targetDocContextView) { // we have a context view and aren't forced to create a new one ... focus on the context + if (targetDocContextView) { // we found a context view and aren't forced to create a new one ... focus on the context first.. targetDocContext.panTransformType = "Ease"; targetDocContextView.props.focus(targetDocContextView.props.Document, willZoom); // now find the target document within the context - if (targetDoc.displayTimecode) { // the target should show up once the video scrubs to the display timecode; + if (targetDoc.displayTimecode) { // if the target has a timecode, it should show up once the (presumed) video context scrubs to the display timecode; targetDocContext.currentTimecode = targetDoc.displayTimecode; - } else { + finished?.(); + } else { // no timecode means we need to find the context view and focus on our target setTimeout(() => { - const retryDocView = DocumentManager.Instance.getDocumentView(targetDoc); - if (retryDocView) { - retryDocView.props.focus(targetDoc, willZoom); // focus on the target if it now exists in the context - } else { - if (closeContextIfNotFound && targetDocContextView.props.removeDocument) targetDocContextView.props.removeDocument(targetDocContextView.props.Document); - targetDoc.layout && (dockFunc || CollectionDockingView.AddRightSplit)(Doc.BrushDoc(Doc.MakeAlias(targetDoc))); // otherwise create a new view of the target + const retryDocView = getFirstDocView(targetDoc); // test again for the target view snce we presumably created the context above by focusing on it + if (retryDocView) { // we found the target in the context + retryDocView.props.focus(targetDoc, willZoom, undefined, focusAndFinish); // focus on the target in the context + } else { // we didn't find the target, so it must have moved out of the context. Go back to just creating it. + if (closeContextIfNotFound) targetDocContextView.props.removeDocument?.(targetDocContextView.props.Document); + targetDoc.layout && createViewFunc(Doc.BrushDoc(targetDoc), finished); // create a new view of the target } highlight(); }, 0); } } else { // there's no context view so we need to create one first and try again - (dockFunc || CollectionDockingView.AddRightSplit)(targetDocContext); + createViewFunc(targetDocContext); // so first we create the target, but don't pass finished because we still need to create the target setTimeout(() => { - const finalDocView = DocumentManager.Instance.getFirstDocumentView(targetDoc); - const finalDocContextView = DocumentManager.Instance.getFirstDocumentView(targetDocContext); + const finalDocView = getFirstDocView(targetDoc); + const finalDocContextView = getFirstDocView(targetDocContext); setTimeout(() => // if not, wait a bit to see if the context can be loaded (e.g., a PDF). wait interval heurisitic tries to guess how we're animating based on what's just become visible - this.jumpToDocument(targetDoc, willZoom, dockFunc, undefined, linkId, true), finalDocView ? 0 : finalDocContextView ? 250 : 2000); // so call jump to doc again and if the doc isn't found, it will be created. + this.jumpToDocument(targetDoc, willZoom, createViewFunc, undefined, linkId, true, undefined, finished), // pass true this time for closeContextIfNotFound + finalDocView ? 0 : finalDocContextView ? 250 : 2000); // so call jump to doc again and if the doc isn't found, it will be created. }, 0); } } } } - public async FollowLink(link: Doc | undefined, doc: Doc, focus: (doc: Doc, maxLocation: string) => void, zoom: boolean = false, reverse: boolean = false, currentContext?: Doc) { + public async FollowLink(link: Opt<Doc>, doc: Doc, createViewFunc: CreateViewFunc, zoom = false, currentContext?: Doc, finished?: () => void, traverseBacklink?: boolean) { const linkDocs = link ? [link] : DocListCast(doc.links); SelectionManager.DeselectAll(); - const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, doc)); - const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, doc)); - const firstDocWithoutView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0); - const secondDocWithoutView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0); - const first = firstDocWithoutView ? [firstDocWithoutView] : firstDocs; - const second = secondDocWithoutView ? [secondDocWithoutView] : secondDocs; - const linkDoc = first.length ? first[0] : second.length ? second[0] : undefined; - const linkFollowDocs = first.length ? [await first[0].anchor2 as Doc, await first[0].anchor1 as Doc] : second.length ? [await second[0].anchor1 as Doc, await second[0].anchor2 as Doc] : undefined; - const linkFollowDocContexts = first.length ? [await first[0].context as Doc, await first[0].context as Doc] : second.length ? [await second[0].context as Doc, await second[0].context as Doc] : [undefined, undefined]; - const linkFollowTimecodes = first.length ? [NumCast(first[0].anchor2_timecode), NumCast(first[0].anchor1_timecode)] : second.length ? [NumCast(second[0].anchor1_timecode), NumCast(second[0].anchor2_timecode)] : [undefined, undefined]; - if (linkFollowDocs && linkDoc) { - const maxLocation = StrCast(linkDoc.maximizeLocation, "inTab"); - const targetContext = !Doc.AreProtosEqual(linkFollowDocContexts[reverse ? 1 : 0], currentContext) ? linkFollowDocContexts[reverse ? 1 : 0] : undefined; - const target = linkFollowDocs[reverse ? 1 : 0]; - const annotatedDoc = await Cast(target.annotationOn, Doc); - if (annotatedDoc) { - annotatedDoc.currentTimecode !== undefined && (target.currentTimecode = linkFollowTimecodes[reverse ? 1 : 0]); + const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, doc)); // link docs where 'doc' is anchor1 + const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, doc)); // link docs where 'doc' is anchor2 + const fwdLinkWithoutTargetView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0); + const backLinkWithoutTargetView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0); + const linkWithoutTargetDoc = traverseBacklink === undefined ? fwdLinkWithoutTargetView || backLinkWithoutTargetView : traverseBacklink ? backLinkWithoutTargetView : fwdLinkWithoutTargetView; + const linkDocList = linkWithoutTargetDoc ? [linkWithoutTargetDoc] : (traverseBacklink === undefined ? firstDocs.concat(secondDocs) : traverseBacklink ? secondDocs : firstDocs); + const linkDoc = linkDocList.length && linkDocList[0]; + if (linkDoc) { + const target = (doc === linkDoc.anchor1 ? linkDoc.anchor2 : doc === linkDoc.anchor2 ? linkDoc.anchor1 : + (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) ? linkDoc.anchor2 : linkDoc.anchor1)) as Doc; + if (target) { + const containerDoc = (await Cast(target.annotationOn, Doc)) || target; + containerDoc.currentTimecode !== undefined && (containerDoc.currentTimecode = NumCast(target?.timecode)); + const targetContext = await target?.context as Doc; + const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined; + DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "onRight"), finished), targetNavContext, linkDoc[Id], undefined, doc, finished); } else { - target.currentTimecode !== undefined && (target.currentTimecode = linkFollowTimecodes[reverse ? 1 : 0]); + finished?.(); } - DocumentManager.Instance.jumpToDocument(linkFollowDocs[reverse ? 1 : 0], zoom, (doc: Doc) => focus(doc, maxLocation), targetContext, linkDoc[Id], undefined, doc); - } else if (link) { - DocumentManager.Instance.jumpToDocument(link, zoom, (doc: Doc) => focus(doc, "onRight"), undefined, undefined); - } - } - - @action - zoomIntoScale = (docDelegate: Doc, scale: number) => { - const docView = DocumentManager.Instance.getDocumentView(Doc.GetProto(docDelegate)); - docView?.props.zoomToScale(scale); - } - - getScaleOfDocView = (docDelegate: Doc) => { - const doc = Doc.GetProto(docDelegate); - - const docView = DocumentManager.Instance.getDocumentView(doc); - if (docView) { - return docView.props.getScale(); } else { - return 1; + finished?.(); } } } diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 1f2312032..53c0a2963 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -17,6 +17,7 @@ import { convertDropDataToButtons } from "./DropConverter"; import { AudioBox } from "../views/nodes/AudioBox"; import { DateField } from "../../new_fields/DateField"; import { DocumentView } from "../views/nodes/DocumentView"; +import { UndoManager } from "./UndoManager"; export type dropActionType = "place" | "alias" | "copy" | undefined; export function SetupDrag( @@ -199,6 +200,7 @@ export namespace DragManager { dropDoc instanceof Doc && AudioBox.ActiveRecordings.map(d => DocUtils.MakeLink({ doc: dropDoc }, { doc: d }, "audio link", "audio timeline")); return dropDoc; }; + const batch = UndoManager.StartBatch("dragging"); const finishDrag = (e: DragCompleteEvent) => { e.docDragData && (e.docDragData.droppedDocuments = dragData.draggedDocuments.map(d => !dragData.isSelectionMove && !dragData.userDropAction && ScriptCast(d.onDragStart) ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result) : @@ -208,6 +210,7 @@ export namespace DragManager { e.docDragData?.droppedDocuments.forEach((drop: Doc, i: number) => (dragData?.removeDropProperties || []).concat(Cast(dragData.draggedDocuments[i].removeDropProperties, listSpec("string"), [])).map(prop => drop[prop] = undefined) ); + batch.end(); }; dragData.draggedDocuments.map(d => d.dragFactory); // does this help? trying to make sure the dragFactory Doc is loaded StartDrag(eles, dragData, downX, downY, options, finishDrag); @@ -401,15 +404,17 @@ export namespace DragManager { function dispatchDrag(dragEles: HTMLElement[], e: PointerEvent, dragData: { [index: string]: any }, options?: DragOptions, finishDrag?: (e: DragCompleteEvent) => void) { const removed = dragData.dontHideOnDrop ? [] : dragEles.map(dragEle => { - const ret = { ele: dragEle, w: dragEle.style.width, h: dragEle.style.height }; + const ret = { ele: dragEle, w: dragEle.style.width, h: dragEle.style.height, o: dragEle.style.overflow }; dragEle.style.width = "0"; dragEle.style.height = "0"; + dragEle.style.overflow = "hidden"; return ret; }); const target = document.elementFromPoint(e.x, e.y); removed.map(r => { r.ele.style.width = r.w; r.ele.style.height = r.h; + r.ele.style.overflow = r.o; }); if (target) { const complete = new DragCompleteEvent(false, dragData); diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index 69dc303cd..d96257ca1 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -58,7 +58,10 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) { (layoutDoc.layout instanceof Doc) && !data.userDropAction; } layoutDoc.isTemplateDoc = true; - dbox = Docs.Create.FontIconDocument({ _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, backgroundColor: StrCast(doc.backgroundColor), title: StrCast(layoutDoc.title), icon: layoutDoc.isTemplateDoc ? "font" : "bolt" }); + dbox = Docs.Create.FontIconDocument({ + _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, isButton: true, + backgroundColor: StrCast(doc.backgroundColor), title: StrCast(layoutDoc.title), icon: layoutDoc.isTemplateDoc ? "font" : "bolt" + }); dbox.dragFactory = layoutDoc; dbox.removeDropProperties = doc.removeDropProperties instanceof ObjectField ? ObjectField.MakeCopy(doc.removeDropProperties) : undefined; dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)'); diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx index 2eec02a42..f2d569cf3 100644 --- a/src/client/util/InteractionUtils.tsx +++ b/src/client/util/InteractionUtils.tsx @@ -10,20 +10,6 @@ export namespace InteractionUtils { const REACT_POINTER_PEN_BUTTON = 0; const ERASER_BUTTON = 5; - export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, color: string, width: number) { - const pts = points.reduce((acc: string, pt: { X: number, Y: number }) => acc + `${pt.X - left},${pt.Y - top} `, ""); - return ( - <polyline - points={pts} - style={{ - fill: "none", - stroke: color, - strokeWidth: width - }} - /> - ); - } - export class MultiTouchEvent<T extends React.TouchEvent | TouchEvent> { constructor( readonly fingers: number, diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 094cd58f3..de2707d36 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -16,7 +16,7 @@ import { listSpec } from "../../new_fields/Schema"; import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField"; import { ComputedField } from "../../new_fields/ScriptField"; import { BoolCast, Cast, NumCast, StrCast } from "../../new_fields/Types"; -import { emptyFunction, returnEmptyString, returnFalse, returnOne, Utils } from "../../Utils"; +import { emptyFunction, returnEmptyString, returnFalse, returnOne, Utils, returnZero } from "../../Utils"; import { DocServer } from "../DocServer"; import { Docs } from "../documents/Documents"; import { CollectionViewType } from "../views/collections/CollectionView"; @@ -818,11 +818,14 @@ export class DashDocView { LibraryPath={this._textBox.props.LibraryPath} fitToBox={BoolCast(dashDoc._fitToBox)} addDocument={returnFalse} + rootSelected={this._textBox.props.isSelected} removeDocument={removeDoc} ScreenToLocalTransform={this.getDocTransform} addDocTab={this._textBox.props.addDocTab} pinToPres={returnFalse} renderDepth={self._textBox.props.renderDepth + 1} + NativeHeight={returnZero} + NativeWidth={returnZero} PanelWidth={finalLayout[WidthSym]} PanelHeight={finalLayout[HeightSym]} focus={this.outerFocus} @@ -830,8 +833,6 @@ export class DashDocView { parentActive={returnFalse} whenActiveChanged={returnFalse} bringToFront={emptyFunction} - zoomToScale={emptyFunction} - getScale={returnOne} dontRegisterView={false} ContainingCollectionView={this._textBox.props.ContainingCollectionView} ContainingCollectionDoc={this._textBox.props.ContainingCollectionDoc} diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index ce21b7fa7..cf04c44ca 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -195,14 +195,14 @@ export type Transformer = { getVars?: () => { capturedVariables: { [name: string]: Field } } }; export interface ScriptOptions { - requiredType?: string; - addReturn?: boolean; - params?: { [name: string]: string }; - capturedVariables?: { [name: string]: Field }; - typecheck?: boolean; - editable?: boolean; - traverser?: TraverserParam; - transformer?: Transformer; + requiredType?: string; // does function required a typed return value + addReturn?: boolean; // does the compiler automatically add a return statement + params?: { [name: string]: string }; // list of function parameters and their types + capturedVariables?: { [name: string]: Field }; // list of captured variables + typecheck?: boolean; // should the compiler perform typechecking + editable?: boolean; // can the script edit Docs + traverser?: TraverserParam; + transformer?: Transformer; // does the editor display a text label by each document that can be used as a captured document reference globals?: { [name: string]: any }; } diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts index b597f1e07..2026bf940 100644 --- a/src/client/util/SearchUtil.ts +++ b/src/client/util/SearchUtil.ts @@ -64,7 +64,7 @@ export namespace SearchUtil { const textDocs = newIds.map((id: string) => textDocMap[id]).map(doc => doc as Doc); for (let i = 0; i < textDocs.length; i++) { const testDoc = textDocs[i]; - if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && testDoc.type !== DocumentType.EXTENSION && theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1) { + if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1) { theDocs.push(Doc.GetProto(testDoc)); theLines.push(newLines[i].map(line => line.replace(query, query.toUpperCase()))); } @@ -74,7 +74,7 @@ export namespace SearchUtil { const docs = ids.map((id: string) => docMap[id]).map(doc => doc as Doc); for (let i = 0; i < ids.length; i++) { const testDoc = docs[i]; - if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && testDoc.type !== DocumentType.EXTENSION && (options.allowAliases || theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1)) { + if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && (options.allowAliases || theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1)) { theDocs.push(testDoc); theLines.push([]); } diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 0e281e77e..6c386d684 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -54,12 +54,14 @@ export namespace SelectionManager { export function SelectDoc(docView: DocumentView, ctrlPressed: boolean): void { manager.SelectDoc(docView, ctrlPressed); } - + // computed functions, such as used in IsSelected generate errors if they're called outside of a + // reaction context. Specifying the context with 'outsideReaction' allows an efficiency feature + // to avoid unnecessary mobx invalidations when running inside a reaction. export function IsSelected(doc: DocumentView, outsideReaction?: boolean): boolean { return outsideReaction ? - manager.SelectedDocuments.get(doc) ? true : false : - computedFn(function isSelected(doc: DocumentView) { + manager.SelectedDocuments.get(doc) ? true : false : // get() accesses a hashtable -- setting anything in the hashtable generates a mobx invalidation for every get() + computedFn(function isSelected(doc: DocumentView) { // wraapping get() in a computedFn only generates mobx() invalidations when the return value of the function for the specific get parameters has changed return manager.SelectedDocuments.get(doc) ? true : false; })(doc); } |
