Skip to content

Commit 8f8c351

Browse files
committed
UX improvements for searchbar: input focusing, arrows handling
1 parent 9f9eb0c commit 8f8c351

File tree

2 files changed

+64
-6
lines changed

2 files changed

+64
-6
lines changed

scaladoc-js/resources/scaladoc-searchbar.css

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,11 @@
4040
display: none;
4141
}
4242

43-
#scaladoc-searchbar {
44-
position: absolute;
43+
#scala3doc-searchbar {
44+
position: fixed;
4545
top: 50px;
4646
right: 40px;
47+
z-index: 5;
4748
width: calc(100% - 360px);
4849
box-shadow: 0 2px 16px 0 rgba(0, 42, 76, 0.15);
4950
font-size: 13px;
@@ -70,7 +71,8 @@
7071
overflow: auto;
7172
}
7273

73-
.scaladoc-searchbar-result {
74+
.scala3doc-searchbar-result {
75+
background: white;
7476
line-height: 32px;
7577
padding-left: 10px;
7678
padding-right: 10px;
@@ -80,7 +82,7 @@
8082
margin-top: 10px;
8183
}
8284

83-
.scaladoc-searchbar-result:hover {
85+
.scala3doc-searchbar-result[selected] {
8486
background-color: #d4edff;
8587
}
8688

scaladoc-js/src/searchbar/SearchbarComponent.scala

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]):
2222

2323
wrapper.appendChild(resultA)
2424
wrapper.appendChild(location)
25+
wrapper.addEventListener("mouseover", {
26+
case e: MouseEvent => handleHover(wrapper)
27+
})
2528
wrapper
2629

2730
def handleNewQuery(query: String) =
@@ -52,7 +55,10 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]):
5255
if (document.body.contains(rootDiv)) {
5356
document.body.removeChild(rootDiv)
5457
}
55-
else document.body.appendChild(rootDiv)
58+
else {
59+
document.body.appendChild(rootDiv)
60+
input.focus()
61+
}
5662

5763
val element = createNestingDiv("search-content")(
5864
createNestingDiv("search-container")(
@@ -69,6 +75,7 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]):
6975
val element = document.createElement("input").asInstanceOf[html.Input]
7076
element.id = "scaladoc-searchbar-input"
7177
element.addEventListener("input", (e) => handleNewQuery(e.target.asInstanceOf[html.Input].value))
78+
element.autocomplete = "off"
7279
element
7380

7481
private val resultsDiv: html.Div =
@@ -94,9 +101,58 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]):
94101
document.body.removeChild(element)
95102
}
96103
)
97-
element.id = "scaladoc-searchbar"
104+
element.addEventListener("keydown", {
105+
case e: KeyboardEvent =>
106+
if e.keyCode == 40 then handleArrowDown()
107+
else if e.keyCode == 38 then handleArrowUp()
108+
else if e.keyCode == 13 then handleEnter()
109+
})
110+
element.id = "scala3doc-searchbar"
98111
element.appendChild(input)
99112
element.appendChild(resultsDiv)
100113
element
101114

115+
private def handleArrowUp() = {
116+
val selectedElement = resultsDiv.querySelector("[selected]")
117+
if selectedElement != null then {
118+
selectedElement.removeAttribute("selected")
119+
val sibling = selectedElement.previousElementSibling
120+
if sibling != null then {
121+
sibling.setAttribute("selected", "")
122+
resultsDiv.scrollTop = sibling.asInstanceOf[html.Element].offsetTop - (2 * sibling.asInstanceOf[html.Element].clientHeight)
123+
}
124+
}
125+
}
126+
private def handleArrowDown() = {
127+
val selectedElement = resultsDiv.querySelector("[selected]")
128+
if selectedElement != null then {
129+
val sibling = selectedElement.nextElementSibling
130+
if sibling != null then {
131+
selectedElement.removeAttribute("selected")
132+
sibling.setAttribute("selected", "")
133+
resultsDiv.scrollTop = sibling.asInstanceOf[html.Element].offsetTop - (2 * sibling.asInstanceOf[html.Element].clientHeight)
134+
}
135+
} else {
136+
val firstResult = resultsDiv.firstElementChild
137+
if firstResult != null then {
138+
firstResult.setAttribute("selected", "")
139+
resultsDiv.scrollTop = firstResult.asInstanceOf[html.Element].offsetTop - (2 * firstResult.asInstanceOf[html.Element].clientHeight)
140+
}
141+
}
142+
}
143+
private def handleEnter() = {
144+
val selectedElement = resultsDiv.querySelector("[selected] a").asInstanceOf[html.Element]
145+
if selectedElement != null then {
146+
selectedElement.click()
147+
}
148+
}
149+
150+
private def handleHover(elem: html.Element) = {
151+
val selectedElement = resultsDiv.querySelector("[selected]")
152+
if selectedElement != null then {
153+
selectedElement.removeAttribute("selected")
154+
}
155+
elem.setAttribute("selected","")
156+
}
157+
102158
handleNewQuery("")

0 commit comments

Comments
 (0)