diff options
-rw-r--r-- | src/client/views/collections/CollectionSchemaCells.tsx | 26 | ||||
-rw-r--r-- | src/client/views/collections/CollectionTreeView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/FieldView.tsx | 3 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.tsx | 39 | ||||
-rw-r--r-- | src/client/views/pdf/PDFViewer.tsx | 4 | ||||
-rw-r--r-- | src/client/views/search/SearchBox.tsx | 34 | ||||
-rw-r--r-- | src/fields/Doc.ts | 25 |
7 files changed, 69 insertions, 64 deletions
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index ea786800c..4bd69041b 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -894,26 +894,22 @@ export class CollectionSchemaCheckboxCell extends CollectionSchemaCell { export class CollectionSchemaButtons extends CollectionSchemaCell { render() { const doc = this.props.rowProps.original; - const searchMatch = (backward: boolean = true) => { doc.searchMatch = undefined; setTimeout(() => doc.searchMatch = backward, 0); }; + const searchMatch = (backward: boolean = true) => Doc.SearchMatchNext(doc, backward); // const reference = React.createRef<HTMLDivElement>(); // const onItemDown = (e: React.PointerEvent) => { // (!this.props.CollectionView || !this.props.CollectionView.props.isSelected() ? undefined : // SetupDrag(reference, () => this._document, this.props.moveDocument, this.props.Document.schemaDoc ? "copy" : undefined)(e)); // }; return !BoolCast(this.props.Document._searchDoc) ? <></> - : StrCast(doc.type) === DocumentType.PDF ? - <button style={{ position: "relative", height: 30, width: 28, left: 1, }} onClick={() => searchMatch(false)}> - <FontAwesomeIcon icon="arrow-down" size="sm" /> - </button> - : StrCast(doc.type) === DocumentType.RTF ? - <div style={{ paddingTop: 8, paddingLeft: 3, }} > - <button style={{ padding: 2, left: 77 }} onClick={() => searchMatch(true)}> - <FontAwesomeIcon icon="arrow-up" size="sm" /> - </button> - <button style={{ padding: 2 }} onClick={() => searchMatch(false)} > - <FontAwesomeIcon icon="arrow-down" size="sm" /> - </button> - </div> : - <></>; + : [DocumentType.PDF, DocumentType.RTF].includes(StrCast(doc.type) as DocumentType) ? + <div style={{ paddingTop: 8, paddingLeft: 3, }} > + <button style={{ padding: 2, left: 77 }} onClick={() => searchMatch(true)}> + <FontAwesomeIcon icon="arrow-up" size="sm" /> + </button> + <button style={{ padding: 2 }} onClick={() => searchMatch(false)} > + <FontAwesomeIcon icon="arrow-down" size="sm" /> + </button> + </div> : + <></>; } }
\ No newline at end of file diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 4503b0fb3..8440d3e9b 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -472,7 +472,7 @@ class TreeView extends React.Component<TreeViewProps> { return <> <div className="docContainer" ref={this._tref} title="click to edit title" id={`docContainer-${this.props.parentKey}`} style={{ - fontWeight: this.doc.searchMatch !== undefined ? "bold" : undefined, + fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? "bold" : undefined, textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined, outline: BoolCast(this.doc.dashboardBrush) ? "dashed 1px #06123232" : undefined, pointerEvents: this.props.active() || SnappingManager.GetIsDragging() ? undefined : "none" diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index d95951f3a..3726f1d5f 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -58,9 +58,6 @@ export interface FieldViewProps { ChromeHeight?: () => number; childLayoutTemplate?: () => Opt<Doc>; - highlighting?: string[]; - lines?: string[]; - doc?: Doc; // properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React) height?: number; width?: number; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 6b4115e53..5250a8f58 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -376,8 +376,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } public highlightSearchTerms = (terms: string[], backward: boolean) => { if (this._editorView && (this._editorView as any).docView && terms.some(t => t)) { - - const mark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight); const activeMark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight, { selected: true }); const res = terms.filter(t => t).map(term => this.findInNode(this._editorView!, this._editorView!.state.doc, term)); @@ -385,24 +383,19 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp let tr = this._editorView.state.tr; const flattened: TextSelection[] = []; res.map(r => r.map(h => flattened.push(h))); - if (BoolCast(Doc.GetProto(this.dataDoc).resetSearch) === true) { - this._searchIndex = 0; - Doc.GetProto(this.dataDoc).resetSearch = undefined; - } - else { - this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex; - if (backward === true) { - if (this._searchIndex > 1) { - this._searchIndex += -2; - } - else if (this._searchIndex === 1) { - this._searchIndex = length - 1; - } - else if (this._searchIndex === 0 && length !== 1) { - this._searchIndex = length - 2; - } - + console.log("Search:" + this.rootDoc.title + " " + this._searchIndex + " => " + (this._searchIndex + 1 > flattened.length - 1 ? 0 : this._searchIndex + 1)); + this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex; + if (backward === true) { + if (this._searchIndex > 1) { + this._searchIndex += -2; + } + else if (this._searchIndex === 1) { + this._searchIndex = length - 1; } + else if (this._searchIndex === 0 && length !== 1) { + this._searchIndex = length - 2; + } + } const lastSel = Math.min(flattened.length - 1, this._searchIndex); @@ -909,9 +902,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this.setupEditor(this.config, this.props.fieldKey); - this._disposers.search = reaction(() => this.rootDoc.searchMatch, - search => search !== undefined ? this.highlightSearchTerms([Doc.SearchQuery()], BoolCast(search)) : this.unhighlightSearchTerms(), - { fireImmediately: this.rootDoc.searchMatch !== undefined ? true : false }); + this._disposers.search = reaction(() => Doc.IsSearchMatch(this.rootDoc), + search => { + search ? this.highlightSearchTerms([Doc.SearchQuery()], search.searchMatch < 0) : this.unhighlightSearchTerms(); + }, + { fireImmediately: Doc.IsSearchMatch(this.rootDoc) ? true : false }); this._disposers.record = reaction(() => this._recording, () => { diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 0916e8b0c..f2aa9fa3f 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -150,9 +150,9 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu runInAction(() => this._showWaiting = this._showCover = true); this.props.startupLive && this.setupPdfJsViewer(); this._mainCont.current && (this._mainCont.current.scrollTop = this.layoutDoc._scrollTop || 0); - this._searchReactionDisposer = reaction(() => this.Document.searchMatch, + this._searchReactionDisposer = reaction(() => Doc.IsSearchMatch(this.rootDoc), m => { - if (m !== undefined) (this._lastSearch = true) && this.search(Doc.SearchQuery(), true); + if (m) (this._lastSearch = true) && this.search(Doc.SearchQuery(), m.searchMatch > 0); else !(this._lastSearch = false) && setTimeout(() => !this._lastSearch && this.search("", false, true), 200); }, { fireImmediately: true }); diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index d7c065a75..c571adefe 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -84,21 +84,17 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc Object.values(this._disposers).forEach(disposer => disposer?.()); } - @action - getViews = (doc: Doc) => SearchUtil.GetViewsOfDocument(doc) - @action.bound onChange(e: React.ChangeEvent<HTMLInputElement>) { this.layoutDoc._searchString = e.target.value; this.newsearchstring = e.target.value; if (e.target.value === "") { if (this.currentSelectedCollection) { - this.doLoop(this.currentSelectedCollection, undefined); + this.setSearchDocsRecursive(this.currentSelectedCollection, undefined); } this.closeSearch(false); if (this.currentSelectedCollection !== undefined) { - this.currentSelectedCollection.props.Document._searchDocs = new List<Doc>([]); this.currentSelectedCollection = undefined; this.props.Document.selectedDoc = undefined; } @@ -242,14 +238,12 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc while (docs.length > 0) { newarray = []; docs.forEach((d) => { - if (d.data !== undefined) { - newarray.push(...DocListCast(d.data)); - } - const hlights: string[] = []; + d.data && newarray.push(...DocListCast(d.data)); + const hlights = new Set<string>(); this.documentKeys(d).forEach(key => - Field.toString(d[key] as Field).toLowerCase().includes(query) && !hlights.includes(key) && hlights.push(key)); - if (hlights.length > 0) { - found.push([d, hlights, []]); + Field.toString(d[key] as Field).toLowerCase().includes(query) && hlights.add(key)); + if (Array.from(hlights.keys()).length > 0) { + found.push([d, Array.from(hlights.keys()), []]); docsforFilter.push(d); } }); @@ -259,7 +253,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc this.docsforfilter = docsforFilter; if (this.filter === true) { selectedCollection.props.Document._searchDocs = new List<Doc>(docsforFilter); - this.doLoop(selectedCollection, docsforFilter); + this.setSearchDocsRecursive(selectedCollection, docsforFilter); } this._numTotalResults = found.length; this.realTotalResults = found.length; @@ -285,7 +279,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc @action submitSearch = async (reset?: boolean) => { if (this.currentSelectedCollection !== undefined) { - this.doLoop(this.currentSelectedCollection, undefined); + this.setSearchDocsRecursive(this.currentSelectedCollection, undefined); } if (reset) { this.layoutDoc._searchString = ""; @@ -434,7 +428,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc closeSearch = (closesearchbar = true) => { this._results.forEach(result => { Doc.UnBrushDoc(result[0]); - result[0].searchMatch = undefined; + Doc.ClearSearchMatches(); }); closesearchbar && (this._searchbarOpen = false); } @@ -479,9 +473,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc const highlights = Array.from([...Array.from(new Set(result[1]).values())]); const lines = new List<string>(result[2]); highlights.forEach((item) => headers.add(item)); - result[0].lines = lines; - result[0].highlighting = highlights.join(", "); - result[0].searchMatch = true; + Doc.SetSearchMatch(result[0], { searchMatch: 1 }); if (i < this._visibleDocuments.length) { this._visibleDocuments[i] = result[0]; Doc.BrushDoc(result[0]); @@ -530,7 +522,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc this._searchFullDB = scope; this.dataDoc[this.fieldKey] = new List<Doc>([]); if (this.currentSelectedCollection !== undefined) { - this.doLoop(this.currentSelectedCollection, undefined); + this.setSearchDocsRecursive(this.currentSelectedCollection, undefined); } this.submitSearch(); } @@ -566,7 +558,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc </div>; } - doLoop = (collectionView: DocumentView, filter: Doc[] | undefined) => { + setSearchDocsRecursive = (collectionView: DocumentView, filter: Doc[] | undefined) => { let docs = DocListCast(collectionView.dataDoc[Doc.LayoutFieldKey(collectionView.dataDoc)]); let newarray: Doc[] = []; while (docs.length > 0) { @@ -614,7 +606,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc onPointerDown={e => { e.stopPropagation(); SetupDrag(this.collectionRef, () => this.layoutDoc._searchString ? this.startDragCollection() : undefined); }} onClick={action(() => { this.filter = !this.filter && !this._searchFullDB; - this.currentSelectedCollection && this.doLoop(this.currentSelectedCollection, this.filter ? this.docsforfilter : undefined); + this.currentSelectedCollection && this.setSearchDocsRecursive(this.currentSelectedCollection, this.filter ? this.docsforfilter : undefined); })} /> </div> </Tooltip> diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index c78b01def..dc274a063 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -23,6 +23,7 @@ import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, u import { LinkManager } from "../client/util/LinkManager"; import JSZip = require("jszip"); import { saveAs } from "file-saver"; +import { result } from "lodash"; export namespace Field { export function toKeyValueString(doc: Doc, key: string): string { @@ -881,6 +882,7 @@ export namespace Doc { export class DocBrush { BrushedDoc: ObservableMap<Doc, boolean> = new ObservableMap(); + SearchMatchDoc: ObservableMap<Doc, { searchMatch: number }> = new ObservableMap(); } const brushManager = new DocBrush(); @@ -906,6 +908,29 @@ export namespace Doc { export function SetSelectedTool(tool: InkTool) { Doc.UserDoc().activeInkTool = tool; } export function GetSelectedTool(): InkTool { return StrCast(Doc.UserDoc().activeInkTool, InkTool.None) as InkTool; } export function SetUserDoc(doc: Doc) { manager._user_doc = doc; } + + export function IsSearchMatch(doc: Doc) { + return computedFn(function IsSearchMatch(doc: Doc) { + return brushManager.SearchMatchDoc.has(doc) ? brushManager.SearchMatchDoc.get(doc) : + brushManager.SearchMatchDoc.has(Doc.GetProto(doc)) ? brushManager.SearchMatchDoc.get(Doc.GetProto(doc)) : undefined; + })(doc); + } + export function SetSearchMatch(doc: Doc, results: { searchMatch: number }) { + if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return doc; + brushManager.SearchMatchDoc.set(doc, results); + return doc; + } + export function SearchMatchNext(doc: Doc, backward: boolean) { + if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return doc; + const result = brushManager.SearchMatchDoc.get(doc); + const num = Math.abs(result?.searchMatch || 0) + 1; + runInAction(() => result && brushManager.SearchMatchDoc.set(doc, { searchMatch: backward ? -num : num })); + return doc; + } + export function ClearSearchMatches() { + brushManager.SearchMatchDoc.clear(); + } + export function IsBrushed(doc: Doc) { return computedFn(function IsBrushed(doc: Doc) { return brushManager.BrushedDoc.has(doc) || brushManager.BrushedDoc.has(Doc.GetProto(doc)); |