diff options
| author | bob <bcz@cs.brown.edu> | 2019-08-19 10:11:59 -0400 |
|---|---|---|
| committer | bob <bcz@cs.brown.edu> | 2019-08-19 10:11:59 -0400 |
| commit | e37bf9124c952aa26c3e29deb9e4faa01cad1a7e (patch) | |
| tree | be44ae9bd5e2eb6c5ce392383d41505b5863d061 /src/client/views/search | |
| parent | 07482c3bf435748140addfd4fd338fc668657798 (diff) | |
| parent | b037aa89fb564812f880994453ce002054a0ad82 (diff) | |
Merge branch 'master' into presentation_f
Diffstat (limited to 'src/client/views/search')
| -rw-r--r-- | src/client/views/search/CheckBox.scss | 10 | ||||
| -rw-r--r-- | src/client/views/search/FieldFilters.scss | 9 | ||||
| -rw-r--r-- | src/client/views/search/FilterBox.scss | 89 | ||||
| -rw-r--r-- | src/client/views/search/FilterBox.tsx | 117 | ||||
| -rw-r--r-- | src/client/views/search/IconBar.scss | 5 | ||||
| -rw-r--r-- | src/client/views/search/IconButton.scss | 14 | ||||
| -rw-r--r-- | src/client/views/search/IconButton.tsx | 5 | ||||
| -rw-r--r-- | src/client/views/search/SearchBox.scss | 10 | ||||
| -rw-r--r-- | src/client/views/search/SearchBox.tsx | 21 | ||||
| -rw-r--r-- | src/client/views/search/SearchItem.tsx | 139 | ||||
| -rw-r--r-- | src/client/views/search/SelectorContextMenu.scss | 1 | ||||
| -rw-r--r-- | src/client/views/search/ToggleBar.scss | 9 | ||||
| -rw-r--r-- | src/client/views/search/ToggleBar.tsx | 1 |
13 files changed, 340 insertions, 90 deletions
diff --git a/src/client/views/search/CheckBox.scss b/src/client/views/search/CheckBox.scss index af59d5fbf..cc858bec6 100644 --- a/src/client/views/search/CheckBox.scss +++ b/src/client/views/search/CheckBox.scss @@ -13,9 +13,9 @@ margin-top: 0px; .check-container:hover~.check-box { - background-color: $intermediate-color; + background-color: $darker-alt-accent; } - + .check-container { width: 40px; height: 40px; @@ -27,7 +27,8 @@ position: absolute; fill-opacity: 0; stroke-width: 4px; - stroke: white; + // stroke: white; + stroke: gray; } } @@ -55,5 +56,4 @@ margin-left: 15px; } -} - +}
\ No newline at end of file diff --git a/src/client/views/search/FieldFilters.scss b/src/client/views/search/FieldFilters.scss index ba0926140..e1d0d8df5 100644 --- a/src/client/views/search/FieldFilters.scss +++ b/src/client/views/search/FieldFilters.scss @@ -1,5 +1,12 @@ .field-filters { width: 100%; display: grid; - grid-template-columns: 18% 20% 60%; + // grid-template-columns: 18% 20% 60%; + grid-template-columns: 20% 25% 60%; +} + +.field-filters-required { + width: 100%; + display: grid; + grid-template-columns: 50% 50%; }
\ No newline at end of file diff --git a/src/client/views/search/FilterBox.scss b/src/client/views/search/FilterBox.scss index 1eb8963d7..ebb39460d 100644 --- a/src/client/views/search/FilterBox.scss +++ b/src/client/views/search/FilterBox.scss @@ -3,22 +3,25 @@ .filter-form { padding: 25px; - width: 600px; - background: $dark-color; + width: 440px; + background: whitesmoke; position: relative; right: 1px; - color: $light-color; + color: grey; flex-direction: column; display: inline-block; transform-origin: top; overflow: auto; + border-radius: 15px; + box-shadow: $intermediate-color 0.2vw 0.2vw 0.4vw; + border: solid #BBBBBBBB 1px; .top-filter-header { #header { text-transform: uppercase; letter-spacing: 2px; - font-size: 25; + font-size: 13; width: 80%; } @@ -26,13 +29,13 @@ width: 20%; opacity: .6; position: relative; - display: inline-block; + display: block; .line { display: block; background: $alt-accent; - width: $width-line; - height: $height-line; + width: 20; + height: 3; position: absolute; right: 0; border-radius: ($height-line / 2); @@ -69,9 +72,10 @@ display: flex; align-items: center; margin-bottom: 10px; + letter-spacing: 2px; .filter-title { - font-size: 18; + font-size: 13; text-transform: uppercase; margin-top: 10px; margin-bottom: 10px; @@ -96,6 +100,7 @@ -moz-transition: all 0.2s ease-in-out; -o-transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out; + text-align: center; } } } @@ -105,4 +110,72 @@ border-top-style: solid; padding-top: 10px; } +} + +.active-filters { + display: flex; + flex-direction: row-reverse; + justify-content: flex-end; + width: 100%; + margin-right: 30px; + position: relative; + + .active-icon { + max-width: 40px; + flex: initial; + + &.icon{ + width: 40px; + text-align: center; + margin-bottom: 5px; + position: absolute; + } + + &.container { + display: flex; + flex-direction: column; + width: 40px; + } + + &.description { + text-align: center; + top: 40px; + position: absolute; + width: 40px; + font-size: 9px; + opacity: 0; + -webkit-transition: all 0.2s ease-in-out; + -moz-transition: all 0.2s ease-in-out; + -o-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; + } + + &.icon:hover + .description { + opacity: 1; + } + } + + .col-icon { + height: 35px; + margin-left: 5px; + width: 35px; + background-color: black; + color: white; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + + .save-filter, + .reset-filter, + .all-filter { + background-color: gray; + } + + .save-filter:hover, + .reset-filter:hover, + .all-filter:hover { + background-color: $darker-alt-accent; + } + } }
\ No newline at end of file diff --git a/src/client/views/search/FilterBox.tsx b/src/client/views/search/FilterBox.tsx index 706d1eb7f..3e8582d61 100644 --- a/src/client/views/search/FilterBox.tsx +++ b/src/client/views/search/FilterBox.tsx @@ -2,25 +2,26 @@ import * as React from 'react'; import { observer } from 'mobx-react'; import { observable, action } from 'mobx'; import "./SearchBox.scss"; -import { faTimes } from '@fortawesome/free-solid-svg-icons'; +import { faTimes, faCheckCircle, faObjectGroup } from '@fortawesome/free-solid-svg-icons'; import { library } from '@fortawesome/fontawesome-svg-core'; import { Doc } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; import { DocumentType } from '../../documents/Documents'; import { Cast, StrCast } from '../../../new_fields/Types'; import * as _ from "lodash"; -import { ToggleBar } from './ToggleBar'; import { IconBar } from './IconBar'; import { FieldFilters } from './FieldFilters'; import { SelectionManager } from '../../util/SelectionManager'; import { DocumentView } from '../nodes/DocumentView'; import { CollectionFilters } from './CollectionFilters'; -import { NaviconButton } from './NaviconButton'; import * as $ from 'jquery'; import "./FilterBox.scss"; import { SearchBox } from './SearchBox'; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; library.add(faTimes); +library.add(faCheckCircle); +library.add(faObjectGroup); export enum Keys { TITLE = "title", @@ -35,11 +36,18 @@ export class FilterBox extends React.Component { public _allIcons: string[] = [DocumentType.AUDIO, DocumentType.COL, DocumentType.HIST, 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 @observable private _basicWordStatus: boolean = true; @observable private _filterOpen: boolean = false; + //if icons = all icons, then no icon filter is applied @observable private _icons: string[] = this._allIcons; + //if all of these are true, no key filter is applied + @observable private _anyKeywordStatus: boolean = true; + @observable private _allKeywordStatus: boolean = true; @observable private _titleFieldStatus: boolean = true; @observable private _authorFieldStatus: boolean = true; + @observable private _dataFieldStatus: boolean = true; + //this also serves as an indicator if the collection status filter is applied @observable public _deletedDocsStatus: boolean = false; @observable private _collectionStatus = false; @observable private _collectionSelfStatus = true; @@ -114,10 +122,9 @@ export class FilterBox extends React.Component { @action.bound resetFilters = () => { - ToggleBar.Instance.resetToggle(); + this._basicWordStatus = true; IconBar.Instance.selectAll(); FieldFilters.Instance.resetFieldFilters(); - CollectionFilters.Instance.resetCollectionFilters(); } basicRequireWords(query: string): string { @@ -259,6 +266,40 @@ export class FilterBox extends React.Component { return finalDocs; } + getABCicon() { + return ( + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 87.8 87.8" height="35"> + <path d="M25.4 47.9c-1.3 1.3-1.9 2.8-1.9 4.8 0 3.8 2.3 6.1 6.1 6.1 5.1 0 8-3.3 9-6.2 0.2-0.7 0.4-1.4 0.4-2.1v-6.1c-0.1 0-0.1 0-0.2 0C32.2 44.5 27.7 45.6 25.4 47.9z" /> + <path d="M64.5 28.6c-2.2 0-4.1 1.5-4.7 3.8l0 0.2c-0.1 0.3-0.1 0.7-0.1 1.1v3.3c0 0.4 0.1 0.8 0.2 1.1 0.6 2.2 2.4 3.6 4.6 3.6 3.2 0 5.2-2.6 5.2-6.7C69.5 31.8 68 28.6 64.5 28.6z" /> + <path d="M43.9 0C19.7 0 0 19.7 0 43.9s19.7 43.9 43.9 43.9 43.9-19.6 43.9-43.9S68.1 0 43.9 0zM40.1 65.5l-0.5-4c-3 3.1-7.4 4.9-12.1 4.9 -6.8 0-13.6-4.4-13.6-12.8 0-4 1.3-7.4 4-10 4.1-4.1 11.1-6.2 20.8-6.3 0-5.5-2.9-8.4-8.3-8.4 -3.6 0-7.4 1.1-10.2 2.9l-1.1 0.7 -2.4-6.9 0.7-0.4c3.7-2.4 8.9-3.8 14.1-3.8 10.9 0 16.7 6.2 16.7 17.9V54.6c0 4.1 0.2 7.2 0.7 9.7L49 65.5H40.1zM65.5 67.5c1.8 0 3-0.5 4-0.9l0.5-0.2 0.8 3.4 -0.3 0.2c-1 0.5-3 1.1-5.5 1.1 -5.8 0-9.7-4-9.7-9.9 0-6.1 4.3-10.3 10.4-10.3 2.1 0 4 0.5 4.9 1l0.3 0.2 -1 3.5 -0.5-0.3c-0.7-0.4-1.8-0.8-3.7-0.8 -3.7 0-6.1 2.6-6.1 6.6C59.5 64.8 61.9 67.5 65.5 67.5zM65 45.3c-2.5 0-4.5-0.9-5.9-2.7l-0.1 2.3h-3.8l0-0.5c0.1-1.2 0.2-3.1 0.2-4.8V16.7h4.3v10.8c1.4-1.6 3.5-2.5 6-2.5 2.2 0 4.1 0.8 5.5 2.3 1.8 1.8 2.8 4.5 2.8 7.7C73.8 42.1 69.3 45.3 65 45.3z" /> + </svg> + ); + } + + getTypeIcon() { + return ( + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 87.8 87.8" height="35"> + <path d="M43.9 0C19.7 0 0 19.7 0 43.9s19.7 43.9 43.9 43.9 43.9-19.6 43.9-43.9S68.1 0 43.9 0zM43.9 12.2c4.1 0 7.5 3.4 7.5 7.5 0 4.1-3.4 7.5-7.5 7.5 -4.1 0-7.5-3.4-7.5-7.5C36.4 15.5 39.7 12.2 43.9 12.2zM11.9 50.4l7.5-13 7.5 13H11.9zM47.6 75.7h-7.5l-3.7-6.5 3.8-6.5h7.5l3.8 6.5L47.6 75.7zM70.7 70.7c-0.2 0.2-0.4 0.3-0.7 0.3s-0.5-0.1-0.7-0.3l-25.4-25.4 -25.4 25.4c-0.2 0.2-0.4 0.3-0.7 0.3s-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1 0-1.4l25.4-25.4 -25.4-25.4c-0.4-0.4-0.4-1 0-1.4s1-0.4 1.4 0l25.4 25.4 25.4-25.4c0.4-0.4 1-0.4 1.4 0s0.4 1 0 1.4l-25.4 25.4 25.4 25.4C71.1 69.7 71.1 70.3 70.7 70.7zM61.4 51.4v-15h15v15H61.4z" /> + </svg> + ); + } + + getKeyIcon() { + return ( + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 87.8 87.8" height="35"> + <path d="M38.5 32.4c0 3.4-2.7 6.1-6.1 6.1 -3.4 0-6.1-2.7-6.1-6.1 0-3.4 2.8-6.1 6.1-6.1C35.8 26.3 38.5 29 38.5 32.4zM87.8 43.9c0 24.2-19.6 43.9-43.9 43.9S0 68.1 0 43.9C0 19.7 19.7 0 43.9 0S87.8 19.7 87.8 43.9zM66.8 60.3L50.2 43.7c-0.5-0.5-0.6-1.2-0.4-1.8 2.4-5.6 1.1-12.1-3.2-16.5 -5.9-5.8-15.4-5.8-21.2 0l0 0c-4.3 4.3-5.6 10.8-3.2 16.5 3.2 7.6 12 11.2 19.7 8 0.6-0.3 1.4-0.1 1.8 0.4l3.1 3.1h3.9c1.2 0 2.2 1 2.2 2.2v3.6h3.6c1.2 0 2.2 1 2.2 2.2v4l1.6 1.6h6.5V60.3z" /> + </svg> + ); + } + + getColIcon() { + return ( + <div className="col-icon"> + <FontAwesomeIcon icon={faObjectGroup} size="lg" /> + </div> + ); + } + @action.bound openFilter = () => { this._filterOpen = !this._filterOpen; @@ -268,10 +309,9 @@ export class FilterBox extends React.Component { //if true, any keywords can be used. if false, all keywords are required. @action.bound - handleWordQueryChange = () => { this._basicWordStatus = !this._basicWordStatus; } - - @action.bound - getBasicWordStatus() { return this._basicWordStatus; } + handleWordQueryChange = () => { + this._basicWordStatus = !this._basicWordStatus; + } @action.bound updateIcon(newArray: string[]) { this._icons = newArray; } @@ -290,16 +330,10 @@ export class FilterBox extends React.Component { } @action.bound - toggleFieldOpen() { this._fieldOpen = !this._fieldOpen; } - - @action.bound - toggleColOpen() { this._colOpen = !this._colOpen; } - - @action.bound - toggleTypeOpen() { this._typeOpen = !this._typeOpen; } + updateAnyKeywordStatus(newStat: boolean) { this._anyKeywordStatus = newStat; } @action.bound - toggleWordStatusOpen() { this._wordStatusOpen = !this._wordStatusOpen; } + updateAllKeywordStatus(newStat: boolean) { this._allKeywordStatus = newStat; } @action.bound updateTitleStatus(newStat: boolean) { this._titleFieldStatus = newStat; } @@ -319,6 +353,8 @@ export class FilterBox extends React.Component { @action.bound updateParentCollectionStatus(newStat: boolean) { this._collectionParentStatus = newStat; } + getAnyKeywordStatus() { return this._anyKeywordStatus; } + getAllKeywordStatus() { return this._allKeywordStatus; } getCollectionStatus() { return this._collectionStatus; } getSelfCollectionStatus() { return this._collectionSelfStatus; } getParentCollectionStatus() { return this._collectionParentStatus; } @@ -326,6 +362,31 @@ export class FilterBox extends React.Component { getAuthorStatus() { return this._authorFieldStatus; } getDataStatus() { return this._deletedDocsStatus; } + getActiveFilters() { + console.log(this._authorFieldStatus, this._titleFieldStatus, this._dataFieldStatus); + return ( + <div className="active-filters"> + {!this._basicWordStatus ? <div className="active-icon container"> + <div className="active-icon icon">{this.getABCicon()}</div> + <div className="active-icon description">Required Words Applied</div> + </div> : undefined} + {!(this._icons.length === 9) ? <div className="active-icon container"> + <div className="active-icon icon">{this.getTypeIcon()}</div> + <div className="active-icon description">Type Filters Applied</div> + </div> : undefined} + {!(this._authorFieldStatus && this._dataFieldStatus && this._titleFieldStatus) ? + <div className="active-icon container"> + <div className="active-icon icon">{this.getKeyIcon()}</div> + <div className="active-icon description">Field Filters Applied</div> + </div> : undefined} + {this._collectionStatus ? <div className="active-icon container"> + <div className="active-icon icon">{this.getColIcon()}</div> + <div className="active-icon description">Collection Filters Active</div> + </div> : undefined} + </div> + ); + } + // Useful queries: // Delegates of a document: {!join from=id to=proto_i}id:{protoId} // Documents in a collection: {!join from=data_l to=id}id:{collectionProtoId} //id of collections prototype @@ -334,11 +395,13 @@ export class FilterBox extends React.Component { <div> <div style={{ display: "flex", flexDirection: "row-reverse" }}> <SearchBox /> + {this.getActiveFilters()} </div> {this._filterOpen ? ( <div className="filter-form" onPointerDown={this.stopProp} id="filter-form" style={this._filterOpen ? { display: "flex" } : { display: "none" }}> <div className="top-filter-header" style={{ display: "flex", width: "100%" }}> <div id="header">Filter Search Results</div> + <div style={{ marginLeft: "auto" }}></div> <div className="close-icon" onClick={this.closeFilter}> <span className="line line-1"></span> <span className="line line-2"></span></div> @@ -347,33 +410,20 @@ export class FilterBox extends React.Component { <div className="filter-div"> <div className="filter-header"> <div className='filter-title words'>Required words</div> - <div style={{ marginLeft: "auto" }}><NaviconButton onClick={this.toggleWordStatusOpen} /></div> </div> <div className="filter-panel" > - <ToggleBar handleChange={this.handleWordQueryChange} getStatus={this.getBasicWordStatus} - originalStatus={this._basicWordStatus} optionOne={"Include Any Keywords"} optionTwo={"Include All Keywords"} /> + <button className="all-filter" onClick={this.handleWordQueryChange}>Include All Keywords</button> </div> </div> <div className="filter-div"> <div className="filter-header"> <div className="filter-title icon">Filter by type of node</div> - <div style={{ marginLeft: "auto" }}><NaviconButton onClick={this.toggleTypeOpen} /></div> </div> <div className="filter-panel"><IconBar /></div> </div> <div className="filter-div"> <div className="filter-header"> - <div className='filter-title collection'>Search in current collections</div> - <div style={{ marginLeft: "auto" }}><NaviconButton onClick={this.toggleColOpen} /></div> - </div> - <div className="filter-panel"><CollectionFilters - updateCollectionStatus={this.updateCollectionStatus} updateParentCollectionStatus={this.updateParentCollectionStatus} updateSelfCollectionStatus={this.updateSelfCollectionStatus} - collectionStatus={this._collectionStatus} collectionParentStatus={this._collectionParentStatus} collectionSelfStatus={this._collectionSelfStatus} /></div> - </div> - <div className="filter-div"> - <div className="filter-header"> <div className="filter-title field">Filter by Basic Keys</div> - <div style={{ marginLeft: "auto" }}><NaviconButton onClick={this.toggleFieldOpen} /></div> </div> <div className="filter-panel"><FieldFilters titleFieldStatus={this._titleFieldStatus} dataFieldStatus={this._deletedDocsStatus} authorFieldStatus={this._authorFieldStatus} @@ -381,13 +431,12 @@ export class FilterBox extends React.Component { </div> </div> <div className="filter-buttons" style={{ display: "flex", justifyContent: "space-around" }}> - <button className="minimize-filter" onClick={this.minimizeAll}>Minimize All</button> - <button className="advanced-filter" >Advanced Filters</button> <button className="save-filter" >Save Filters</button> <button className="reset-filter" onClick={this.resetFilters}>Reset Filters</button> </div> </div> - ) : undefined} + ) : + undefined} </div> ); } diff --git a/src/client/views/search/IconBar.scss b/src/client/views/search/IconBar.scss index e384722ce..2555ad271 100644 --- a/src/client/views/search/IconBar.scss +++ b/src/client/views/search/IconBar.scss @@ -4,9 +4,8 @@ display: flex; justify-content: space-evenly; align-items: center; - height: 40px; + height: 35px; width: 100%; flex-wrap: wrap; margin-bottom: 10px; -} - +}
\ No newline at end of file diff --git a/src/client/views/search/IconButton.scss b/src/client/views/search/IconButton.scss index 94b294ba5..d1853177e 100644 --- a/src/client/views/search/IconButton.scss +++ b/src/client/views/search/IconButton.scss @@ -4,13 +4,15 @@ display: flex; flex-direction: column; align-items: center; - width: 45px; + width: 30px; height: 60px; .type-icon { - height: 45px; - width: 45px; + height: 30px; + width: 30px; color: $light-color; + // background-color: rgb(194, 194, 197); + background-color: gray; border-radius: 50%; display: flex; justify-content: center; @@ -22,8 +24,8 @@ font-size: 2em; .fontawesome-icon { - height: 24px; - width: 24px; + height: 15px; + width: 15px } } @@ -44,7 +46,7 @@ transform: scale(1.1); background-color: $darker-alt-accent; opacity: 1; - + +.filter-description { opacity: 1; } diff --git a/src/client/views/search/IconButton.tsx b/src/client/views/search/IconButton.tsx index bfe2c7d0b..5d23f6eeb 100644 --- a/src/client/views/search/IconButton.tsx +++ b/src/client/views/search/IconButton.tsx @@ -13,6 +13,7 @@ import { IconBar } from './IconBar'; import { props } from 'bluebird'; import { FilterBox } from './FilterBox'; import { Search } from '../../../server/Search'; +import { gravity } from 'sharp'; library.add(faSearch); library.add(faObjectGroup); @@ -123,11 +124,11 @@ export class IconButton extends React.Component<IconButtonProps>{ selected = { opacity: 1, - backgroundColor: "#c2c2c5" //$alt-accent + backgroundColor: "rgb(128, 128, 128)" }; notSelected = { - opacity: 0.6, + opacity: 0.2, }; hoverStyle = { diff --git a/src/client/views/search/SearchBox.scss b/src/client/views/search/SearchBox.scss index 109b88ac9..5ed33a596 100644 --- a/src/client/views/search/SearchBox.scss +++ b/src/client/views/search/SearchBox.scss @@ -37,6 +37,11 @@ margin-left: 2px; margin-right: 2px } + + &.searchBox-close { + color: $light-color; + max-height: 32px; + } } } @@ -45,6 +50,11 @@ top: 300px; display: flex; flex-direction: column; + margin-right: 72px; + // height: 560px; + height: 100%; + // overflow: hidden; + // overflow-y: auto; max-height: 560px; overflow: hidden; overflow-y: auto; diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 2214ac8af..4dc409e77 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -4,6 +4,8 @@ import { observable, action, runInAction, flow, computed } from 'mobx'; import "./SearchBox.scss"; import "./FilterBox.scss"; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faTimes } from '@fortawesome/free-solid-svg-icons'; +import { library } from '@fortawesome/fontawesome-svg-core'; import { SetupDrag } from '../../util/DragManager'; import { Docs } from '../../documents/Documents'; import { NumCast, Cast } from '../../../new_fields/Types'; @@ -14,8 +16,12 @@ import { Id } from '../../../new_fields/FieldSymbols'; import { SearchUtil } from '../../util/SearchUtil'; import { RouteStore } from '../../../server/RouteStore'; import { FilterBox } from './FilterBox'; +import { ReadStream } from 'fs'; +import * as $ from 'jquery'; +import { MainView } from '../MainView'; import { Utils } from '../../../Utils'; +library.add(faTimes); @observer export class SearchBox extends React.Component { @@ -29,6 +35,7 @@ export class SearchBox extends React.Component { @observable private _visibleElements: JSX.Element[] = []; private resultsRef = React.createRef<HTMLDivElement>(); + public inputRef = React.createRef<HTMLInputElement>(); private _isSearch: ("search" | "placeholder" | undefined)[] = []; private _numTotalResults = -1; @@ -46,6 +53,15 @@ export class SearchBox extends React.Component { this.resultsScrolled = this.resultsScrolled.bind(this); } + componentDidMount = () => { + if (this.inputRef.current) { + this.inputRef.current.focus(); + runInAction(() => { + this._searchbarOpen = true; + }); + } + } + @action getViews = async (doc: Doc) => { const results = await SearchUtil.GetViewsOfDocument(doc); @@ -229,6 +245,7 @@ export class SearchBox extends React.Component { @action.bound closeSearch = () => { + console.log("closing search") FilterBox.Instance.closeFilter(); this.closeResults(); this._searchbarOpen = false; @@ -321,11 +338,12 @@ export class SearchBox extends React.Component { <span className="searchBox-barChild searchBox-collection" onPointerDown={SetupDrag(this.collectionRef, this.startDragCollection)} ref={this.collectionRef} title="Drag Results as Collection"> <FontAwesomeIcon icon="object-group" size="lg" /> </span> - <input value={this._searchString} onChange={this.onChange} type="text" placeholder="Search..." + <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} 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> </div> <div className="searchBox-results" onScroll={this.resultsScrolled} style={{ display: this._resultsOpen ? "flex" : "none", @@ -336,5 +354,4 @@ export class SearchBox extends React.Component { </div> ); } - }
\ No newline at end of file diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx index 562594210..8201aa374 100644 --- a/src/client/views/search/SearchItem.tsx +++ b/src/client/views/search/SearchItem.tsx @@ -1,31 +1,34 @@ import React = require("react"); import { library } from '@fortawesome/fontawesome-svg-core'; -import { faCaretUp, faChartBar, faFilePdf, faFilm, faGlobeAsia, faImage, faLink, faMusic, faObjectGroup, faStickyNote, faFingerprint } from '@fortawesome/free-solid-svg-icons'; +import { faCaretUp, faChartBar, faFile, faFilePdf, faFilm, faFingerprint, faGlobeAsia, faImage, faLink, faMusic, faObjectGroup, faStickyNote } from '@fortawesome/free-solid-svg-icons'; 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 { 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, returnFalse, returnOne, Utils } from "../../../Utils"; +import { emptyFunction, returnEmptyString, returnFalse, returnOne, Utils } from "../../../Utils"; +import { DocServer } from "../../DocServer"; import { DocumentType } from "../../documents/Documents"; import { DocumentManager } from "../../util/DocumentManager"; -import { SetupDrag, DragManager } from "../../util/DragManager"; +import { DragManager, SetupDrag } from "../../util/DragManager"; 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 { CollectionDockingView } from "../collections/CollectionDockingView"; +import { ContextMenu } from "../ContextMenu"; import { DocumentView } from "../nodes/DocumentView"; import { SearchBox } from "./SearchBox"; import "./SearchItem.scss"; import "./SelectorContextMenu.scss"; -import { ContextMenu } from "../ContextMenu"; -import { faFile } from '@fortawesome/free-solid-svg-icons'; export interface SearchItemProps { doc: Doc; + query?: string; highlighting: string[]; } @@ -86,7 +89,7 @@ export class SelectorContextMenu extends React.Component<SearchItemProps> { SetupDrag(item, () => doc.col, undefined, undefined, undefined, undefined, () => SearchBox.Instance.closeSearch())}> <FontAwesomeIcon icon={faStickyNote} /> </div> - <a className="title" onClick={this.getOnClick(doc)}>{doc.col.title}</a> + <a onClick={this.getOnClick(doc)}>{doc.col.title}</a> </div>; })} </div> @@ -94,27 +97,103 @@ export class SelectorContextMenu extends React.Component<SearchItemProps> { } } +export interface LinkMenuProps { + doc1: Doc; + doc2: Doc; +} + +@observer +export class LinkContextMenu extends React.Component<LinkMenuProps> { + + highlightDoc = (doc: Doc) => () => Doc.BrushDoc(doc); + + unHighlightDoc = (doc: Doc) => () => Doc.UnBrushDoc(doc); + + getOnClick = (col: Doc) => () => CollectionDockingView.Instance.AddRightSplit(col, undefined); + + render() { + return ( + <div className="parents"> + <p className="contexts">Anchors:</p> + <div className="collection"><a onMouseEnter={this.highlightDoc(this.props.doc1)} onMouseLeave={this.unHighlightDoc(this.props.doc1)} onClick={this.getOnClick(this.props.doc1)}>Doc 1: {this.props.doc2.title}</a></div> + <div><a onMouseEnter={this.highlightDoc(this.props.doc2)} onMouseLeave={this.unHighlightDoc(this.props.doc2)} onClick={this.getOnClick(this.props.doc2)}>Doc 2: {this.props.doc1.title}</a></div> + </div> + ); + } + +} + @observer export class SearchItem extends React.Component<SearchItemProps> { @observable _selected: boolean = false; + private _previewDoc?: Doc; onClick = () => { // I dont think this is the best functionality because clicking the name of the collection does that. Change it back if you'd like DocumentManager.Instance.jumpToDocument(this.props.doc, false); + if (this.props.doc.data instanceof RichTextField) { + this.highlightTextBox(this.props.doc); + } // CollectionDockingView.Instance.AddRightSplit(this.props.doc, undefined); } @observable _useIcons = true; @observable _displayDim = 50; - @computed - public get DocumentIcon() { + highlightTextBox = (doc: Doc) => { + if (this.props.query) { + const fieldkey = 'search_string'; + if (Object.keys(doc).indexOf(fieldkey) === -1) { + doc.search_string = this.props.query; + } + else { + doc.search_string = undefined; + } + + } + } + + fitToBox = () => { + let bounds = Doc.ComputeContentBounds([this.props.doc]); + return [(bounds.x + bounds.r) / 2, (bounds.y + bounds.b) / 2, Number(SEARCH_THUMBNAIL_SIZE) / Math.max((bounds.b - bounds.y), (bounds.r - bounds.x)), this._displayDim]; + } + + componentWillUnmount() { + if (this._previewDoc) { + DocServer.DeleteDocument(this._previewDoc[Id]); + } + } + + + //@computed + @action + public DocumentIcon() { + let layoutresult = StrCast(this.props.doc.type); if (!this._useIcons) { + let renderDoc = this.props.doc; + //let box: number[] = []; + if (layoutresult.indexOf(DocumentType.COL) !== -1) { + renderDoc = Doc.MakeDelegate(renderDoc); + let bounds = DocListCast(renderDoc.data).reduce((bounds, doc) => { + var [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)]; + let [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()]; + return { + x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y), + r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b) + }; + }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE }); + let box = () => [(bounds.x + bounds.r) / 2, (bounds.y + bounds.b) / 2, Number(SEARCH_THUMBNAIL_SIZE) / (bounds.r - bounds.x), this._displayDim]; + } let returnXDimension = () => this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE); let returnYDimension = () => this._displayDim; - let scale = () => returnXDimension() / NumCast(this.props.doc.nativeWidth, returnXDimension()); - return <div - onPointerDown={action(() => { this._useIcons = !this._useIcons; this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE); })} + let scale = () => returnXDimension() / NumCast(renderDoc.nativeWidth, returnXDimension()); + let newRenderDoc = Doc.MakeDelegate(renderDoc); /// newRenderDoc -> renderDoc -> render"data"Doc -> TextProt + this._previewDoc = newRenderDoc; + const docview = <div + onPointerDown={action(() => { + this._useIcons = !this._useIcons; + this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE); + })} onPointerEnter={action(() => this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE))} onPointerLeave={action(() => this._displayDim = 50)} > <DocumentView @@ -128,6 +207,7 @@ export class SearchItem extends React.Component<SearchItemProps> { PanelWidth={returnXDimension} PanelHeight={returnYDimension} focus={emptyFunction} + backgroundColor={returnEmptyString} selectOnLoad={false} parentActive={returnFalse} whenActiveChanged={returnFalse} @@ -138,9 +218,15 @@ export class SearchItem extends React.Component<SearchItemProps> { ContentScaling={scale} /> </div>; + const data = renderDoc.data; + if (data instanceof ObjectField) newRenderDoc.data = ObjectField.MakeCopy(data); + newRenderDoc.preview = true; + newRenderDoc.search_string = this.props.query; + return docview; + } + if (this._previewDoc) { + DocServer.DeleteDocument(this._previewDoc[Id]); } - - let layoutresult = StrCast(this.props.doc.type); let button = layoutresult.indexOf(DocumentType.PDF) !== -1 ? faFilePdf : layoutresult.indexOf(DocumentType.IMG) !== -1 ? faImage : layoutresult.indexOf(DocumentType.TEXT) !== -1 ? faStickyNote : @@ -188,14 +274,12 @@ export class SearchItem extends React.Component<SearchItemProps> { let doc1 = Cast(this.props.doc.anchor1, Doc, null); let doc2 = Cast(this.props.doc.anchor2, Doc, null); - doc1 && (doc1.libraryBrush = true); - doc2 && (doc2.libraryBrush = true); + Doc.BrushDoc(doc1); + Doc.BrushDoc(doc2); } } else { - let docViews: DocumentView[] = DocumentManager.Instance.getAllDocumentViews(this.props.doc); - docViews.forEach(element => { - element.props.Document.libraryBrush = true; - }); + DocumentManager.Instance.getAllDocumentViews(this.props.doc).forEach(element => + Doc.BrushDoc(element.props.Document)); } } @@ -205,14 +289,12 @@ export class SearchItem extends React.Component<SearchItemProps> { let doc1 = Cast(this.props.doc.anchor1, Doc, null); let doc2 = Cast(this.props.doc.anchor2, Doc, null); - doc1 && (doc1.libraryBrush = false); - doc2 && (doc2.libraryBrush = false); + Doc.UnBrushDoc(doc1); + Doc.UnBrushDoc(doc2); } } else { - let docViews: DocumentView[] = DocumentManager.Instance.getAllDocumentViews(this.props.doc); - docViews.forEach(element => { - element.props.Document.libraryBrush = false; - }); + DocumentManager.Instance.getAllDocumentViews(this.props.doc). + forEach(element => Doc.UnBrushDoc(element.props.Document)); } } @@ -239,6 +321,8 @@ export class SearchItem extends React.Component<SearchItemProps> { } render() { + const doc1 = Cast(this.props.doc.anchor1, Doc); + 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" @@ -251,7 +335,7 @@ export class SearchItem extends React.Component<SearchItemProps> { </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-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"> @@ -262,7 +346,8 @@ export class SearchItem extends React.Component<SearchItemProps> { </div> </div> <div className="searchBox-instances"> - <SelectorContextMenu {...this.props} /> + {(doc1 instanceof Doc && doc2 instanceof Doc) && this.props.doc.type === DocumentType.LINK ? <LinkContextMenu doc1={doc1} doc2={doc2} /> : + <SelectorContextMenu {...this.props} />} </div> </div> ); diff --git a/src/client/views/search/SelectorContextMenu.scss b/src/client/views/search/SelectorContextMenu.scss index 49f77b9bf..48cacc608 100644 --- a/src/client/views/search/SelectorContextMenu.scss +++ b/src/client/views/search/SelectorContextMenu.scss @@ -3,6 +3,7 @@ .parents { background: $lighter-alt-accent; padding: 10px; + // width: 300px; .contexts { text-transform: uppercase; diff --git a/src/client/views/search/ToggleBar.scss b/src/client/views/search/ToggleBar.scss index 633a194fe..79f866acb 100644 --- a/src/client/views/search/ToggleBar.scss +++ b/src/client/views/search/ToggleBar.scss @@ -16,11 +16,15 @@ -moz-transition: all 0.2s ease-in-out; -o-transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out; + color: gray; + font-size: 13; } } .toggle-bar { - height: 50px; + // height: 50px; + height: 30px; + width: 100px; background-color: $alt-accent; border-radius: 10px; padding: 5px; @@ -28,7 +32,8 @@ align-items: center; .toggle-button { - width: 275px; + // width: 275px; + width: 40px; height: 100%; border-radius: 10px; background-color: $light-color; diff --git a/src/client/views/search/ToggleBar.tsx b/src/client/views/search/ToggleBar.tsx index 178578c5c..ed5ecd3ba 100644 --- a/src/client/views/search/ToggleBar.tsx +++ b/src/client/views/search/ToggleBar.tsx @@ -59,6 +59,7 @@ export class ToggleBar extends React.Component<ToggleBarProps>{ this._forwardTimeline.play(); this._forwardTimeline.reverse(); this.props.handleChange(); + console.log(this.props.getStatus()); } @action.bound |
