diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/search/IconBar.tsx | 46 | ||||
-rw-r--r-- | src/client/views/search/SearchBox.tsx | 203 | ||||
-rw-r--r-- | src/client/views/search/SearchItem.tsx | 11 |
3 files changed, 185 insertions, 75 deletions
diff --git a/src/client/views/search/IconBar.tsx b/src/client/views/search/IconBar.tsx index 3f77d64eb..f3741a5de 100644 --- a/src/client/views/search/IconBar.tsx +++ b/src/client/views/search/IconBar.tsx @@ -160,95 +160,95 @@ export class IconBar extends React.Component<IconBarProps> { render() { return ( <div> - <div className="icon-title">Filter by type of node</div> - <div className="icon-bar"> - <div className="type-outer"> - <div className={"type-icon none"} + <div className="filter icon-title">Filter by type of node</div> + <div className="filter icon-bar"> + <div className="filter type-outer"> + <div className={"type-icon filter none"} ref={this.noneRef} data-selected={"false"} onClick={() => { this.onClick(DocTypes.NONE); }}> - <FontAwesomeIcon className="fontawesome-icon" style={{ order: -2 }} icon={faBan} /> + <FontAwesomeIcon className="fontawesome-icon filter" style={{ order: -2 }} icon={faBan} /> </div> <div className="filter-description">Clear</div> </div> <div className="type-outer"> - <div className={"type-icon " + (this.isRefSelected(this.pdfRef) ? "selected" : "not-selected")} + <div className={"type-icon filter " + (this.isRefSelected(this.pdfRef) ? "selected" : "not-selected")} ref={this.pdfRef} data-selected={this.getInitialStatus(DocTypes.PDF)} onClick={() => { this.onClick(DocTypes.PDF); }}> - <FontAwesomeIcon className="fontawesome-icon" style={{ order: 0 }} icon={faFilePdf} /> + <FontAwesomeIcon className="fontawesome-icon filter" style={{ order: 0 }} icon={faFilePdf} /> </div> <div className="filter-description">PDF</div> </div> <div className="type-outer"> - <div className={"type-icon " + (this.isRefSelected(this.histRef) ? "selected" : "not-selected")} + <div className={"type-icon filter " + (this.isRefSelected(this.histRef) ? "selected" : "not-selected")} ref={this.histRef} data-selected={this.getInitialStatus(DocTypes.HIST)} onClick={() => { this.onClick(DocTypes.HIST); }}> - <FontAwesomeIcon className="fontawesome-icon" style={{ order: 1 }} icon={faChartBar} /> + <FontAwesomeIcon className="fontawesome-icon filter" style={{ order: 1 }} icon={faChartBar} /> </div> <div className="filter-description">Histogram</div> </div> <div className="type-outer"> - <div className={"type-icon " + (this.isRefSelected(this.colRef) ? "selected" : "not-selected")} + <div className={"type-icon filter " + (this.isRefSelected(this.colRef) ? "selected" : "not-selected")} ref={this.colRef} data-selected={this.getInitialStatus(DocTypes.COL)} onClick={() => { this.onClick(DocTypes.COL); }}> - <FontAwesomeIcon className="fontawesome-icon" style={{ order: 2 }} icon={faObjectGroup} /> + <FontAwesomeIcon className="fontawesome-icon filter" style={{ order: 2 }} icon={faObjectGroup} /> </div> <div className="filter-description">Collection</div> </div> <div className="type-outer"> - <div className={"type-icon " + (this.isRefSelected(this.imgRef) ? "selected" : "not-selected")} + <div className={"type-icon filter " + (this.isRefSelected(this.imgRef) ? "selected" : "not-selected")} ref={this.imgRef} data-selected={this.getInitialStatus(DocTypes.IMG)} onClick={() => { this.onClick(DocTypes.IMG); }}> - <FontAwesomeIcon className="fontawesome-icon" style={{ order: 3 }} icon={faImage} /> + <FontAwesomeIcon className="fontawesome-icon filter" style={{ order: 3 }} icon={faImage} /> </div> <div className="filter-description">Image</div> </div> <div className="type-outer"> - <div className={"type-icon " + (this.isRefSelected(this.vidRef) ? "selected" : "not-selected")} + <div className={"type-icon filter " + (this.isRefSelected(this.vidRef) ? "selected" : "not-selected")} ref={this.vidRef} data-selected={this.getInitialStatus(DocTypes.VID)} onClick={() => { this.onClick(DocTypes.VID); }}> - <FontAwesomeIcon className="fontawesome-icon" style={{ order: 4 }} icon={faFilm} /> + <FontAwesomeIcon className="fontawesome-icon filter" style={{ order: 4 }} icon={faFilm} /> </div> <div className="filter-description">Video</div> </div> <div className="type-outer"> - <div className={"type-icon " + (this.isRefSelected(this.webRef) ? "selected" : "not-selected")} + <div className={"type-icon filter " + (this.isRefSelected(this.webRef) ? "selected" : "not-selected")} ref={this.webRef} data-selected={this.getInitialStatus(DocTypes.WEB)} onClick={() => { this.onClick(DocTypes.WEB); }}> - <FontAwesomeIcon className="fontawesome-icon" style={{ order: 5 }} icon={faGlobeAsia} /> + <FontAwesomeIcon className="fontawesome-icon filter" style={{ order: 5 }} icon={faGlobeAsia} /> </div> <div className="filter-description">Web</div> </div> <div className="type-outer"> - <div className={"type-icon " + (this.isRefSelected(this.linkRef) ? "selected" : "not-selected")} + <div className={"type-icon filter " + (this.isRefSelected(this.linkRef) ? "selected" : "not-selected")} ref={this.linkRef} data-selected={this.getInitialStatus(DocTypes.LINK)} onClick={() => { this.onClick(DocTypes.LINK); }}> - <FontAwesomeIcon className="fontawesome-icon" style={{ order: 6 }} icon={faLink} /> + <FontAwesomeIcon className="fontawesome-icon filter" style={{ order: 6 }} icon={faLink} /> </div> <div className="filter-description">Link</div> </div> <div className="type-outer"> - <div className={"type-icon " + (this.isRefSelected(this.audioRef) ? "selected" : "not-selected")} + <div className={"type-icon filter " + (this.isRefSelected(this.audioRef) ? "selected" : "not-selected")} ref={this.audioRef} data-selected={this.getInitialStatus(DocTypes.AUDIO)} onClick={() => { this.onClick(DocTypes.AUDIO); }}> - <FontAwesomeIcon className="fontawesome-icon" style={{ order: 7 }} icon={faMusic} /> + <FontAwesomeIcon className="fontawesome-icon filter" style={{ order: 7 }} icon={faMusic} /> </div> <div className="filter-description">Audio</div> </div> <div className="type-outer"> - <div className={"type-icon " + (this.isRefSelected(this.textRef) ? "selected" : "not-selected")} + <div className={"type-icon filter " + (this.isRefSelected(this.textRef) ? "selected" : "not-selected")} ref={this.textRef} data-selected={this.getInitialStatus(DocTypes.TEXT)} onClick={() => { this.onClick(DocTypes.TEXT); }}> - <FontAwesomeIcon className="fontawesome-icon" style={{ order: 8 }} icon={faStickyNote} /> + <FontAwesomeIcon className="fontawesome-icon filter" style={{ order: 8 }} icon={faStickyNote} /> </div> <div className="filter-description">Text</div> </div> diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 0303707c7..0b9b84030 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -27,6 +27,9 @@ import { type } from 'os'; @observer export class SearchBox extends React.Component { + + static Instance: SearchBox; + @observable _searchString: string = ""; //if true, any keywords can be used. if false, all keywords are required. @observable _wordStatus: boolean = true; @@ -35,6 +38,21 @@ export class SearchBox extends React.Component { @observable private _resultsOpen: boolean = false; @observable private _results: Doc[] = []; @observable filterBoxStatus: boolean = false; + @observable filterMenuRef: React.RefObject<HTMLDivElement> = React.createRef(); + @observable filterButtonRef: React.RefObject<HTMLButtonElement> = React.createRef(); + + constructor(props: Readonly<{}>) { + super(props); + SearchBox.Instance = this; + } + + componentDidMount = () => { + document.addEventListener("pointerdown", (e) => { + if (e.timeStamp !== this._pointerTime) { + this.closeSearch(); + } + }); + } @action.bound onChange(e: React.ChangeEvent<HTMLInputElement>) { @@ -45,7 +63,7 @@ export class SearchBox extends React.Component { submitSearch = async () => { let query = this._searchString; - if(!this._wordStatus){ + if (!this._wordStatus) { let oldWords = query.split(" "); let newWords: string[] = []; console.log(oldWords); @@ -87,16 +105,16 @@ export class SearchBox extends React.Component { return this.filterDocs(docs); } - @action filterDocs(docs: Doc[]){ + @action filterDocs(docs: Doc[]) { console.log(this._icons) - if(this._icons.length === 0){ + if (this._icons.length === 0) { console.log("length is 0") return docs; } let finalDocs: Doc[] = []; docs.forEach(doc => { let layoutresult = Cast(doc.type, "string", ""); - if(this._icons.includes(layoutresult)){ + if (this._icons.includes(layoutresult)) { finalDocs.push(doc) } }); @@ -120,51 +138,107 @@ export class SearchBox extends React.Component { } } - @action - handleSearchClick = (e: Event): void => { - let element = document.getElementsByClassName((e.target as any).className)[0]; - let name: string = (e.target as any).className; - //handles case with filter button - if (String(name).indexOf("filter") !== -1 || String(name).indexOf("SVG") !== -1) { - this._resultsOpen = false; - this._results = []; - this._open = true; - } - else if (element && element.parentElement) { - //if the filter element is found, show the form and hide the results - if (this.findAncestor(element, "filter-form")) { - this._resultsOpen = false; - this._results = []; - this._open = true; - } - //if in main search div, keep results open and close filter - else if (this.findAncestor(element, "main-searchDiv")) { - this._resultsOpen = true; - this._open = false; - } - } - //not in either, close both - else { - this._resultsOpen = false; - this._results = []; - this._open = false; - } + // isClickInFilter(curElement: Element) { + // console.log(curElement) + // let name: string = curElement.className; + // if (name.indexOf("filter") !== -1) { + // return true; + // } + // else { + // return this.findAncestor2(name); - } + // // if(curElement.parentElement){ + // // let parentName = curElement.parentElement.className; + // // if(parentName.indexOf("filter") !== -1){ + // // return true; + // // } + // // } + // } + // return false; + // } - //finds ancestor div that matches class name passed in, if not found false returned - findAncestor(curElement: any, cls: string) { - while ((curElement = curElement.parentElement) && !curElement.classList.contains(cls)); - return curElement; - } + // findAncestor2(elementName: string){ - componentWillMount() { - document.addEventListener('mousedown', this.handleSearchClick, false); - } + // if(elementName === ""){ + // return false; + // } - componentWillUnmount() { - document.removeEventListener('mousedown', this.handleSearchClick, false); - } + // if(String(elementName).indexOf("filter") !== -1){ + // return true; + // } + + // if(String(elementName).indexOf("SVG")!==-1){ + // console.log("is svg") + // return true; + // } + + // let curElement: Element = document.getElementsByClassName(elementName)[0]; + // let name: string; + // while(curElement.parentElement){ + // name = curElement.className; + // if(name.indexOf("filter") !== -1){ + // return true; + // } + // curElement = curElement.parentElement; + // } + // return false; + // } + + // @action + // handleSearchClick = (e: MouseEvent): void => { + + // // console.log(e) + + // let name: string = (e.target as any).className; + + + + // // console.log((e.target as any).className) + // let element = document.getElementsByClassName((e.target as any).className)[0]; + // // console.log(this.findAncestor2(name)); + // //handles case with filter button + // if (String(name).indexOf("filter") !== -1 || String(name).indexOf("SVG") !== -1) { + // this._resultsOpen = false; + // this._results = []; + // this._open = true; + // // console.log("name is svg") + // } + // else if (element && element.parentElement) { + // //if the filter element is found, show the form and hide the results + // if (this.findAncestor(element, "filter-form")) { + // this._resultsOpen = false; + // this._results = []; + // this._open = true; + // // console.log("parent is filter form") + // } + // //if in main search div, keep results open and close filter + // else if (this.findAncestor(element, "main-searchDiv")) { + // this._resultsOpen = true; + // this._open = false; + // } + // } + // //not in either, close both + // else { + // this._resultsOpen = false; + // this._results = []; + // this._open = false; + // } + + // } + + // //finds ancestor div that matches class name passed in, if not found false returned + // findAncestor(curElement: any, cls: string) { + // while ((curElement = curElement.parentElement) && !curElement.classList.contains(cls)); + // return curElement; + // } + + // componentWillMount() { + // document.addEventListener('mousedown', this.handleSearchClick, false); + // } + + // componentWillUnmount() { + // document.removeEventListener('mousedown', this.handleSearchClick, false); + // } enter = (e: React.KeyboardEvent) => { if (e.key === "Enter") { @@ -172,6 +246,19 @@ export class SearchBox extends React.Component { } } + @action.bound + closeSearch = () => { + this._open = false; + this._resultsOpen = false; + } + + @action + openFilter = () => { + this._open = true; + this._resultsOpen = false; + this._results = []; + } + collectionRef = React.createRef<HTMLSpanElement>(); startDragCollection = async () => { const results = await this.getResults(this._searchString); @@ -235,6 +322,22 @@ export class SearchBox extends React.Component { return this._icons; } + private _pointerTime: number = -1; + + stopProp = (e: React.PointerEvent) => { + e.stopPropagation(); + console.log('stopping prop') + this._pointerTime = e.timeStamp; + } + + @action.bound + openSearch(e: React.PointerEvent) { + e.stopPropagation(); + this._open = false; + this._resultsOpen = true; + this._pointerTime = e.timeStamp; + } + // 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} @@ -247,9 +350,9 @@ export class SearchBox extends React.Component { <FontAwesomeIcon icon="object-group" className="searchBox-barChild" size="lg" /> </span> <input value={this._searchString} onChange={this.onChange} type="text" placeholder="Search..." - className="searchBox-barChild searchBox-input" onKeyPress={this.enter} + className="searchBox-barChild searchBox-input" onPointerDown={this.openSearch} onKeyPress={this.enter} style={{ width: this._resultsOpen ? "500px" : "100px" }} /> - <button className="searchBox-barChild searchBox-filter">Filter</button> + <button className="searchBox-barChild searchBox-filter" onClick={this.openFilter} onPointerDown={this.stopProp} ref={this.filterButtonRef}>Filter</button> </div> {this._resultsOpen ? ( <div className="searchBox-results"> @@ -259,14 +362,14 @@ export class SearchBox extends React.Component { </div> {/* these all need class names in order to find ancestor - please do not delete */} {this._open ? ( - <div className="filter-form" id="filter" style={this._open ? { display: "flex" } : { display: "none" }}> + <div className="filter-form" onPointerDown={this.stopProp} ref={this.filterMenuRef} id="filter" style={this._open ? { display: "flex" } : { display: "none" }}> <div className="filter-form filter-div" id="header">Filter Search Results</div> <div className="filter-form " id="option"> <div className="required-words filter-div"> <ToggleBar originalStatus={this._wordStatus} optionOne={"Include Any Keywords"} optionTwo={"Include All Keywords"} changeStatus={this.handleWordQueryChange} /> </div> <div className="type-of-node filter-div"> - <IconBar updateIcon={this.updateIcon} getIcons={this.getIcons}/> + <IconBar updateIcon={this.updateIcon} getIcons={this.getIcons} /> </div> <div className="filter-collection filter-div"> temp for filtering by collection @@ -275,7 +378,7 @@ export class SearchBox extends React.Component { temp for filtering where in doc the keywords are found </div> </div> - <button className = "reset-filter">Reset Filters</button> + <button className="reset-filter">Reset Filters</button> </div> ) : undefined} </div> diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx index d470090f8..65f4dfd31 100644 --- a/src/client/views/search/SearchItem.tsx +++ b/src/client/views/search/SearchItem.tsx @@ -15,6 +15,7 @@ import { observer } from "mobx-react"; import "./SearchItem.scss"; import { CollectionViewType } from "../collections/CollectionBaseView"; import { DocTypes } from "../../documents/Documents"; +import { SearchBox } from "./SearchBox"; export interface SearchItemProps { doc: Doc; @@ -133,10 +134,16 @@ export class SearchItem extends React.Component<SearchItemProps> { return num.toString() + " links"; } + pointerDown = (e: React.PointerEvent) => { + SearchBox.Instance.openSearch(e); + } + render() { return ( - <div className="search-overview"> - <div className="search-item" ref={this.collectionRef} id="result" onClick={this.onClick} onPointerDown={SetupDrag(this.collectionRef, this.startDocDrag)} > + <div className="search-overview" onPointerDown = {this.pointerDown}> + <div className="search-item" ref={this.collectionRef} id="result" onClick={this.onClick} onPointerDown={ () => { + this.pointerDown; + SetupDrag(this.collectionRef, this.startDocDrag);}} > <div className="main-search-info"> <div className="search-title" id="result" >{this.props.doc.title}</div> <div className="search-info"> |