diff options
author | bobzel <zzzman@gmail.com> | 2021-09-16 21:04:57 -0400 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2021-09-16 21:04:57 -0400 |
commit | 64119b5d8766725025b8b2bfda72f2401bba0f00 (patch) | |
tree | c35230d13e954e7541d5e433f190454dd700c5cd /src | |
parent | 68b20c7cf3b3472a7c7adbdcffa2318ad3549d8d (diff) |
added search() component method. changed search menu to call search on documents that match search string. added seach bar for web pages.
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/GlobalKeyHandler.ts | 16 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 4 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 1 | ||||
-rw-r--r-- | src/client/views/nodes/PDFBox.tsx | 18 | ||||
-rw-r--r-- | src/client/views/nodes/WebBox.scss | 86 | ||||
-rw-r--r-- | src/client/views/nodes/WebBox.tsx | 65 | ||||
-rw-r--r-- | src/client/views/pdf/PDFViewer.tsx | 13 | ||||
-rw-r--r-- | src/client/views/search/SearchBox.tsx | 15 |
8 files changed, 172 insertions, 46 deletions
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index f66c9c788..de6f4ae8b 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -10,10 +10,11 @@ import { Cast, PromiseValue } from "../../fields/Types"; import { GoogleAuthenticationManager } from "../apis/GoogleAuthenticationManager"; import { DocServer } from "../DocServer"; import { DocumentType } from "../documents/DocumentTypes"; -import { DictationManager } from "../util/DictationManager"; +import { CurrentUserUtils } from "../util/CurrentUserUtils"; import { DragManager } from "../util/DragManager"; import { GroupManager } from "../util/GroupManager"; import { SelectionManager } from "../util/SelectionManager"; +import { SettingsManager } from "../util/SettingsManager"; import { SharingManager } from "../util/SharingManager"; import { SnappingManager } from "../util/SnappingManager"; import { undoBatch, UndoManager } from "../util/UndoManager"; @@ -27,8 +28,6 @@ import { LightboxView } from "./LightboxView"; import { MainView } from "./MainView"; import { DocumentLinksButton } from "./nodes/DocumentLinksButton"; import { AnchorMenu } from "./pdf/AnchorMenu"; -import { CurrentUserUtils } from "../util/CurrentUserUtils"; -import { SettingsManager } from "../util/SettingsManager"; const modifiers = ["control", "meta", "shift", "alt"]; type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo | Promise<KeyControlInfo>; @@ -222,10 +221,13 @@ 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": - const searchBtn = Doc.UserDoc().searchBtn as Doc; - - if (searchBtn) { - MainView.Instance.selectMenu(searchBtn); + if (SelectionManager.Views().length === 1 && SelectionManager.Views()[0].ComponentView?.search) { + SelectionManager.Views()[0].ComponentView?.search?.("", false, false); + } else { + const searchBtn = Doc.UserDoc().searchBtn as Doc; + if (searchBtn) { + MainView.Instance.selectMenu(searchBtn); + } } break; case "o": diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index be0b078ec..94cf1c5a6 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -902,7 +902,9 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P HistoryUtil.pushState(state); } } - SelectionManager.DeselectAll(); + if (SelectionManager.Views().length !== 1 || SelectionManager.Views()[0].Document !== doc) { + SelectionManager.DeselectAll(); + } if (this.props.Document.scrollHeight || this.props.Document.scrollTop !== undefined) { this.props.focus(doc, options); } else { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index e8a78d75c..187905960 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -96,6 +96,7 @@ export interface DocComponentView { annotationKey?: string; getTitle?: () => string; getScrollHeight?: () => number; + search?: (str:string, bwd?:boolean, clear?:boolean) => boolean; } export interface DocumentViewSharedProps { renderDepth: number; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index ce851b830..274d166f1 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -96,7 +96,17 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps !this.Document._fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw)); } - public search = (string: string, fwd: boolean) => this._pdfViewer?.search(string, fwd); + public search = action((searchString: string, bwd?: boolean, clear: boolean = false) => { + if (!this._searching && !clear) { + this._searching = true; + setTimeout(() => { + this._searchRef.current?.focus(); + this._searchRef.current?.select(); + this._searchRef.current?.setRangeText(searchString); + }); + } + return this._pdfViewer?.search(searchString, bwd, clear) || false; + }); public prevAnnotation = () => this._pdfViewer?.prevAnnotation(); public nextAnnotation = () => this._pdfViewer?.nextAnnotation(); public backPage = () => { this.Document._curPage = (this.Document._curPage || 1) - 1; return true; }; @@ -184,8 +194,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps <div className="pdfBox-overlayCont" onPointerDown={(e) => e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}> <button className="pdfBox-overlayButton" title={searchTitle} /> <input className="pdfBox-searchBar" placeholder="Search" ref={this._searchRef} onChange={this.searchStringChanged} - onKeyDown={e => e.keyCode === KeyCodes.ENTER && this.search(this._searchString, !e.shiftKey)} /> - <button className="pdfBox-search" title="Search" onClick={e => this.search(this._searchString, !e.shiftKey)}> + onKeyDown={e => e.keyCode === KeyCodes.ENTER && this.search(this._searchString, e.shiftKey)} /> + <button className="pdfBox-search" title="Search" onClick={e => this.search(this._searchString, e.shiftKey)}> <FontAwesomeIcon icon="search" size="sm" /> </button> <button className="pdfBox-prevIcon" title="Previous Annotation" onClick={this.prevAnnotation} > @@ -196,7 +206,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps </button> </div> <button className="pdfBox-overlayButton" title={searchTitle} - onClick={action(() => { this._searching = !this._searching; this.search("mxytzlaf", true); })} > + onClick={action(() => { this._searching = !this._searching; this.search("", true, true); })} > <div className="pdfBox-overlayButton-arrow" onPointerDown={(e) => e.stopPropagation()} /> <div className="pdfBox-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}> <FontAwesomeIcon icon={this._searching ? "times" : "search"} size="lg" /> diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss index 417a17d96..79289abaa 100644 --- a/src/client/views/nodes/WebBox.scss +++ b/src/client/views/nodes/WebBox.scss @@ -6,6 +6,92 @@ position: relative; display: flex; + .webBox-ui { + position: absolute; + width: 100%; + height: 100%; + z-index: 1; + pointer-events: none; + top: 0; + left: 0; + overflow: hidden; + + .webBox-overlayButton { + border-bottom-left-radius: 50%; + display: flex; + justify-content: space-evenly; + align-items: center; + height: 20px; + background: none; + padding: 0; + position: absolute; + pointer-events: all; + color: white; + bottom: 0; + right: 0; + + .webBox-overlayButton-arrow { + width: 0; + height: 0; + border-top: 10px solid transparent; + border-bottom: 10px solid transparent; + border-right: 15px solid #121721; + transition: all 0.5s; + } + + .webBox-overlayButton-iconCont { + background: #121721; + height: 20px; + width: 25px; + display: flex; + position: relative; + align-items: center; + justify-content: center; + border-radius: 3px; + pointer-events: all; + } + } + + .webBox-nextIcon, + .webBox-prevIcon { + background: #121721; + color: white; + height: 20px; + width: 25px; + display: flex; + position: relative; + align-items: center; + justify-content: center; + border-radius: 3px; + pointer-events: all; + padding: 0px; + } + + .webBox-overlayButton:hover { + background: none; + } + + + .webBox-overlayCont { + position: absolute; + width: calc(100% - 40px); + height: 20px; + background: #121721; + bottom: 0; + display: flex; + justify-content: center; + align-items: center; + overflow: hidden; + transition: left .5s; + pointer-events: all; + + .webBox-searchBar { + width: 70%; + font-size: 14px; + } + } + } + .webBox-overlayButton-sidebar { background: #121721; height: 25px; diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 0c9c36418..cb9256595 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -16,6 +16,7 @@ import { TraceMobx } from "../../../fields/util"; import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, Utils } from "../../../Utils"; import { Docs } from "../../documents/Documents"; import { CurrentUserUtils } from "../../util/CurrentUserUtils"; +import { KeyCodes } from "../../util/KeyCodes"; import { Scripting } from "../../util/Scripting"; import { SnappingManager } from "../../util/SnappingManager"; import { undoBatch } from "../../util/UndoManager"; @@ -52,6 +53,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps private _keyInput = React.createRef<HTMLInputElement>(); private _initialScroll: Opt<number>; private _sidebarRef = React.createRef<SidebarAnnos>(); + private _searchRef = React.createRef<HTMLInputElement>(); + private _searchString = ""; + @observable private _searching: boolean = false; + @observable _showSidebar = false; @observable private _scrollTimer: any; @observable private _overlayAnnoInfo: Opt<Doc>; @observable private _marqueeing: number[] | undefined; @@ -73,6 +78,24 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || this.Document[HeightSym]() / this.Document[WidthSym]() * 850); } + @action + search = (searchString: string, bwd?: boolean, clear: boolean = false) => { + if (!this._searching && !clear) { + this._searching = true; + setTimeout(() => { + this._searchRef.current?.focus(); + this._searchRef.current?.select(); + this._searchRef.current?.setRangeText(searchString); + }); + } + if (clear) { + this._iframe?.contentWindow?.getSelection()?.empty(); + } + if (searchString) { + (this._iframe?.contentWindow as any)?.find(searchString, false, bwd, true); + } + return true; + } async componentDidMount() { this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link. @@ -524,9 +547,29 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps </div>; } - @observable _showSidebar = false; @computed get SidebarShown() { return this._showSidebar || this.layoutDoc._showSidebar ? true : false; } + @computed get searchUI() { + return <div className="webBox-ui" + onPointerDown={e => e.stopPropagation()} style={{ display: this.props.isContentActive() ? "flex" : "none" }}> + <div className="webBox-overlayCont" onPointerDown={(e) => e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}> + <button className="webBox-overlayButton" title={"search"} /> + <input className="webBox-searchBar" placeholder="Search" ref={this._searchRef} onChange={this.searchStringChanged} + onKeyDown={e => e.keyCode === KeyCodes.ENTER && this.search(this._searchString, e.shiftKey)} /> + <button className="webBox-search" title="Search" onClick={e => this.search(this._searchString, e.shiftKey)}> + <FontAwesomeIcon icon="search" size="sm" /> + </button> + </div> + <button className="webBox-overlayButton" title={"search"} + onClick={action(() => { this._searching = !this._searching; this.search("", false, true); })} > + <div className="webBox-overlayButton-arrow" onPointerDown={(e) => e.stopPropagation()} /> + <div className="webBox-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}> + <FontAwesomeIcon icon={this._searching ? "times" : "search"} size="lg" /> + </div> + </button> + </div>; + } + searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => this._searchString = e.currentTarget.value; showInfo = action((anno: Opt<Doc>) => this._overlayAnnoInfo = anno); setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => this._setPreviewCursor = func; panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0); @@ -623,25 +666,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps onPointerDown={this.sidebarBtnDown} > <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={"comment-alt"} size="sm" /> </div> - {/* <div className="webBox-search" key="search" title="Search" - style={{ - top: 10, - left: 10, - width: 200, - height: 50, - display: "block", - position: "absolute" - }} - > - <input onKeyPress={e => { - if (e.key === "Enter") { - (this._iframe?.contentWindow as any)?.find(e.target.value); - } - }} onChange={e => { - this._iframe?.contentWindow?.getSelection()?.empty(); - (this._iframe?.contentWindow as any)?.find(e.target.value) - }}></input> - </div> */} + {!this.props.isContentActive() ? (null) : this.searchUI} </div>); } } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index d953c6b6c..7aa18e46f 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -125,12 +125,6 @@ export class PDFViewer extends React.Component<IViewerProps> { } }); - this._disposers.searchMatch = reaction(() => Doc.IsSearchMatch(this.props.rootDoc), - m => { - if (m) (this._lastSearch = true) && this.search(Doc.SearchQuery(), m.searchMatch > 0); - else !(this._lastSearch = false) && setTimeout(() => !this._lastSearch && this.search("", false, true), 200); - }, { fireImmediately: true }); - this._disposers.selected = reaction(() => this.props.isSelected(), selected => { // if (!selected) { @@ -337,10 +331,10 @@ export class PDFViewer extends React.Component<IViewerProps> { } @action - search = (searchString: string, fwd: boolean, clear: boolean = false) => { + search = (searchString: string, bwd?: boolean, clear: boolean = false) => { const findOpts = { caseSensitive: false, - findPrevious: !fwd, + findPrevious: bwd, highlightAll: true, phraseSearch: true, query: searchString @@ -348,7 +342,7 @@ export class PDFViewer extends React.Component<IViewerProps> { if (clear) { this._pdfViewer?.findController.executeCommand('reset', { query: "" }); } else if (!searchString) { - fwd ? this.nextAnnotation() : this.prevAnnotation(); + bwd ? this.prevAnnotation() : this.nextAnnotation(); } else if (this._pdfViewer?.pageViewsReady) { this._pdfViewer.findController.executeCommand('findagain', findOpts); } @@ -357,6 +351,7 @@ export class PDFViewer extends React.Component<IViewerProps> { this._mainCont.current.addEventListener("pagesloaded", executeFind); this._mainCont.current.addEventListener("pagerendered", executeFind); } + return true; } @action diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 9c353e9d0..3612bd7c4 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -104,9 +104,9 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps, SearchBoxDoc * This method is called when the user clicks on a search result. The _selectedResult is * updated accordingly and the doc is highlighted with the selectElement method. */ - onResultClick = action((doc: Doc) => { - this.selectElement(doc); + onResultClick = action(async (doc: Doc) => { this._selectedResult = doc; + this.selectElement(doc, () => DocumentManager.Instance.getFirstDocumentView(doc)?.ComponentView?.search?.(this._searchString, undefined, false)); }); makeLink = action((linkTo: Doc) => { @@ -269,8 +269,8 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps, SearchBoxDoc * This method selects a doc by either jumping to it (centering/zooming in on it) * or opening it in a new tab. */ - selectElement = async (doc: Doc) => { - await DocumentManager.Instance.jumpToDocument(doc, true); + selectElement = async (doc: Doc, finishFunc: () => void) => { + await DocumentManager.Instance.jumpToDocument(doc, true, undefined, undefined, undefined, undefined, undefined, finishFunc); } /** @@ -307,7 +307,12 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps, SearchBoxDoc validResults++; return ( <Tooltip key={result[0][Id]} placement={"right"} title={<><div className="dash-tooltip">{title}</div></>}> - <div onClick={isLinkSearch ? () => this.makeLink(result[0]) : () => this.onResultClick(result[0])} className={className}> + <div onClick={isLinkSearch ? + () => this.makeLink(result[0]) : + e => { + this.onResultClick(result[0]); + e.stopPropagation(); + }} className={className}> <div className="searchBox-result-title"> {title} </div> |