diff options
-rw-r--r-- | src/client/util/CurrentUserUtils.ts | 11 | ||||
-rw-r--r-- | src/client/views/MainView.tsx | 6 | ||||
-rw-r--r-- | src/client/views/search/SearchBox.scss | 173 | ||||
-rw-r--r-- | src/client/views/search/SearchBox.tsx | 491 |
4 files changed, 291 insertions, 390 deletions
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 596ccd9a6..31384da3b 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -947,17 +947,12 @@ export class CurrentUserUtils { // Search sidebar is where searches within the document are performed static setupSearchSidebar(doc: Doc) { - if (doc.mySearchPanelDoc === undefined) { - doc.mySearchPanelDoc = new PrefetchProxy(Docs.Create.SearchDocument({ - _width: 500, _height: 300, backgroundColor: "dimGray", ignoreClick: true, _searchDoc: true, + if (doc.mySearchPanel === undefined) { + doc.mySearchPanel = new PrefetchProxy(Docs.Create.SearchDocument({ + backgroundColor: "dimGray", ignoreClick: true, _searchDoc: true, childDropAction: "alias", _lockedPosition: true, _viewType: CollectionViewType.Schema, title: "sidebar search stack", system: true })) as any as Doc; } - if (doc.mySearchPanel === undefined) { - const searchPanelDoc = Cast(doc.mySearchPanelDoc, Doc, null); - //const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Import External document", _stayInCollection: true, _hideContextMenu: true, title: "Import", icon: "upload", system: true }); - doc.mySearchPanel = new PrefetchProxy(Docs.Create.StackingDocument([searchPanelDoc], { title: "Search", _yMargin: 20, ignoreClick: true, _chromeHidden: true, _stayInCollection: true, _hideContextMenu: true, _lockedPosition: true, system: true })); - } } static setupClickEditorTemplates(doc: Doc) { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index bc9c6f4d2..0d957518b 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -178,12 +178,12 @@ export class MainView extends React.Component { const targets = document.elementsFromPoint(e.x, e.y); if (targets.length) { const targClass = targets[0].className.toString(); - if (SearchBox.Instance._searchbarOpen || SearchBox.Instance.open) { + /*if (SearchBox.Instance._resultsOpen) { const check = targets.some((thing) => (thing.className === "collectionSchemaView-searchContainer" || (thing as any)?.dataset.icon === "filter" || thing.className === "collectionSchema-header-menuOptions")); !check && SearchBox.Instance.resetSearch(true); - } + }*/ !targClass.includes("contextMenu") && ContextMenu.Instance.closeMenu(); !["timeline-menu-desc", "timeline-menu-item", "timeline-menu-input"].includes(targClass) && TimelineMenu.Instance.closeMenu(); } @@ -192,7 +192,7 @@ export class MainView extends React.Component { initEventListeners = () => { window.addEventListener("drop", e => e.preventDefault(), false); // prevent default behavior of navigating to a new web page window.addEventListener("dragover", e => e.preventDefault(), false); - document.addEventListener("pointermove", action(e => SearchBox.Instance._undoBackground = UndoManager.batchCounter ? "#000000a8" : undefined)); + //document.addEventListener("pointermove", action(e => SearchBox.Instance._undoBackground = UndoManager.batchCounter ? "#000000a8" : undefined)); document.addEventListener("pointerdown", this.globalPointerDown); document.addEventListener("click", (e: MouseEvent) => { if (!e.cancelBubble) { diff --git a/src/client/views/search/SearchBox.scss b/src/client/views/search/SearchBox.scss index 4f5b7e41a..f8e994da7 100644 --- a/src/client/views/search/SearchBox.scss +++ b/src/client/views/search/SearchBox.scss @@ -2,141 +2,84 @@ @import "./NaviconButton.scss"; .searchBox-container { - display: flex; - flex-direction: column; width: 100%; height: 100%; - position: relative; font-size: 10px; line-height: 1; - overflow-y: auto; - overflow-x: visible; - background: lightgrey; - overflow: visible; + background: none; z-index: 1000; + padding: 0px; + cursor: default; .searchBox-bar { - height: $searchpanel-height; + width: 100%; + height: 35px; display: flex; justify-content: center; align-items: center; - background-color: black; + background-color: none; + padding: 5px; - .searchBox-lozenges { - position: absolute; - left: 15; - display: flex; - - .searchBox-lozenge-user, - .searchBox-lozenge-dashboard, - .searchBox-lozenge { - height: 18px; - padding: 4px; - margin-right: 5px; - display: flex; - align-items: center; - border: grey 1px solid; - .searchBox-logoff, - .searchBox-dashboards { - border-radius: 3px; - background: olivedrab; - color: white; - display: none; - margin-left: 5px; - padding: 1px 2px 1px 2px; - cursor: pointer; - } - .searchBox-logoff { - background: red; - } - - .searchBox-dashSelect{ - border: none; - background-color: transparent; + .searchBox-type { + display: block; + width: 50px; + outline: none; + padding: 1px 5px 1px 5px; + color: black; + height: 25px; + border: 1px solid black; + } - &:hover { - cursor: pointer; - } - } - } - .searchBox-lozenge-user:hover { - .searchBox-logoff { - display:inline-block; - } - } - .searchBox-lozenge-dashboard:hover { - .searchBox-dashboards { - display:inline-block; - } - } + .searchBox-input { + display: block; + width: calc(100% - 50px); + outline: none; + padding: 1px 5px 1px 5px; + color: black; + height: 25px; + border: 1px solid black; } - .searchBox-query { - position: relative; + } + + .searchBox-results-container { + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + justify-content: "center"; + + .searchBox-results-count { display: flex; - width: 450; + color: gray; + margin-left: 5px; } - .searchBox-barChild { + + .searchBox-results-scroll-view { + margin-top: 10px; + display: inline-block; + width: 100%; + height: calc(100% - 55px); + overflow-y: scroll; - &.searchBox-collection { - flex: 0 1 auto; - margin-left: 2px; - margin-right: 2px - } + .searchBox-results-scroll-view-result { + display: inline-block; + vertical-align: middle; + width: 100%; + height: 40px; + cursor: pointer; + font-size: 15px; + padding: 10px; - &.searchBox-input { - margin:5px; - border-radius:20px; - border:black; - display: block; - width: 130px; - -webkit-transition: width 0.4s; - transition: width 0.4s; - align-self: stretch; - outline:none; - &:focus { - width: 500px; - outline:none; + &.searchBox-results-scroll-view-result-selected { + background: gray; } - } - &.searchBox-filter { - align-self: stretch; - button{ - transform:none; - &:hover { - transform: none; - } - } - } - &.searchBox-submit { - margin-left: 2px; - margin-right: 2px - } - - &.searchBox-close { - color: $light-color; - max-height: $searchpanel-height; + .titletitle { + display: relative; + float: left; + left: 50px; + } } } - } -} - -.searchBox-results { - display: flex; - flex-direction: column; - top: 300px; - display: flex; - flex-direction: column; - height: 100%; - overflow: visible; - - .no-result { - width: 500px; - background: $light-color-secondary; - padding: 10px; - height: 50px; - text-transform: uppercase; - text-align: left; - font-weight: bold; } }
\ No newline at end of file diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index cc4590969..374a754bf 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -3,7 +3,7 @@ import { Tooltip } from '@material-ui/core'; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { Doc, DocListCast, Field, Opt, DocListCastAsync, DataSym, HeightSym } from '../../../fields/Doc'; +import { Doc, DocListCast, Field, Opt, DocListCastAsync, DataSym, HeightSym, FieldsSym } from '../../../fields/Doc'; import { documentSchema } from "../../../fields/documentSchemas"; import { Copy, Id, ToString } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; @@ -26,6 +26,9 @@ import "./SearchBox.scss"; import { undoBatch } from "../../util/UndoManager"; import { DocServer } from "../../DocServer"; import { MainView } from "../MainView"; +import { DocumentManager } from "../../util/DocumentManager"; +import { CollectionSchemaBooleanCell } from "../collections/CollectionSchemaCells"; +import { transpileModule } from "typescript"; export const searchSchema = createSchema({ Document: Doc }); @@ -38,33 +41,18 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc public static Instance: SearchBox; private _allIcons: string[] = [DocumentType.INK, DocumentType.AUDIO, DocumentType.COL, DocumentType.IMG, DocumentType.LINK, DocumentType.PDF, DocumentType.RTF, DocumentType.VID, DocumentType.WEB]; - private _numResultsPerPage = 500; - private _numTotalResults = -1; - private _endIndex = -1; - private _lockPromise?: Promise<void>; private _resultsSet = new Map<Doc, number>(); private _inputRef = React.createRef<HTMLInputElement>(); - private _maxSearchIndex: number = 0; - private _curRequest?: Promise<any> = undefined; private _disposers: { [name: string]: IReactionDisposer } = {}; private _blockedTypes = [DocumentType.PRESELEMENT, DocumentType.KVP, DocumentType.FILTER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING]; - private docsforfilter: Doc[] | undefined = []; - private realTotalResults: number = 0; - private newsearchstring = ""; - - @observable _undoBackground: string | undefined = ""; + @observable _searchString = ""; + @observable _docTypeString = "all"; @observable _icons: string[] = this._allIcons; @observable _results: [Doc, string[], string[]][] = []; - @observable _visibleElements: JSX.Element[] = []; - @observable _visibleDocuments: Doc[] = []; + @observable _selectedResult: Doc | undefined = undefined; @observable _deletedDocsStatus: boolean = false; @observable _onlyAliases: boolean = true; - @observable _searchbarOpen = false; - @observable _noResults = ""; - @observable _pageStart = 0; - @observable open = false; - @observable children = 0; @computed get filter() { return this._results?.length && (this.currentSelectedCollection?.props.Document._searchFilterDocs || this.currentSelectedCollection?.props.Document._docFilters); } constructor(props: any) { @@ -76,8 +64,6 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc if (this._inputRef.current) { this._inputRef.current.focus(); } - this._disposers.filters = reaction(() => this.props.Document._docFilters, - (filters: any) => this.setSearchFilter(this.currentSelectedCollection, !this.filter ? undefined : this.docsforfilter)); }); componentWillUnmount() { @@ -86,32 +72,25 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc @computed get currentSelectedCollection() { return CollectionDockingView.Instance; } - onChange = action((e: React.ChangeEvent<HTMLInputElement>) => { - this.newsearchstring = e.target.value; - if (e.target.value === "") { - console.log("Reset start"); - this.docsforfilter = undefined; - this.setSearchFilter(this.currentSelectedCollection, undefined); - this.resetSearch(false); - - this.open = false; - this._results = []; - this._resultsSet.clear(); - this._visibleElements = []; - this._numTotalResults = -1; - this._endIndex = -1; - this._curRequest = undefined; - this._maxSearchIndex = 0; - } + onInputChange = action((e: React.ChangeEvent<HTMLInputElement>) => { + this._searchString = e.target.value; + this.submitSearch(); }); - enter = action((e: React.KeyboardEvent | undefined) => { + onSelectChange = action((e: React.ChangeEvent<HTMLSelectElement>) => { + this._docTypeString = e.target.value; + this.submitSearch(); + }); + + /*enter = action((e: React.KeyboardEvent | undefined) => { if (!e || e.key === "Enter") { - this.layoutDoc._searchString = this.newsearchstring; - this._pageStart = 0; - this.open = true; this.submitSearch(); } + });*/ + + onResultClick = action((doc: Doc) => { + this.selectElement(doc); + this._selectedResult = doc; }); @action @@ -126,21 +105,6 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc return finalDocs; } - static async foreachRecursiveDocAsync(docs: Doc[], func: (doc: Doc) => void) { - let newarray: Doc[] = []; - while (docs.length > 0) { - newarray = []; - await Promise.all(docs.filter(d => d).map(async d => { - const fieldKey = Doc.LayoutFieldKey(d); - const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView"); - const data = d[annos ? fieldKey + "-annotations" : fieldKey]; - const docs = await DocListCastAsync(data); - docs && newarray.push(...docs); - func(d); - })); - docs = newarray; - } - } static foreachRecursiveDoc(docs: Doc[], func: (doc: Doc) => void) { let newarray: Doc[] = []; while (docs.length > 0) { @@ -172,15 +136,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc }); this._results = found; - this.docsforfilter = this._results.map(r => r[0]); - this.setSearchFilter(selectedCollection, this.filter && found.length ? this.docsforfilter : undefined); - this._numTotalResults = found.length; - this.realTotalResults = found.length; } - else { - this._noResults = "No collection selected :("; - } - } static documentKeys(doc: Doc) { @@ -197,246 +153,253 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc @action submitSearch = async () => { - this.resetSearch(false); - - //this.props.Document._docFilters = new List(); - this._noResults = ""; + this.resetSearch(); this.dataDoc[this.fieldKey] = new List<Doc>([]); - this.children = 0; - let query = StrCast(this.layoutDoc._searchString); + let query = StrCast(this._searchString); Doc.SetSearchQuery(query); this._results = []; this._resultsSet.clear(); - this._visibleElements = []; - this._visibleDocuments = []; if (query) { - this._endIndex = 12; - this._maxSearchIndex = 0; - this._numTotalResults = -1; this.searchCollection(query); - runInAction(() => { - this.open = this._searchbarOpen = true; - this.resultsScrolled(); - }); } } - getAllResults = async (query: string) => { - return SearchUtil.Search(query, true, { fq: this.filterQuery, start: 0, rows: 10000000 }); - } + resetSearch = action(() => { + this._results.forEach(result => { + Doc.UnBrushDoc(result[0]); + Doc.ClearSearchMatches(); + }); + }); - private get filterQuery() { - const baseExpr = "NOT system_b:true"; - const authorExpr = undefined; - const includeDeleted = this._deletedDocsStatus ? "" : " NOT deleted_b:true"; - const typeExpr = this._onlyAliases ? "NOT {!join from=id to=proto_i}type_t:*" : `(type_t:* OR {!join from=id to=proto_i}type_t:*) ${this._blockedTypes.map(type => `NOT ({!join from=id to=proto_i}type_t:${type}) AND NOT type_t:${type}`).join(" AND ")}`; - // fq: type_t:collection OR {!join from=id to=proto_i}type_t:collection q:text_t:hello - return [baseExpr, authorExpr, includeDeleted, typeExpr].filter(q => q).join(" AND ").replace(/AND $/, ""); + selectElement = async (doc: Doc) => { + await DocumentManager.Instance.jumpToDocument(doc, true); // documents open in new tab instead of on right } - @computed get primarySort() { - const suffixMap = (type: ColumnType) => { - switch (type) { - case ColumnType.Date: return "_d"; - case ColumnType.String: return "_t"; - case ColumnType.Boolean: return "_b"; - case ColumnType.Number: return "_n"; + render() { + const results = this._results.map(result => { + var className = "searchBox-results-scroll-view-result"; + + if (this._selectedResult == result[0]) { + className += " searchBox-results-scroll-view-result-selected" } - }; - const headers = Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []); - return headers.reduce((p: Opt<string>, header: SchemaHeaderField) => p || (header.desc !== undefined && suffixMap(header.type) ? (header.heading + suffixMap(header.type) + (header.desc ? " desc" : " asc")) : undefined), undefined); - } - searchDatabase = async (query: string) => { - this._lockPromise && (await this._lockPromise); - this._lockPromise = new Promise(async res => { - while (this._results.length <= this._endIndex && (this._numTotalResults === -1 || this._maxSearchIndex < this._numTotalResults)) { - this._curRequest = SearchUtil.Search(query, true, { onlyAliases: true, allowAliases: true, /*sort: this.primarySort,*/ fq: this.filterQuery, start: 0, rows: this._numResultsPerPage, hl: "on", "hl.fl": "*", }).then(action(async (res: SearchUtil.DocSearchResult) => { - // happens at the beginning - this.realTotalResults = res.numFound <= 0 ? 0 : res.numFound; - if (res.numFound !== this._numTotalResults && this._numTotalResults === -1) { - this._numTotalResults = res.numFound; - } - const highlighting = res.highlighting || {}; - const highlightList = res.docs.map(doc => highlighting[doc[Id]]); - const lines = new Map<string, string[]>(); - res.docs.map((doc, i) => lines.set(doc[Id], res.lines[i])); - const docs = res.docs; - const highlights: typeof res.highlighting = {}; - docs.forEach((doc, index) => highlights[doc[Id]] = highlightList[index]); - const filteredDocs = this.filterDocsByType(docs); - - runInAction(() => filteredDocs.forEach((doc, i) => { - const index = this._resultsSet.get(doc); - const highlight = highlights[doc[Id]]; - const line = lines.get(doc[Id]) || []; - const hlights = highlight ? Object.keys(highlight).map(key => key.substring(0, key.length - 2)).filter(k => k) : []; - // if (this.findCommonElements(hlights)) { - // } - if (index === undefined) { - this._resultsSet.set(doc, this._results.length); - this._results.push([doc, hlights, line]); - } else { - this._results[index][1].push(...hlights); - this._results[index][2].push(...line); - } - })); - - this._curRequest = undefined; - })); - this._maxSearchIndex += this._numResultsPerPage; - - await this._curRequest; + if (this._docTypeString == "all" || this._docTypeString == result[0].type) { + return (<div key={result[0][Id]} onClick={() => this.onResultClick(result[0])} className={className}><div className="titletitle">{result[0].title}</div></div>) } - this.resultsScrolled(); + return null; + }) - const selectedCollection = this.currentSelectedCollection;//SelectionManager.SelectedDocuments()[0]; - this.docsforfilter = this._results.map(r => r[0]); - this.setSearchFilter(selectedCollection, this.filter ? this.docsforfilter : undefined); - res(); - }); - return this._lockPromise; + results.filter(result => result) + + return ( + <div style={{ pointerEvents: "all" }} className="searchBox-container"> + <div className="searchBox-bar"> + <select name="type" id="searchBox-type" className="searchBox-type" onChange={this.onSelectChange}> + <option value="all">All</option> + <option value="rtf">Text</option> + <option value="img">Image</option> + <option value="pdf">PDF</option> + </select> + <input defaultValue={""} autoComplete="off" onChange={this.onInputChange} type="text" placeholder="Search..." id="search-input" ref={this._inputRef} className="searchBox-input" /> + </div > + <div className="searchBox-results-container"> + <div className="searchBox-results-count"> + {`${results.length}` + " result" + (results.length == 1 ? "" : "s")} + </div> + <div className="searchBox-results-scroll-view"> + {results} + </div> + </div> + </div > + ); } +} + + + +/* +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { Tooltip } from '@material-ui/core'; +import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; +import { observer } from 'mobx-react'; +import * as React from 'react'; +import { Doc, DocListCast, Field, Opt, DocListCastAsync, DataSym, HeightSym, FieldsSym } from '../../../fields/Doc'; +import { documentSchema } from "../../../fields/documentSchemas"; +import { Copy, Id, ToString } from '../../../fields/FieldSymbols'; +import { List } from '../../../fields/List'; +import { createSchema, listSpec, makeInterface } from '../../../fields/Schema'; +import { SchemaHeaderField } from '../../../fields/SchemaHeaderField'; +import { Cast, NumCast, StrCast } from '../../../fields/Types'; +import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents, Utils } from '../../../Utils'; +import { Docs } from '../../documents/Documents'; +import { DocumentType } from "../../documents/DocumentTypes"; +import { CurrentUserUtils } from "../../util/CurrentUserUtils"; +import { SetupDrag } from '../../util/DragManager'; +import { SearchUtil } from '../../util/SearchUtil'; +import { Transform } from '../../util/Transform'; +import { CollectionDockingView } from "../collections/CollectionDockingView"; +import { CollectionSchemaView, ColumnType } from "../collections/CollectionSchemaView"; +import { CollectionViewType } from '../collections/CollectionView'; +import { ViewBoxBaseComponent } from "../DocComponent"; +import { FieldView, FieldViewProps } from '../nodes/FieldView'; +import "./SearchBox.scss"; +import { undoBatch } from "../../util/UndoManager"; +import { DocServer } from "../../DocServer"; +import { MainView } from "../MainView"; +import { SelectionManager } from "../../util/SelectionManager"; +import { CollectionSchemaBooleanCell } from "../collections/CollectionSchemaCells"; +import { transpileModule } from "typescript"; +import { DocumentManager } from "../../util/DocumentManager"; + +export const searchSchema = createSchema({ Document: Doc }); + +type SearchBoxDocument = makeInterface<[typeof documentSchema, typeof searchSchema]>; +const SearchBoxDocument = makeInterface(documentSchema, searchSchema); + +@observer +export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDocument>(SearchBoxDocument) { + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SearchBox, fieldKey); } + public static Instance: SearchBox; + + @observable _searchString = ""; + @observable _docTypeString = "all"; + @observable _results: [Doc, string[], string[]][] = []; + @observable _selectedResult: Doc | undefined = undefined; + @observable _deletedDocsStatus: boolean = false; + @observable _onlyAliases: boolean = true; - @action.bound - openSearch(e: React.SyntheticEvent) { - e.stopPropagation(); - this._results.forEach(result => Doc.BrushDoc(result[0])); + constructor(props: any) { + super(props); + SearchBox.Instance = this; } - resetSearch = action((close: boolean) => { - this._results.forEach(result => { - Doc.UnBrushDoc(result[0]); - Doc.ClearSearchMatches(); - }); - close && (this.open = this._searchbarOpen = false); + onInputChange = action((e: React.ChangeEvent<HTMLInputElement>) => { + this._searchString = e.target.value; + this.submitSearch(); }); - @action.bound - closeResults() { - this._results = []; - this._resultsSet.clear(); - this._visibleElements = []; - this._visibleDocuments = []; - this._numTotalResults = -1; - this._endIndex = -1; - this._curRequest = undefined; - } + onSelectChange = action((e: React.ChangeEvent<HTMLSelectElement>) => { + this._docTypeString = e.target.value; + this.submitSearch(); + }); - @action - resultsScrolled = (e?: React.UIEvent<HTMLDivElement>) => { - this._endIndex = 30; - const headers = new Set<string>(["title", "author", "text", "type", "data", "*lastModified", "context"]); + onResultClick = action((doc: Doc) => { + this.selectElement(doc); + this._selectedResult = doc; + }); - if (this._numTotalResults <= this._maxSearchIndex) { - this._numTotalResults = this._results.length; + static foreachRecursiveDoc(docs: Doc[], func: (doc: Doc) => void) { + const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.KVP, DocumentType.FILTER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING]; + let newarray: Doc[] = []; + while (docs.length > 0) { + newarray = []; + docs.filter(d => d).forEach(d => { + const dtype = StrCast(d.type, "string") as DocumentType; + if (dtype && !blockedTypes.includes(dtype)) { + const fieldKey = Doc.LayoutFieldKey(d); + const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView"); + const data = d[annos ? fieldKey + "-annotations" : fieldKey]; + data && newarray.push(...DocListCast(data)); + func(d); + } + }); + docs = newarray; } + } - // only hit right at the beginning - // visibleElements is all of the elements (even the ones you can't see) - if (this._visibleElements.length !== this._numTotalResults) { - // undefined until a searchitem is put in there - this._visibleElements = Array<JSX.Element>(this._numTotalResults === -1 ? 0 : this._numTotalResults); - this._visibleDocuments = Array<Doc>(this._numTotalResults === -1 ? 0 : this._numTotalResults); - } - let max = this._numResultsPerPage; - max > this._results.length ? max = this._results.length : console.log(""); - for (let i = this._pageStart; i < max; i++) { - //if the index is out of the window then put a placeholder in - //should ones that have already been found get set to placeholders? - - let result: [Doc, string[], string[]] | undefined = undefined; - - result = this._results[i]; - if (result) { - const highlights = Array.from([...Array.from(new Set(result[1]).values())]); - const lines = new List<string>(result[2]); - highlights.forEach((item) => headers.add(item)); - Doc.SetSearchMatch(result[0], { searchMatch: 1 }); - if (i < this._visibleDocuments.length) { - this._visibleDocuments[i] = result[0]; - Doc.BrushDoc(result[0]); - Doc.AddDocToList(this.dataDoc, this.props.fieldKey, result[0]); - this.children++; + @action + searchCollection(query: string) { + const selectedCollection = CollectionDockingView.Instance; + query = query.toLowerCase(); + + if (selectedCollection !== undefined) { + console.log("hello111") + // this._currentSelectedCollection = selectedCollection; + const docs = DocListCast(selectedCollection.dataDoc[Doc.LayoutFieldKey(selectedCollection.dataDoc)]); + const found: [Doc, string[], string[]][] = []; + SearchBox.foreachRecursiveDoc(docs, (doc: Doc) => { + console.log("HELLO") + if (this._docTypeString == "all" || this._docTypeString == doc.type) { + const hlights = new Set<string>(); + SearchBox.documentKeys(doc).forEach(key => Field.toString(doc[key] as Field).toLowerCase().includes(query) && hlights.add(key)); + Array.from(hlights.keys()).length > 0 && found.push([doc, Array.from(hlights.keys()), []]); } - } - } - if (this.props.Document._schemaHeaders === undefined) { - this.props.Document._schemaHeaders = new List<SchemaHeaderField>([new SchemaHeaderField("title", "#f1efeb")]); - } - if (this._maxSearchIndex >= this._numTotalResults) { - this._visibleElements.length = this._results.length; - this._visibleDocuments.length = this._results.length; + }); + + this._results = found; + //this.setSearchFilter(selectedCollection, this.filter && found.length ? this._docsforfilter : undefined); } } - getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight - panelHeight = () => this.props.PanelHeight(); - selectElement = (doc: Doc) => { /* this.gotoDocument(this.childDocs.indexOf(doc), NumCasst(this.layoutDoc._itemIndex)); */ }; - returnHeight = () => NumCast(this.layoutDoc._height); - returnLength = () => Math.min(window.innerWidth, 51 + 205 * Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []).length); - - setSearchFilter = action((collectionView: { props: { Document: Doc } }, docsForFilter: Doc[] | undefined) => { - if (collectionView) { - const docFilters = Cast(this.props.Document._docFilters, listSpec("string"), null); - collectionView.props.Document._searchFilterDocs = docsForFilter?.length ? new List<Doc>(docsForFilter) : undefined; - collectionView.props.Document._docFilters = docsForFilter?.length && docFilters?.length ? new List<string>(docFilters) : undefined; + static documentKeys(doc: Doc) { + const keys: { [key: string]: boolean } = {}; + // bcz: ugh. this is untracked since otherwise a large collection of documents will blast the server for all their fields. + // then as each document's fields come back, we update the documents _proxies. Each time we do this, the whole schema will be + // invalidated and re-rendered. This workaround will inquire all of the document fields before the options button is clicked. + // then by the time the options button is clicked, all of the fields should be in place. If a new field is added while this menu + // is displayed (unlikely) it won't show up until something else changes. + //TODO Types + Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => keys[key] = false)); + return Array.from(Object.keys(keys)); + } + + @action + submitSearch = async () => { + Doc.ClearSearchMatches(); + this._results = []; + + this.dataDoc[this.fieldKey] = new List<Doc>([]); + let query = StrCast(this._searchString); + Doc.SetSearchQuery(query); + this._results = []; + + if (query) { + this.searchCollection(query); } - }); + } + + selectElement = async (doc: Doc) => { + await DocumentManager.Instance.jumpToDocument(doc, true); // documents open in new tab instead of on right + } render() { - const myDashboards = DocListCast(CurrentUserUtils.MyDashboards.data); + const results = this._results.map(result => { + var className = "searchBox-results-scroll-view-result"; + + if (this._selectedResult == result[0]) { + className += " searchBox-results-scroll-view-result-selected" + } + + return (<div key={result[0][Id]} onClick={() => this.onResultClick(result[0])} className={className}><div className="titletitle">{result[0].title}</div></div>) + }) return ( <div style={{ pointerEvents: "all" }} className="searchBox-container"> - <div className="searchBox-bar" style={{ background: SearchBox.Instance._undoBackground }}> - <div className="searchBox-query" > - <input defaultValue={""} autoComplete="off" onChange={this.onChange} type="text" placeholder="Search..." id="search-input" ref={this._inputRef} - className="searchBox-barChild searchBox-input" onKeyPress={this.enter} - style={{ padding: 1, paddingLeft: 20, paddingRight: 60, color: "black", height: 20, width: 250 }} /> - <div style={{ display: "flex", alignItems: "center" }}> - <div style={{ position: "absolute", left: 10 }}> - <FontAwesomeIcon icon={"search"} size="lg" - style={{ color: "black", padding: 1, position: "relative" }} /> - </div> - <div style={{ position: "absolute", left: Doc.UserDoc().noviceMode ? 220 : 200, width: 30, zIndex: 9000, color: "grey", background: "white", }}> - {`${this._results.length}` + " of " + `${this.realTotalResults}`} - </div> - </div> - </div > + <div className="searchBox-bar"> + <select name="type" id="searchBox-type" className="searchBox-type" onChange={this.onSelectChange}> + <option value="all">All</option> + <option value="rtf">Text</option> + <option value="img">Img</option> + <option value="pdf">PDF</option> + <option value="video">Vid</option> + <option value="audio">Aud</option> + <option value="inks">Ink</option> + <option value="col">Col</option> + </select> + <input defaultValue={""} autoComplete="off" onChange={this.onInputChange} type="text" placeholder="Search..." id="search-input" className="searchBox-input" /> </div > - {!this._searchbarOpen ? (null) : - <div style={{ zIndex: 20000, color: "black" }} ref={(r) => r?.focus()}> - <div style={{ display: "flex", justifyContent: "center", }}> - <div style={{ display: this.open ? "flex" : "none", overflow: "auto", position: "absolute" }}> - <CollectionSchemaView {...this.props} - CollectionView={undefined} - addDocument={returnFalse} - Document={this.props.Document} - moveDocument={returnFalse} - removeDocument={returnFalse} - PanelHeight={this.open ? this.returnHeight : returnZero} - PanelWidth={this.open ? this.returnLength : returnZero} - scrollOverflow={length > window.innerWidth || this.children > 6 ? true : false} - focus={this.selectElement} - ScreenToLocalTransform={Transform.Identity} - /> - <div style={{ position: "absolute", right: 5, bottom: 7, width: 15, height: 15, }} - onPointerDown={e => setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => { - this.props.Document._height = NumCast(this.props.Document._height) + delta[1]; - return false; - }, returnFalse, emptyFunction)} - > - <FontAwesomeIcon icon="grip-lines" size="lg" /> - </div> - </div> - </div> + <div className="searchBox-results-container"> + <div className="searchBox-results-count"> + {`${this._results.length}` + " result" + (this._results.length == 1 ? "" : "s")} </div> - } + <div className="searchBox-results-scroll-view"> + {results} + </div> + </div> </div > ); } -}
\ No newline at end of file +}*/
\ No newline at end of file |