diff options
| author | andrewdkim <adkim414@gmail.com> | 2019-11-12 17:30:02 -0500 |
|---|---|---|
| committer | andrewdkim <adkim414@gmail.com> | 2019-11-12 17:30:02 -0500 |
| commit | fb3a8258298589b911c73c18fdb086c8815e27c8 (patch) | |
| tree | c97973e8cc2d828b9ce7486b421fdcf2985c49e2 /src/client/views/search | |
| parent | 4d4db3aecf54a501a981c239feacdfddfe71152c (diff) | |
| parent | ecbe527575bab2cb5f1ced278039ec0a6fc50809 (diff) | |
merge from master
Diffstat (limited to 'src/client/views/search')
| -rw-r--r-- | src/client/views/search/FilterBox.tsx | 4 | ||||
| -rw-r--r-- | src/client/views/search/IconBar.tsx | 16 | ||||
| -rw-r--r-- | src/client/views/search/IconButton.scss | 2 | ||||
| -rw-r--r-- | src/client/views/search/SearchBox.scss | 22 | ||||
| -rw-r--r-- | src/client/views/search/SearchBox.tsx | 32 | ||||
| -rw-r--r-- | src/client/views/search/SearchItem.scss | 50 | ||||
| -rw-r--r-- | src/client/views/search/SearchItem.tsx | 43 |
7 files changed, 90 insertions, 79 deletions
diff --git a/src/client/views/search/FilterBox.tsx b/src/client/views/search/FilterBox.tsx index da733d64b..b841190d4 100644 --- a/src/client/views/search/FilterBox.tsx +++ b/src/client/views/search/FilterBox.tsx @@ -33,7 +33,7 @@ export enum Keys { export class FilterBox extends React.Component { static Instance: FilterBox; - public _allIcons: string[] = [DocumentType.AUDIO, DocumentType.COL, DocumentType.HIST, DocumentType.IMG, DocumentType.LINK, DocumentType.PDF, DocumentType.TEXT, DocumentType.VID, DocumentType.WEB]; + public _allIcons: string[] = [DocumentType.AUDIO, DocumentType.COL, DocumentType.IMG, DocumentType.LINK, DocumentType.PDF, DocumentType.TEXT, DocumentType.VID, DocumentType.WEB]; //if true, any keywords can be used. if false, all keywords are required. //this also serves as an indicator if the word status filter is applied @@ -393,7 +393,7 @@ export class FilterBox extends React.Component { <div> <div style={{ display: "flex", flexDirection: "row-reverse" }}> <SearchBox /> - {this.getActiveFilters()} + {/* {this.getActiveFilters()} */} </div> {this._filterOpen ? ( <div className="filter-form" onPointerDown={this.stopProp} id="filter-form" style={this._filterOpen ? { display: "flex" } : { display: "none" }}> diff --git a/src/client/views/search/IconBar.tsx b/src/client/views/search/IconBar.tsx index c9924222f..cff397407 100644 --- a/src/client/views/search/IconBar.tsx +++ b/src/client/views/search/IconBar.tsx @@ -59,23 +59,9 @@ export class IconBar extends React.Component { render() { return ( <div className="icon-bar"> - <div className="type-outer"> - <div className={"type-icon all"} - onClick={this.selectAll}> - <FontAwesomeIcon className="fontawesome-icon" icon={faCheckCircle} /> - </div> - <div className="filter-description">Select All</div> - </div> {FilterBox.Instance._allIcons.map((type: string) => - <IconButton type={type} /> + <IconButton key={type.toString()} type={type} /> )} - <div className="type-outer"> - <div className={"type-icon none"} - onClick={this.resetSelf}> - <FontAwesomeIcon className="fontawesome-icon" icon={faTimesCircle} /> - </div> - <div className="filter-description">Clear</div> - </div> </div> ); } diff --git a/src/client/views/search/IconButton.scss b/src/client/views/search/IconButton.scss index d1853177e..4a3107676 100644 --- a/src/client/views/search/IconButton.scss +++ b/src/client/views/search/IconButton.scss @@ -35,7 +35,7 @@ text-align: center; height: 15px; margin-top: 5px; - opacity: 0; + opacity: 1; -webkit-transition: all 0.2s ease-in-out; -moz-transition: all 0.2s ease-in-out; -o-transition: all 0.2s ease-in-out; diff --git a/src/client/views/search/SearchBox.scss b/src/client/views/search/SearchBox.scss index 5ed33a596..bc11604a5 100644 --- a/src/client/views/search/SearchBox.scss +++ b/src/client/views/search/SearchBox.scss @@ -1,6 +1,15 @@ @import "../globalCssVariables"; @import "./NaviconButton.scss"; +.searchBox-container { + display: flex; + flex-direction: column; + width:100%; + height:100%; + position: absolute; + font-size: 10px; + line-height: 1; +} .searchBox-bar { height: 32px; display: flex; @@ -34,6 +43,9 @@ &.searchBox-filter { align-self: stretch; + } + + &.searchBox-submit { margin-left: 2px; margin-right: 2px } @@ -45,12 +57,18 @@ } } +.searchBox-quickFilter { + width: 100%; + height: 40px; + margin-top: 10px; +} + .searchBox-results { - margin-right: 136px; + display:flex; + flex-direction: column; top: 300px; display: flex; flex-direction: column; - margin-right: 72px; // height: 560px; height: 100%; // overflow: hidden; diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 0d50124dd..899a35f48 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -18,6 +18,8 @@ import { FilterBox } from './FilterBox'; import "./FilterBox.scss"; import "./SearchBox.scss"; import { SearchItem } from './SearchItem'; +import { IconBar } from './IconBar'; +import { string } from 'prop-types'; library.add(faTimes); @@ -27,7 +29,7 @@ export class SearchBox extends React.Component { @observable private _searchString: string = ""; @observable private _resultsOpen: boolean = false; @observable private _searchbarOpen: boolean = false; - @observable private _results: [Doc, string[]][] = []; + @observable private _results: [Doc, string[], string[]][] = []; private _resultsSet = new Map<Doc, number>(); @observable private _openNoResults: boolean = false; @observable private _visibleElements: JSX.Element[] = []; @@ -139,7 +141,7 @@ export class SearchBox extends React.Component { private get filterQuery() { const types = FilterBox.Instance.filterTypes; const includeDeleted = FilterBox.Instance.getDataStatus(); - return "NOT baseProto_b:true" + (includeDeleted ? "" : " AND NOT deleted_b:true") + (types ? ` AND (${types.map(type => `({!join from=id to=proto_i}type_t:"${type}" AND NOT type_t:*) OR type_t:"${type}"`).join(" ")})` : ""); + return "NOT baseProto_b:true" + (includeDeleted ? "" : " AND NOT deleted_b:true") + (types ? ` AND (${types.map(type => `({!join from=id to=proto_i}type_t:"${type}" AND NOT type_t:*) OR type_t:"${type}" OR type_t:"extension"`).join(" ")})` : ""); } @@ -159,6 +161,8 @@ export class SearchBox extends React.Component { 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 = await Promise.all(res.docs.map(async doc => (await Cast(doc.extendsDoc, Doc)) || doc)); const highlights: typeof res.highlighting = {}; docs.forEach((doc, index) => highlights[doc[Id]] = highlightList[index]); @@ -168,12 +172,14 @@ export class SearchBox extends React.Component { filteredDocs.forEach(doc => { 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)) : []; if (index === undefined) { this._resultsSet.set(doc, this._results.length); - this._results.push([doc, hlights]); + this._results.push([doc, hlights, line]); } else { this._results[index][1].push(...hlights); + this._results[index][2].push(...line); } }); }); @@ -205,7 +211,7 @@ export class SearchBox extends React.Component { }); let x = 0; let y = 0; - for (const doc of docs) { + for (const doc of docs.map(d => Doc.Layout(d))) { doc.x = x; doc.y = y; const size = 200; @@ -231,7 +237,7 @@ export class SearchBox extends React.Component { } @action.bound - openSearch(e: React.PointerEvent) { + openSearch(e: React.SyntheticEvent) { e.stopPropagation(); this._openNoResults = false; FilterBox.Instance.closeFilter(); @@ -296,13 +302,13 @@ export class SearchBox extends React.Component { } else { if (this._isSearch[i] !== "search") { - let result: [Doc, string[]] | undefined = undefined; + let result: [Doc, string[], string[]] | undefined = undefined; if (i >= this._results.length) { this.getResults(this._searchString); if (i < this._results.length) result = this._results[i]; if (result) { let highlights = Array.from([...Array.from(new Set(result[1]).values())]).filter(v => v !== "search_string"); - this._visibleElements[i] = <SearchItem doc={result[0]} query={this._searchString} key={result[0][Id]} highlighting={highlights} />; + this._visibleElements[i] = <SearchItem doc={result[0]} query={this._searchString} key={result[0][Id]} lines={result[2]} highlighting={highlights} />; this._isSearch[i] = "search"; } } @@ -310,7 +316,7 @@ export class SearchBox extends React.Component { result = this._results[i]; if (result) { let highlights = Array.from([...Array.from(new Set(result[1]).values())]).filter(v => v !== "search_string"); - this._visibleElements[i] = <SearchItem doc={result[0]} query={this._searchString} key={result[0][Id]} highlighting={highlights} />; + this._visibleElements[i] = <SearchItem doc={result[0]} query={this._searchString} key={result[0][Id]} lines={result[2]} highlighting={highlights} />; this._isSearch[i] = "search"; } } @@ -337,12 +343,14 @@ export class SearchBox extends React.Component { <FontAwesomeIcon icon="object-group" size="lg" /> </span> <input value={this._searchString} onChange={this.onChange} type="text" placeholder="Search..." id="search-input" ref={this.inputRef} - className="searchBox-barChild searchBox-input" onPointerDown={this.openSearch} onKeyPress={this.enter} + className="searchBox-barChild searchBox-input" onPointerDown={this.openSearch} onKeyPress={this.enter} onFocus={this.openSearch} style={{ width: this._searchbarOpen ? "500px" : "100px" }} /> - <button className="searchBox-barChild searchBox-submit" onClick={this.submitSearch} onPointerDown={FilterBox.Instance.stopProp}>Submit</button> - <button className="searchBox-barChild searchBox-filter" onClick={FilterBox.Instance.openFilter} onPointerDown={FilterBox.Instance.stopProp}>Filter</button> - <button className="searchBox-barChild searchBox-close" title={"Close Search Bar"} onPointerDown={MainView.Instance.toggleSearch}><FontAwesomeIcon icon={faTimes} size="lg" /></button> + <button className="searchBox-barChild searchBox-filter" title="Advanced Filtering Options" onClick={FilterBox.Instance.openFilter} onPointerDown={FilterBox.Instance.stopProp}><FontAwesomeIcon icon="ellipsis-v" color="white" /></button> </div> + {(this._numTotalResults > 0 || !this._searchbarOpen) ? (null) : + (<div className="searchBox-quickFilter" onPointerDown={this.openSearch}> + <div className="filter-panel"><IconBar /></div> + </div>)} <div className="searchBox-results" onScroll={this.resultsScrolled} style={{ display: this._resultsOpen ? "flex" : "none", height: this.resFull ? "560px" : this.resultHeight, overflow: this.resFull ? "auto" : "visible" diff --git a/src/client/views/search/SearchItem.scss b/src/client/views/search/SearchItem.scss index 273d49349..9f12994c3 100644 --- a/src/client/views/search/SearchItem.scss +++ b/src/client/views/search/SearchItem.scss @@ -2,22 +2,32 @@ .search-overview { display: flex; - flex-direction: row-reverse; + flex-direction: reverse; justify-content: flex-end; - height: 70px; z-index: 0; } +.link-count { + background: black; + border-radius: 20px; + color: white; + width: 15px; + text-align: center; + margin-top: 5px; +} .searchBox-placeholder, .search-overview .search-item { - width: 500px; + width: 100%; background: $light-color-secondary; border-color: $intermediate-color; border-bottom-style: solid; padding: 10px; - height: 70px; + min-height: 50px; + max-height: 150px; + height: auto; z-index: 0; display: inline-block; + overflow: auto; .main-search-info { display: flex; @@ -26,6 +36,7 @@ .search-title-container { width: 100%; + overflow: hidden; .search-title { text-transform: uppercase; @@ -58,16 +69,6 @@ overflow: hidden; position: relative; - .link-count { - opacity: 1; - position: absolute; - z-index: 1000; - text-align: center; - -webkit-transition: opacity 0.2s ease-in-out; - -moz-transition: opacity 0.2s ease-in-out; - -o-transition: opacity 0.2s ease-in-out; - transition: opacity 0.2s ease-in-out; - } .link-extended { // display: none; @@ -109,7 +110,7 @@ .icon-icons, .icon-live { - height: 50px; + height: auto; margin: auto; overflow: hidden; @@ -171,9 +172,7 @@ .searchBox-instances:active { opacity: 1; background: $lighter-alt-accent; - -webkit-transform: scale(1); - -ms-transform: scale(1); - transform: scale(1); + width:150px } .search-item:hover { @@ -181,16 +180,19 @@ background: $lighter-alt-accent; } +.search-highlighting { + overflow: hidden; + text-overflow: ellipsis; + white-space: pre; +} + .searchBox-instances { float: left; opacity: 1; - width: 150px; + width: 0px; transition: all 0.2s ease; color: black; - transform-origin: top right; - -webkit-transform: scale(0); - -ms-transform: scale(0); - transform: scale(0); + overflow: hidden; } @@ -199,7 +201,7 @@ } .searchBox-placeholder { - min-height: 70px; + min-height: 50px; margin-left: 150px; text-transform: uppercase; text-align: left; diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx index 96eefacc2..f1d825aa0 100644 --- a/src/client/views/search/SearchItem.tsx +++ b/src/client/views/search/SearchItem.tsx @@ -4,13 +4,10 @@ import { faCaretUp, faChartBar, faFile, faFilePdf, faFilm, faFingerprint, faGlob import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast, HeightSym, WidthSym } from "../../../new_fields/Doc"; +import { Doc } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; -import { ObjectField } from "../../../new_fields/ObjectField"; -import { RichTextField } from "../../../new_fields/RichTextField"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { emptyFunction, returnEmptyString, returnFalse, returnOne, Utils } from "../../../Utils"; -import { DocServer } from "../../DocServer"; import { DocumentType } from "../../documents/DocumentTypes"; import { DocumentManager } from "../../util/DocumentManager"; import { DragManager, SetupDrag } from "../../util/DragManager"; @@ -18,7 +15,7 @@ import { LinkManager } from "../../util/LinkManager"; import { SearchUtil } from "../../util/SearchUtil"; import { Transform } from "../../util/Transform"; import { SEARCH_THUMBNAIL_SIZE } from "../../views/globalCssVariables.scss"; -import { CollectionViewType } from "../collections/CollectionBaseView"; +import { CollectionViewType } from "../collections/CollectionView"; import { CollectionDockingView } from "../collections/CollectionDockingView"; import { ContextMenu } from "../ContextMenu"; import { DocumentView } from "../nodes/DocumentView"; @@ -30,6 +27,7 @@ export interface SearchItemProps { doc: Doc; query: string; highlighting: string[]; + lines: string[]; } library.add(faCaretUp); @@ -152,7 +150,7 @@ export class SearchItem extends React.Component<SearchItemProps> { if (!this._useIcons) { let returnXDimension = () => this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE); let returnYDimension = () => this._displayDim; - let scale = () => returnXDimension() / NumCast(this.props.doc.nativeWidth, returnXDimension()); + let scale = () => returnXDimension() / NumCast(Doc.Layout(this.props.doc).nativeWidth, returnXDimension()); const docview = <div onPointerDown={action(() => { this._useIcons = !this._useIcons; @@ -215,18 +213,15 @@ export class SearchItem extends React.Component<SearchItemProps> { @computed get linkCount() { return LinkManager.Instance.getAllRelatedLinks(this.props.doc).length; } - @computed - get linkString(): string { - let num = this.linkCount; - if (num === 1) { - return num.toString() + " link"; - } - return num.toString() + " links"; - } - @action pointerDown = (e: React.PointerEvent) => { e.preventDefault(); e.button === 0 && SearchBox.Instance.openSearch(e); } + nextHighlight = (e: React.PointerEvent) => { + e.preventDefault(); e.button === 0 && SearchBox.Instance.openSearch(e); + let sstring = StrCast(this.props.doc.search_string); + this.props.doc.search_string = ""; + setTimeout(() => this.props.doc.search_string = sstring, 0); + } highlightDoc = (e: React.PointerEvent) => { if (this.props.doc.type === DocumentType.LINK) { if (this.props.doc.anchor1 && this.props.doc.anchor2) { @@ -239,6 +234,7 @@ export class SearchItem extends React.Component<SearchItemProps> { } else { Doc.BrushDoc(this.props.doc); } + e.stopPropagation(); } unHighlightDoc = (e: React.PointerEvent) => { @@ -282,23 +278,24 @@ export class SearchItem extends React.Component<SearchItemProps> { const doc2 = Cast(this.props.doc.anchor2, Doc); return ( <div className="search-overview" onPointerDown={this.pointerDown} onContextMenu={this.onContextMenu}> - <div className="search-item" onPointerEnter={this.highlightDoc} onPointerLeave={this.unHighlightDoc} id="result" - onClick={this.onClick} onPointerDown={this.pointerDown} > + <div className="search-item" onPointerDown={this.nextHighlight} onPointerEnter={this.highlightDoc} onPointerLeave={this.unHighlightDoc} id="result" + onClick={this.onClick}> <div className="main-search-info"> - <div title="Drag as document" onPointerDown={this.onPointerDown} style={{ marginRight: "7px" }}> <FontAwesomeIcon icon="file" size="lg" /> </div> + <div title="Drag as document" onPointerDown={this.onPointerDown} style={{ marginRight: "7px" }}> <FontAwesomeIcon icon="file" size="lg" /> + <div className="link-container item"> + <div className="link-count" title={`${this.linkCount + " links"}`}>{this.linkCount}</div> + </div> + </div> <div className="search-title-container"> <div className="search-title">{StrCast(this.props.doc.title)}</div> - <div className="search-highlighting">Matched fields: {this.props.highlighting.join(", ")}</div> + <div className="search-highlighting">{this.props.highlighting.length ? "Matched fields:" + this.props.highlighting.join(", ") : this.props.lines.length ? this.props.lines[0] : ""}</div> + {this.props.lines.filter((m, i) => i).map((l, i) => <div id={i.toString()} className="search-highlighting">`${l}`</div>)} </div> <div className="search-info" style={{ width: this._useIcons ? "15%" : "400px" }}> <div className={`icon-${this._useIcons ? "icons" : "live"}`}> <div className="search-type" title="Click to Preview">{this.DocumentIcon()}</div> <div className="search-label">{this.props.doc.type ? this.props.doc.type : "Other"}</div> </div> - <div className="link-container item"> - <div className="link-count">{this.linkCount}</div> - <div className="link-extended">{this.linkString}</div> - </div> </div> </div> </div> |
