aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordg314 <david_grossman@brown.edu>2021-07-29 15:12:03 -0400
committerdg314 <david_grossman@brown.edu>2021-07-29 15:12:03 -0400
commitfc1559fd74bde212f418b27806b68a76748153ad (patch)
tree825cad086d296fe189363fca480825feae60d8be
parent82c9db3e24baddeccafb67d2edc1a834f508f95a (diff)
search panel updates
-rw-r--r--src/client/util/CurrentUserUtils.ts11
-rw-r--r--src/client/views/MainView.tsx6
-rw-r--r--src/client/views/search/SearchBox.scss173
-rw-r--r--src/client/views/search/SearchBox.tsx491
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