diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/util/CurrentUserUtils.ts | 52 | ||||
-rw-r--r-- | src/client/views/GlobalKeyHandler.ts | 8 | ||||
-rw-r--r-- | src/client/views/MainView.tsx | 35 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSchemaView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/collections/collectionSchema/SchemaTable.tsx | 3 | ||||
-rw-r--r-- | src/client/views/search/SearchBox.scss | 182 | ||||
-rw-r--r-- | src/client/views/search/SearchBox.tsx | 729 |
7 files changed, 383 insertions, 628 deletions
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index e1cbaf125..9a634017a 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -2,13 +2,14 @@ import { computed, observable, reaction } from "mobx"; import * as rp from 'request-promise'; import { DataSym, Doc, DocListCast, DocListCastAsync } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; +import { InkTool } from "../../fields/InkField"; import { List } from "../../fields/List"; import { PrefetchProxy } from "../../fields/Proxy"; import { RichTextField } from "../../fields/RichTextField"; import { listSpec } from "../../fields/Schema"; import { SchemaHeaderField } from "../../fields/SchemaHeaderField"; import { ComputedField, ScriptField } from "../../fields/ScriptField"; -import { BoolCast, Cast, NumCast, PromiseValue, StrCast, DateCast } from "../../fields/Types"; +import { BoolCast, Cast, DateCast, NumCast, PromiseValue, StrCast } from "../../fields/Types"; import { nullAudio } from "../../fields/URLField"; import { SharingPermissions } from "../../fields/util"; import { Utils } from "../../Utils"; @@ -19,6 +20,7 @@ import { Networking } from "../Network"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView"; import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; +import { Colors } from "../views/global/globalEnums"; import { MainView } from "../views/MainView"; import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox"; import { LabelBox } from "../views/nodes/LabelBox"; @@ -31,13 +33,10 @@ import { LinkManager } from "./LinkManager"; import { Scripting } from "./Scripting"; import { SearchUtil } from "./SearchUtil"; import { SelectionManager } from "./SelectionManager"; -import { UndoManager } from "./UndoManager"; -import { SnappingManager } from "./SnappingManager"; -import { InkTool } from "../../fields/InkField"; -import { SharingManager } from "./SharingManager"; -import { computedFn } from "mobx-utils"; import { ColorScheme } from "./SettingsManager"; -import { Colors } from "../views/global/globalEnums"; +import { SharingManager } from "./SharingManager"; +import { SnappingManager } from "./SnappingManager"; +import { UndoManager } from "./UndoManager"; export let resolvedPorts: { server: number, socket: number }; @@ -47,6 +46,7 @@ export class CurrentUserUtils { //TODO tfs: these should be temporary... private static mainDocId: string | undefined; + public static searchBtn: Doc; public static get id() { return this.curr_id; } public static get MainDocId() { return this.mainDocId; } public static set MainDocId(id: string | undefined) { this.mainDocId = id; } @@ -56,6 +56,7 @@ export class CurrentUserUtils { @observable public static GuestDashboard: Doc | undefined; @observable public static GuestMobile: Doc | undefined; @observable public static propertiesWidth: number = 0; + @observable public static searchPanelWidth: number = 0; // sets up the default User Templates - slideView, headerView static setupUserTemplateButtons(doc: Doc) { @@ -530,6 +531,7 @@ export class CurrentUserUtils { static async menuBtnDescriptions(doc: Doc) { return [ { title: "Dashboards", target: Cast(doc.myDashboards, Doc, null), icon: "desktop", click: 'selectMainMenu(self)' }, + { title: "Search", target: Cast(doc.mySearchPanel, Doc, null), icon: "search", click: 'selectMainMenu(self)' }, { title: "My Files", target: Cast(doc.myFilesystem, Doc, null), icon: "file", click: 'selectMainMenu(self)' }, { title: "Tools", target: Cast(doc.myTools, Doc, null), icon: "wrench", click: 'selectMainMenu(self)' }, { title: "Import", target: Cast(doc.myImportPanel, Doc, null), icon: "upload", click: 'selectMainMenu(self)' }, @@ -542,14 +544,6 @@ export class CurrentUserUtils { ]; } - static setupSearchPanel(doc: Doc) { - if (doc.mySearchPanelDoc === undefined) { - doc.mySearchPanelDoc = new PrefetchProxy(Docs.Create.SearchDocument({ - _width: 500, _height: 300, backgroundColor: "dimGray", ignoreClick: true, _searchDoc: true, - childDropAction: "alias", _lockedPosition: true, _viewType: CollectionViewType.Schema, title: "sidebar search stack", system: true - })) as any as Doc; - } - } static async setupMenuPanel(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { if (doc.menuStack === undefined) { await this.setupSharingSidebar(doc, sharingDocumentId, linkDatabaseId); // sets up the right sidebar collection for mobile upload documents and sharing @@ -569,10 +563,23 @@ export class CurrentUserUtils { _height: 60, watchedDocuments, onClick: ScriptField.MakeScript(click, { scriptContext: "any" }) - })); + }) + ); + + menuBtns.forEach(menuBtn => { + if (menuBtn.title == "Search") { + this.searchBtn = menuBtn; + } + }); // hack -- last button is assumed to be the userDoc menuBtns[menuBtns.length - 1].hidden = ComputedField.MakeFunction("IsNoviceMode()"); + menuBtns.forEach(menuBtn => { + if (menuBtn.title == "Search") { + doc.searchBtn = menuBtn + } + }) + doc.menuStack = new PrefetchProxy(Docs.Create.StackingDocument(menuBtns, { title: "menuItemPanel", childDropAction: "alias", @@ -819,6 +826,7 @@ export class CurrentUserUtils { (doc.myRecentlyClosedDocs as any as Doc).contextMenuLabels = new List<string>(["Clear All"]); } } + static setupFilterDocs(doc: Doc) { // setup Filter item if (doc.currentFilter === undefined) { @@ -953,6 +961,16 @@ export class CurrentUserUtils { } } + // Search sidebar is where searches within the document are performed + static setupSearchSidebar(doc: Doc) { + 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: "Search Sidebar", system: true + })) as any as Doc; + } + } + static setupClickEditorTemplates(doc: Doc) { if (doc["clickFuncs-child"] === undefined) { // to use this function, select it from the context menu of a collection. then edit the onChildClick script. Add two Doc variables: 'target' and 'thisContainer', then assign 'target' to some target collection. After that, clicking on any document in the initial collection will open it in the target @@ -1041,8 +1059,8 @@ export class CurrentUserUtils { this.setupDefaultIconTemplates(doc); // creates a set of icon templates triggered by the document deoration icon this.setupDocTemplates(doc); // sets up the template menu of templates this.setupImportSidebar(doc); + this.setupSearchSidebar(doc); // sets up the search sidebar this.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile - this.setupSearchPanel(doc); this.setupOverlays(doc); // documents in overlay layer this.setupDockedButtons(doc); // the bottom bar of font icons await this.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 76eb4c142..0127d3080 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -27,7 +27,6 @@ import { LightboxView } from "./LightboxView"; import { MainView } from "./MainView"; import { DocumentLinksButton } from "./nodes/DocumentLinksButton"; import { AnchorMenu } from "./pdf/AnchorMenu"; -import { SearchBox } from "./search/SearchBox"; import { CurrentUserUtils } from "../util/CurrentUserUtils"; import { SettingsManager } from "../util/SettingsManager"; @@ -223,8 +222,11 @@ export class KeyManager { PromiseValue(Cast(Doc.UserDoc()["tabs-button-tools"], Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv })); break; case "f": - SearchBox.Instance._searchFullDB = "My Stuff"; - SearchBox.Instance.enter(undefined); + const searchBtn = Doc.UserDoc().searchBtn as Doc; + + if (searchBtn) { + MainView.Instance.selectMenu(searchBtn); + } break; case "o": const target = SelectionManager.Views()[0]; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 005e46836..6ff8825ed 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -13,7 +13,7 @@ import { List } from '../../fields/List'; import { PrefetchProxy } from '../../fields/Proxy'; import { BoolCast, PromiseValue, StrCast } from '../../fields/Types'; import { TraceMobx } from '../../fields/util'; -import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils'; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; import { DocServer } from '../DocServer'; import { Docs, DocUtils } from '../documents/Documents'; @@ -29,28 +29,27 @@ import { SettingsManager } from '../util/SettingsManager'; import { SharingManager } from '../util/SharingManager'; import { SnappingManager } from '../util/SnappingManager'; import { Transform } from '../util/Transform'; -import { undoBatch, UndoManager } from '../util/UndoManager'; import { TimelineMenu } from './animationtimeline/TimelineMenu'; import { CollectionDockingView } from './collections/CollectionDockingView'; import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOptionsMenu'; import { CollectionLinearView } from './collections/CollectionLinearView'; import { CollectionMenu } from './collections/CollectionMenu'; import { CollectionViewType } from './collections/CollectionView'; +import "./collections/TreeView.scss"; import { ContextMenu } from './ContextMenu'; import { DictationOverlay } from './DictationOverlay'; import { DocumentDecorations } from './DocumentDecorations'; import { GestureOverlay } from './GestureOverlay'; import { MENU_PANEL_WIDTH, SEARCH_PANEL_HEIGHT } from './global/globalCssVariables.scss'; +import { Colors } from './global/globalEnums'; import { KeyManager } from './GlobalKeyHandler'; import { InkStrokeProperties } from './InkStrokeProperties'; import { LightboxView } from './LightboxView'; import { LinkMenu } from './linking/LinkMenu'; import "./MainView.scss"; -import "./collections/TreeView.scss"; import { AudioBox } from './nodes/AudioBox'; import { DocumentLinksButton } from './nodes/DocumentLinksButton'; -import { DocumentView, DocumentViewProps, DocAfterFocusFunc } from './nodes/DocumentView'; -import { FieldViewProps } from './nodes/FieldView'; +import { DocumentView } from './nodes/DocumentView'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup'; import { LinkDocPreview } from './nodes/LinkDocPreview'; @@ -61,10 +60,8 @@ import { OverlayView } from './OverlayView'; import { AnchorMenu } from './pdf/AnchorMenu'; import { PreviewCursor } from './PreviewCursor'; import { PropertiesView } from './PropertiesView'; -import { SearchBox } from './search/SearchBox'; -import { DefaultStyleProvider, DashboardStyleProvider, StyleProp } from './StyleProvider'; +import { DashboardStyleProvider, DefaultStyleProvider } from './StyleProvider'; import { TopBar } from './topbar/TopBar'; -import { Colors } from './global/globalEnums'; const _global = (window /* browser */ || global /* node */) as any; @observer @@ -182,12 +179,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) { - // 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); - // } + /*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(); } @@ -394,10 +391,6 @@ export class MainView extends React.Component { case "Settings": SettingsManager.Instance.open(); break; - case "Catalog": - SearchBox.Instance._searchFullDB = "My Stuff"; - SearchBox.Instance.enter(undefined); - break; case "Help": break; default: @@ -537,7 +530,7 @@ export class MainView extends React.Component { </svg>; } - @computed get search() { + @computed get topbar() { TraceMobx(); return <div className="mainView-topbar"> <TopBar /> @@ -591,7 +584,7 @@ export class MainView extends React.Component { <GroupManager /> <GoogleAuthenticationManager /> <DocumentDecorations boundsLeft={this.leftOffset} boundsTop={this.topOffset} /> - {this.search} + {this.topbar} {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null} {DocumentLinksButton.LinkEditorDocView ? <LinkMenu docView={DocumentLinksButton.LinkEditorDocView} changeFlyout={emptyFunction} /> : (null)} {LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : (null)} @@ -609,7 +602,7 @@ export class MainView extends React.Component { {this.snapLines} <div className="mainView-webRef" ref={this.makeWebRef} /> <LightboxView PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} /> - </div>); + </div >); } makeWebRef = (ele: HTMLDivElement) => { diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 380f82813..1efea96be 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -556,7 +556,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { style={{ overflow: this.props.scrollOverflow === true ? "scroll" : undefined, backgroundColor: "white", pointerEvents: this.props.Document._searchDoc !== undefined && !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined, - width: name === "collectionSchemaView-searchContainer" ? "auto" : this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative", + width: this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative", }} > <div className="collectionSchemaView-tableContainer" style={{ width: `calc(100% - ${this.previewWidth()}px)` }} diff --git a/src/client/views/collections/collectionSchema/SchemaTable.tsx b/src/client/views/collections/collectionSchema/SchemaTable.tsx index de08c327a..abe549072 100644 --- a/src/client/views/collections/collectionSchema/SchemaTable.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTable.tsx @@ -197,6 +197,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> { <div onClick={e => this.changeSorting(col)} style={{ width: 21, padding: 1, display: "inline", zIndex: 1, background: "inherit", cursor: "hand" }}> <FontAwesomeIcon icon={sortIcon} size="lg" /> </div> + {this.props.Document._chromeHidden || this.props.addDocument == returnFalse ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>} </div>; return { @@ -561,7 +562,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> { onPointerDown={this.props.onPointerDown} onClick={this.props.onClick} onWheel={e => this.props.active(true) && e.stopPropagation()} onDrop={e => this.props.onDrop(e, {})} onContextMenu={this.onContextMenu} > {this.reactTable} - {this.props.Document._chromeHidden ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>} + {this.props.Document._chromeHidden || this.props.addDocument == returnFalse ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>} {!this._showDoc ? (null) : <div className="collectionSchemaView-documentPreview" ref="overlay" style={{ diff --git a/src/client/views/search/SearchBox.scss b/src/client/views/search/SearchBox.scss index 6a2fe6f19..3d173f115 100644 --- a/src/client/views/search/SearchBox.scss +++ b/src/client/views/search/SearchBox.scss @@ -2,141 +2,95 @@ @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: $dark-gray; + 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: 55px; + outline: none; + padding: 1px 5px 1px 5px; + color: black; + height: 25px; + border: 1px solid black; + border-right: 0px; + } - &: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% - 55px); + 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:$dark-gray; - 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-result-title { + display: relative; + float: left; + width: calc(100% - 60px); + text-align: left; + } - &.searchBox-close { - color: $white; - max-height: $searchpanel-height; + .searchBox-result-type { + font-size: 12px; + display: relative; + float: right; + width: 60px; + text-align: right; + color: #333; + } } } - } -} - -.searchBox-results { - display: flex; - flex-direction: column; - top: 300px; - display: flex; - flex-direction: column; - height: 100%; - overflow: visible; - - .no-result { - width: 500px; - background: $light-gray; - 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 c72b25040..51b1319d4 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -1,205 +1,73 @@ -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { Tooltip } from '@material-ui/core'; -import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; +import { action, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { Doc, DocListCast, Field, Opt, DocListCastAsync } from '../../../fields/Doc'; +import { Doc, DocListCast, Field } from '../../../fields/Doc'; import { documentSchema } from "../../../fields/documentSchemas"; -import { Copy, Id } 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 { Id } from '../../../fields/FieldSymbols'; +import { createSchema, makeInterface } from '../../../fields/Schema'; +import { StrCast } from '../../../fields/Types'; 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 { DocumentManager } from "../../util/DocumentManager"; import { CollectionDockingView } from "../collections/CollectionDockingView"; -import { CollectionSchemaView, ColumnType } from "../collections/collectionSchema/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"; export const searchSchema = createSchema({ Document: Doc }); type SearchBoxDocument = makeInterface<[typeof documentSchema, typeof searchSchema]>; const SearchBoxDocument = makeInterface(documentSchema, searchSchema); +const selectValues = ["all", "rtf", "image", "pdf", "web", "video", "audio", "collection"] + @observer export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDocument>(SearchBoxDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SearchBox, fieldKey); } 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 = ""; - private collectionRef = React.createRef<HTMLDivElement>(); - - - @observable _undoBackground: string | undefined = ""; - @observable _icons: string[] = this._allIcons; + @observable _searchString = ""; + @observable _docTypeString = "all"; @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 _searchFullDB = "DB"; // "DB" means searh the entire database. "My Stuff" adds a flag that selects only documents that the current user has authored - @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) { super(props); SearchBox.Instance = this; + this._searchString = "reset_search"; + this.submitSearch(); + this._searchString = ""; + this.submitSearch(); } componentDidMount = action(() => { 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() { - Object.values(this._disposers).forEach(disposer => disposer?.()); + this.resetSearch; } - @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) => { - if (!e || e.key === "Enter") { - this.layoutDoc._searchString = this.newsearchstring; - this._pageStart = 0; - this.open = StrCast(this.layoutDoc._searchString) !== "" || this._searchFullDB !== "DB"; - this.submitSearch(); - } + onSelectChange = action((e: React.ChangeEvent<HTMLSelectElement>) => { + this._docTypeString = e.target.value; }); - getFinalQuery(query: string): string { - //alters the query so it looks in the correct fields - //if this is true, then not all of the field boxes are checked - //TODO: data - const initialfilters = Cast(this.props.Document._docFilters, listSpec("string"), []); - - const filters: string[] = []; - - for (const initFilter of initialfilters) { - const fields = initFilter.split(":"); - if (fields[2] !== undefined) { - filters.push(fields[0]); - filters.push(fields[1]); - filters.push(fields[2]); - } - } - - const finalfilters: { [key: string]: string[] } = {}; - - for (let i = 0; i < filters.length; i = i++) { - const fields = filters[i].split(":"); - if (finalfilters[fields[0]] !== undefined) { - finalfilters[fields[0]].push(fields[1]); - } - else { - finalfilters[fields[0]] = [fields[1]]; - } - } - - for (const key in finalfilters) { - const values = finalfilters[key]; - if (values.length === 1) { - const mod = "_t:"; - const newWords: string[] = []; - const oldWords = values[0].split(" "); - oldWords.forEach((word, i) => i === 0 ? newWords.push(key + mod + word) : newWords.push("AND " + key + mod + word)); - query = `(${query}) AND (${newWords.join(" ")})`; - } - else { - for (let i = 0; i < values.length; i++) { - const mod = "_t:"; - const newWords: string[] = []; - const oldWords = values[i].split(" "); - oldWords.forEach((word, i) => i === 0 ? newWords.push(key + mod + word) : newWords.push("AND " + key + mod + word)); - const v = "(" + newWords.join(" ") + ")"; - if (i === 0) { - query = `(${query}) AND (${v}` + (values.length === 1 ? ")" : ""); - } - else query = query + " OR " + v + (i === values.length - 1 ? ")" : ""); - } - } - } - - return query.replace(/-\s+/g, ''); - } - - @action - filterDocsByType(docs: Doc[]) { - const finalDocs: Doc[] = []; - docs.forEach(doc => { - const layoutresult = StrCast(doc.type, "string") as DocumentType; - if (layoutresult && !this._blockedTypes.includes(layoutresult) && this._icons.includes(layoutresult)) { - finalDocs.push(doc); - } - }); - return finalDocs; - } + onResultClick = action((doc: Doc) => { + this.selectElement(doc); + this._selectedResult = doc; + }); - 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) { @@ -215,31 +83,39 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc } } + static formatType(type: String): String { + if (type == "pdf") { + return "PDF"; + } + else if (type == "image") { + return "Img"; + } + + return type.charAt(0).toUpperCase() + type.substring(1, 3); + } + @action searchCollection(query: string) { - const selectedCollection = this.currentSelectedCollection;//SelectionManager.SelectedDocuments()[0]; + const selectedCollection = CollectionDockingView.Instance; + const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.KVP, DocumentType.FILTER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING]; query = query.toLowerCase(); + this._results = [] + if (selectedCollection !== undefined) { - // this._currentSelectedCollection = selectedCollection; const docs = DocListCast(selectedCollection.dataDoc[Doc.LayoutFieldKey(selectedCollection.dataDoc)]); - const found: [Doc, string[], string[]][] = []; SearchBox.foreachRecursiveDoc(docs, (doc: Doc) => { - 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()), []]); + const dtype = StrCast(doc.type, "string") as DocumentType; + if (dtype && !blockedTypes.includes(dtype)) { + 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 && this._results.push([doc, Array.from(hlights.keys()), []]); + } }); - - 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 :("; } + this._results = Array.from(new Set(this._results)) + this._selectedResult = undefined } static documentKeys(doc: Doc) { @@ -256,347 +132,258 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc @action submitSearch = async () => { - this.resetSearch(false); + this.resetSearch(); - //this.props.Document._docFilters = new List(); - this._noResults = ""; - - this.dataDoc[this.fieldKey] = new List<Doc>([]); - this.children = 0; - let query = StrCast(this.layoutDoc._searchString); + //this.dataDoc[this.fieldKey] = new List<Doc>([]); + let query = StrCast(this._searchString); Doc.SetSearchQuery(query); - this._searchFullDB && (query = this.getFinalQuery(query)); this._results = []; this._resultsSet.clear(); - this._visibleElements = []; - this._visibleDocuments = []; - - if (query || this._searchFullDB === "My Stuff") { - this._endIndex = 12; - this._maxSearchIndex = 0; - this._numTotalResults = -1; - this._searchFullDB ? await this.searchDatabase(query) : this.searchCollection(query); - runInAction(() => { - this.open = this._searchbarOpen = true; - this.resultsScrolled(); - }); + + if (query) { + this.searchCollection(query); } } - 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.UnHighlightDoc(result[0]); + Doc.ClearSearchMatches(); + }); + }); - private get filterQuery() { - const baseExpr = "NOT system_b:true"; - const authorExpr = this._searchFullDB === "My Stuff" ? ` author_t:${Doc.CurrentUserEmail}` : 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() { + var validResults = 0; + + 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) { + validResults++; + return (<div key={result[0][Id]} onClick={() => this.onResultClick(result[0])} className={className}><div className="searchBox-result-title">{result[0].title}</div><div className="searchBox-result-type">{SearchBox.formatType(StrCast(result[0].type))}</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); - startDragCollection = async () => { - const res = await this.getAllResults(this.getFinalQuery(StrCast(this.layoutDoc._searchString))); - const filtered = this.filterDocsByType(res.docs); - const docs = filtered.map(doc => Doc.GetT(doc, "isPrototype", "boolean", true) ? Doc.MakeDelegate(doc) : Doc.MakeAlias(doc)); - let x = 0; - let y = 0; - for (const doc of docs.map(d => Doc.Layout(d))) { - doc.x = x; - doc.y = y; - const size = 200; - const aspect = Doc.NativeHeight(doc) / (Doc.NativeWidth(doc) || 1); - if (aspect > 1) { - doc._height = size; - doc._width = size / aspect; - } else if (aspect > 0) { - doc._width = size; - doc._height = size * aspect; - } else { - doc._width = size; - doc._height = size; - } - x += 250; - if (x > 1000) { - x = 0; - y += 300; - } - } - const headers = Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []).map(h => { const v = h[Copy](); v.color = "#f1efeb"; return v; }); - return Docs.Create.SchemaDocument(headers, DocListCast(this.dataDoc[this.fieldKey]), { _autoHeight: true, _viewType: CollectionViewType.Schema, title: StrCast(this.layoutDoc._searchString) }); + const selectOptions = selectValues.map(value => { + return <option key={value} value={value}>{SearchBox.formatType(value)}</option> + }) + + return ( + <div style={{ pointerEvents: "all" }} className="searchBox-container"> + <div className="searchBox-bar"> + <select name="type" id="searchBox-type" className="searchBox-type" onChange={this.onSelectChange}> + {selectOptions} + </select> + <input defaultValue={""} autoComplete="off" onChange={this.onInputChange} type="text" placeholder="Search..." id="search-input" className="searchBox-input" ref={this._inputRef} /> + </div > + <div className="searchBox-results-container"> + <div className="searchBox-results-count"> + {`${validResults}` + " result" + (validResults == 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 }); - @action.bound - openSearch(e: React.SyntheticEvent) { - e.stopPropagation(); - this._results.forEach(result => Doc.BrushDoc(result[0])); +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; + + 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); + 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 - changeSearchScope = (scope: string) => { - this.docsforfilter = undefined; - this.setSearchFilter(this.currentSelectedCollection, undefined); - this._searchFullDB = scope; + submitSearch = async () => { + Doc.ClearSearchMatches(); + this._results = []; + this.dataDoc[this.fieldKey] = new List<Doc>([]); - this.submitSearch(); - } + let query = StrCast(this._searchString); + Doc.SetSearchQuery(query); + this._results = []; - @computed get scopeButtons() { - return <div style={{ height: 25, paddingLeft: "4px", paddingRight: "4px" }}> - <form className="beta" style={{ justifyContent: "space-evenly", display: "flex" }}> - <div style={{ display: "contents" }}> - <div className="radio" style={{ margin: 0 }}> - <label style={{ fontSize: 12, marginTop: 6 }} > - <input type="radio" style={{ marginLeft: -16, marginTop: -1 }} checked={!this._searchFullDB} onChange={() => this.changeSearchScope("")} /> - Dashboard - </label> - </div> - <div className="radio" style={{ margin: 0 }}> - <label style={{ fontSize: 12, marginTop: 6 }} > - <input type="radio" style={{ marginLeft: -16, marginTop: -1 }} checked={this._searchFullDB?.length ? true : false} onChange={() => this.changeSearchScope("DB")} /> - DB - <span onClick={action(() => this._searchFullDB = this._searchFullDB === "My Stuff" ? "DB" : "My Stuff")}> - {this._searchFullDB === "My Stuff" ? "(me)" : "(full)"} - </span> - </label> - </div> - </div> - </form> - </div>; + if (query) { + this.searchCollection(query); + } } - 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; - } - }); + 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-lozenges" > - <div className="searchBox-lozenge-user"> - {`${Doc.CurrentUserEmail}`} - <div className="searchBox-logoff" onClick={() => window.location.assign(Utils.prepend("/logout"))}> - Logoff - </div> - </div> - <div className="searchBox-lozenge" onClick={() => DocServer.UPDATE_SERVER_CACHE()}> - {`UI project`} - </div> - <div className="searchBox-lozenge-dashboard" > - <select className="searchBox-dashSelect" onChange={e => CurrentUserUtils.openDashboard(Doc.UserDoc(), myDashboards[Number(e.target.value)])} - value={myDashboards.indexOf(CurrentUserUtils.ActiveDashboard)}> - {myDashboards.map((dash, i) => <option key={dash[Id]} value={i} style={{ backgroundColor: "black" }}> {StrCast(dash.title)} </option>)} - </select> - <div className="searchBox-dashboards" onClick={undoBatch(() => CurrentUserUtils.createNewDashboard(Doc.UserDoc()))}> - New - </div> - <div className="searchBox-dashboards" onClick={undoBatch(() => CurrentUserUtils.snapshotDashboard(Doc.UserDoc()))}> - Snapshot - </div> - </div> - </div> - <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 }}> - <Tooltip title={<div className="dash-tooltip" >drag search results as collection</div>}> - <div ref={this.collectionRef}><FontAwesomeIcon onPointerDown={SetupDrag(this.collectionRef, () => StrCast(this.layoutDoc._searchString) ? this.startDragCollection() : undefined)} icon={"search"} size="lg" - style={{ cursor: "hand", color: "black", padding: 1, position: "relative" }} /></div> - </Tooltip> - </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> - {Doc.UserDoc().noviceMode ? (null) : <div style={{ cursor: "default", left: 235, position: "absolute", }}> - <Tooltip title={<div className="dash-tooltip" >only display documents matching search</div>} > - <div> - <FontAwesomeIcon icon={"filter"} size="lg" - style={{ cursor: "hand", padding: 1, backgroundColor: this.filter ? "white" : "lightgray", color: this.filter ? "black" : "white" }} - onPointerDown={e => { e.stopPropagation(); SetupDrag(this.collectionRef, () => this.layoutDoc._searchString ? this.startDragCollection() : undefined); }} - onClick={action(() => this.setSearchFilter(this.currentSelectedCollection, this.filter ? undefined : this.docsforfilter))} /> - </div> - </Tooltip> - </div>} - {this.scopeButtons} - </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 |