Skip to content

Commit d7d4a9f

Browse files
authored
Merge pull request #12568 from dylemma/search-ux
Improvements to search UX
2 parents aef7987 + fecd60e commit d7d4a9f

File tree

4 files changed

+61
-3
lines changed

4 files changed

+61
-3
lines changed

scaladoc-js/resources/scaladoc-searchbar.css

+8-1
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,14 @@
9494

9595
.scaladoc-searchbar-result a {
9696
color: #1f2326;
97+
/* for some reason, with display:block if there's a wrap between the
98+
* search result text and the location span, the dead space to the
99+
* left of the location span doesn't get treated as part of the block,
100+
* which defeats the purpose of making the <a> a block element.
101+
* But inline-block with width:100% works as desired.
102+
*/
103+
display: inline-block;
104+
width: 100%;
97105
text-indent: -20px;
98106
padding-left: 20px;
99107
}
@@ -142,4 +150,3 @@
142150
-ms-transform: rotate(45deg);
143151
transform: rotate(45deg);
144152
}
145-

scaladoc-js/src/searchbar/SearchbarComponent.scala

+27-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]):
2626

2727
wrapper.appendChild(icon)
2828
wrapper.appendChild(resultA)
29-
wrapper.appendChild(location)
29+
resultA.appendChild(location)
3030
wrapper.addEventListener("mouseover", {
3131
case e: MouseEvent => handleHover(wrapper)
3232
})
@@ -64,6 +64,8 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]):
6464
document.body.appendChild(rootDiv)
6565
input.focus()
6666
}
67+
// open the search if the user hits the `s` key when not focused on a text input
68+
document.body.addEventListener("keydown", (e: KeyboardEvent) => handleGlobalKeyDown(e))
6769

6870
val element = createNestingDiv("search-content")(
6971
createNestingDiv("search-container")(
@@ -75,7 +77,6 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]):
7577
document.getElementById("scaladoc-searchBar").appendChild(element)
7678
element
7779

78-
7980
private val input: html.Input =
8081
val element = document.createElement("input").asInstanceOf[html.Input]
8182
element.id = "scaladoc-searchbar-input"
@@ -111,6 +112,7 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]):
111112
if e.keyCode == 40 then handleArrowDown()
112113
else if e.keyCode == 38 then handleArrowUp()
113114
else if e.keyCode == 13 then handleEnter()
115+
else if e.keyCode == 27 then handleEscape()
114116
})
115117
element.id = "scaladoc-searchbar"
116118
element.appendChild(input)
@@ -151,6 +153,12 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]):
151153
selectedElement.click()
152154
}
153155
}
156+
private def handleEscape() = {
157+
// clear the search input and close the search
158+
input.value = ""
159+
handleNewQuery("")
160+
document.body.removeChild(rootDiv)
161+
}
154162

155163
private def handleHover(elem: html.Element) = {
156164
val selectedElement = resultsDiv.querySelector("[selected]")
@@ -160,4 +168,21 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]):
160168
elem.setAttribute("selected","")
161169
}
162170

171+
private def handleGlobalKeyDown(e: KeyboardEvent) = {
172+
// if the user presses the "S" key while not focused on an input, open the search
173+
if (e.key == "s" || e.key == "/") {
174+
val tag = e.target.asInstanceOf[html.Element].tagName
175+
if (tag != "INPUT" && tag != "TEXTAREA") {
176+
if (!document.body.contains(rootDiv)) {
177+
// Firefox's "quick find" uses "/" as a trigger; prevent that.
178+
e.preventDefault()
179+
180+
document.body.appendChild(rootDiv)
181+
// if we focus during the event handler, the `s` gets typed into the input
182+
window.setTimeout(() => input.focus(), 1.0)
183+
}
184+
}
185+
}
186+
}
187+
163188
handleNewQuery("")

scaladoc/resources/dotty_res/scripts/components/Input.js

+12
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,27 @@ class Input extends Component {
44

55
this.inputRef = findRef(".filterableInput");
66
this.onChangeFn = withEvent(this.inputRef, "input", this.onInputChange);
7+
this.onKeydownFn = withEvent(this.inputRef, "keydown", this.onKeydown);
78
}
89

910
onInputChange = ({ currentTarget: { value } }) => {
1011
this.props.onInputChange(value);
1112
};
1213

14+
onKeydown = (e) => {
15+
// if the user hits Escape while typing in the filter input,
16+
// clear the filter and un-focus the input
17+
if (e.keyCode == 27) {
18+
this.inputRef.value = '';
19+
this.onInputChange(e);
20+
setTimeout(() => this.inputRef.blur(), 1);
21+
}
22+
}
23+
1324
componentWillUnmount() {
1425
if (this.onChangeFn) {
1526
this.onChangeFn();
27+
this.onKeydownFn();
1628
}
1729
}
1830
}

scaladoc/resources/dotty_res/scripts/ux.js

+14
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,20 @@ window.addEventListener("DOMContentLoaded", () => {
5454
hljs.registerLanguage("scala", highlightDotty);
5555
hljs.registerAliases(["dotty", "scala3"], "scala");
5656
hljs.initHighlighting();
57+
58+
/* listen for the `F` key to be pressed, to focus on the member filter input (if it's present) */
59+
document.body.addEventListener('keydown', e => {
60+
if (e.key == "f") {
61+
const tag = e.target.tagName;
62+
if (tag != "INPUT" && tag != "TEXTAREA") {
63+
const filterInput = findRef('.documentableFilter input.filterableInput');
64+
if (filterInput != null) {
65+
// if we focus during this event handler, the `f` key gets typed into the input
66+
setTimeout(() => filterInput.focus(), 1);
67+
}
68+
}
69+
}
70+
})
5771
});
5872

5973
var zoom;

0 commit comments

Comments
 (0)