diff --git a/scaladoc-js/resources/scaladoc-searchbar.css b/scaladoc-js/resources/scaladoc-searchbar.css index cbe71226f20f..017261336dab 100644 --- a/scaladoc-js/resources/scaladoc-searchbar.css +++ b/scaladoc-js/resources/scaladoc-searchbar.css @@ -94,6 +94,14 @@ .scaladoc-searchbar-result a { color: #1f2326; + /* for some reason, with display:block if there's a wrap between the + * search result text and the location span, the dead space to the + * left of the location span doesn't get treated as part of the block, + * which defeats the purpose of making the a block element. + * But inline-block with width:100% works as desired. + */ + display: inline-block; + width: 100%; text-indent: -20px; padding-left: 20px; } @@ -142,4 +150,3 @@ -ms-transform: rotate(45deg); transform: rotate(45deg); } - diff --git a/scaladoc-js/src/searchbar/SearchbarComponent.scala b/scaladoc-js/src/searchbar/SearchbarComponent.scala index a1415bda81a5..4c710f876c8b 100644 --- a/scaladoc-js/src/searchbar/SearchbarComponent.scala +++ b/scaladoc-js/src/searchbar/SearchbarComponent.scala @@ -26,7 +26,7 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]): wrapper.appendChild(icon) wrapper.appendChild(resultA) - wrapper.appendChild(location) + resultA.appendChild(location) wrapper.addEventListener("mouseover", { case e: MouseEvent => handleHover(wrapper) }) @@ -64,6 +64,8 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]): document.body.appendChild(rootDiv) input.focus() } + // open the search if the user hits the `s` key when not focused on a text input + document.body.addEventListener("keydown", (e: KeyboardEvent) => handleGlobalKeyDown(e)) val element = createNestingDiv("search-content")( createNestingDiv("search-container")( @@ -75,7 +77,6 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]): document.getElementById("scaladoc-searchBar").appendChild(element) element - private val input: html.Input = val element = document.createElement("input").asInstanceOf[html.Input] element.id = "scaladoc-searchbar-input" @@ -111,6 +112,7 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]): if e.keyCode == 40 then handleArrowDown() else if e.keyCode == 38 then handleArrowUp() else if e.keyCode == 13 then handleEnter() + else if e.keyCode == 27 then handleEscape() }) element.id = "scaladoc-searchbar" element.appendChild(input) @@ -151,6 +153,12 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]): selectedElement.click() } } + private def handleEscape() = { + // clear the search input and close the search + input.value = "" + handleNewQuery("") + document.body.removeChild(rootDiv) + } private def handleHover(elem: html.Element) = { val selectedElement = resultsDiv.querySelector("[selected]") @@ -160,4 +168,21 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]): elem.setAttribute("selected","") } + private def handleGlobalKeyDown(e: KeyboardEvent) = { + // if the user presses the "S" key while not focused on an input, open the search + if (e.key == "s" || e.key == "/") { + val tag = e.target.asInstanceOf[html.Element].tagName + if (tag != "INPUT" && tag != "TEXTAREA") { + if (!document.body.contains(rootDiv)) { + // Firefox's "quick find" uses "/" as a trigger; prevent that. + e.preventDefault() + + document.body.appendChild(rootDiv) + // if we focus during the event handler, the `s` gets typed into the input + window.setTimeout(() => input.focus(), 1.0) + } + } + } + } + handleNewQuery("") diff --git a/scaladoc/resources/dotty_res/scripts/components/Input.js b/scaladoc/resources/dotty_res/scripts/components/Input.js index 0c7a449fa1e7..dbe6ad2d724a 100644 --- a/scaladoc/resources/dotty_res/scripts/components/Input.js +++ b/scaladoc/resources/dotty_res/scripts/components/Input.js @@ -4,15 +4,27 @@ class Input extends Component { this.inputRef = findRef(".filterableInput"); this.onChangeFn = withEvent(this.inputRef, "input", this.onInputChange); + this.onKeydownFn = withEvent(this.inputRef, "keydown", this.onKeydown); } onInputChange = ({ currentTarget: { value } }) => { this.props.onInputChange(value); }; + onKeydown = (e) => { + // if the user hits Escape while typing in the filter input, + // clear the filter and un-focus the input + if (e.keyCode == 27) { + this.inputRef.value = ''; + this.onInputChange(e); + setTimeout(() => this.inputRef.blur(), 1); + } + } + componentWillUnmount() { if (this.onChangeFn) { this.onChangeFn(); + this.onKeydownFn(); } } } diff --git a/scaladoc/resources/dotty_res/scripts/ux.js b/scaladoc/resources/dotty_res/scripts/ux.js index 710aa6ba3e05..63d284d2d93e 100644 --- a/scaladoc/resources/dotty_res/scripts/ux.js +++ b/scaladoc/resources/dotty_res/scripts/ux.js @@ -54,6 +54,20 @@ window.addEventListener("DOMContentLoaded", () => { hljs.registerLanguage("scala", highlightDotty); hljs.registerAliases(["dotty", "scala3"], "scala"); hljs.initHighlighting(); + + /* listen for the `F` key to be pressed, to focus on the member filter input (if it's present) */ + document.body.addEventListener('keydown', e => { + if (e.key == "f") { + const tag = e.target.tagName; + if (tag != "INPUT" && tag != "TEXTAREA") { + const filterInput = findRef('.documentableFilter input.filterableInput'); + if (filterInput != null) { + // if we focus during this event handler, the `f` key gets typed into the input + setTimeout(() => filterInput.focus(), 1); + } + } + } + }) }); var zoom;