Skip to content

Commit 930def0

Browse files
committed
Bring search bar into view on desktop
This commit changes how the search bar is brought into view on desktop as brought up in #1860: when using one of the keyboard shortcuts to focus the input, the entire page jumps to the top making you lose your scroll position. On mobile, technically smaller screens, this is remedied by causing the input to slide into view on scroll up, but I don't believe this is a desirable solution for desktop. The following changes are introduced: * Using one of the keyboard shortcuts will focus the search input causing it to stick to the top. * It will continue to stick to the top so long as it is focused allowing you to scroll with it open. * The slide-on-scroll effect has been changed to only fire on touch-enabled devices as opposed to just smaller screens. This is allows us to make the hexdocs window very small and still use keyboard shortcuts--Useful on laptops. Closes #1860
1 parent 7e83e8c commit 930def0

File tree

5 files changed

+96
-34
lines changed

5 files changed

+96
-34
lines changed

assets/css/layout.css

+23-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,28 @@ body.sidebar-closed .content {
122122
left: 0;
123123
}
124124

125-
@media screen and (max-width: 768px) {
125+
@media screen and (hover: hover) and (max-width: 768px) {
126+
body.sidebar-opening .content {
127+
left: 0;
128+
width: 100%;
129+
}
130+
131+
body.sidebar-closed .sidebar-button {
132+
position: absolute;
133+
top: 14px;
134+
}
135+
136+
body.sidebar-closed .sidebar-button.fixed {
137+
position: fixed;
138+
padding: 16px 12px 18px 19px;
139+
}
140+
141+
body.sidebar-closed .sidebar-button.fixed-top {
142+
position: fixed;
143+
}
144+
}
145+
146+
@media screen and (hover: none) {
126147
.content,
127148
body.sidebar-opening .content {
128149
left: 0;
@@ -137,6 +158,7 @@ body.sidebar-closed .content {
137158

138159
body.sidebar-closed .sidebar-button {
139160
position: absolute;
161+
top: 14px;
140162
}
141163

142164
.sm-fixed {

assets/css/search-bar.css

+17-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
11
.top-search {
2+
position: relative;
3+
top: 0;
4+
z-index: 101;
25
margin-top: 10px;
36
background-color: var(--background);
7+
padding: 0.8rem 0;
8+
}
9+
10+
@media (max-width: 480px) or ((min-width: 481px) and (max-width: 768px)) {
11+
.top-search {
12+
padding: 0.8rem 1.8rem 0.8rem 3rem;
13+
margin-left: -1rem;
14+
margin-right: -1rem;
15+
}
16+
}
17+
18+
.top-search.sticky {
19+
position: sticky;
420
}
521

622
.search-settings {
@@ -114,13 +130,7 @@
114130
color: var(--iconAction);
115131
}
116132

117-
@media (max-width: 480px) or ((min-width: 481px) and (max-width: 768px)) {
118-
.search-bar {
119-
margin-left: 0px;
120-
}
121-
}
122-
123-
@media (max-width: 768px) {
133+
@media (hover: none) {
124134
.top-search {
125135
margin-top: 0;
126136
position: absolute;

assets/js/helpers.js

+12-1
Original file line numberDiff line numberDiff line change
@@ -161,10 +161,21 @@ export function getProjectNameAndVersion () {
161161
}
162162

163163
/**
164-
* Return `true` if the client's OS is MacOS
164+
* Return `true` if the client's OS is MacOS.
165165
*
166166
* @return {Boolean}
167167
*/
168168
export function isMacOS () {
169169
return /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform)
170170
}
171+
172+
/**
173+
* Return `true` if the client's device is touch-enabled.
174+
*
175+
* @return {Boolean}
176+
*/
177+
export function isTouchDevice () {
178+
return (('ontouchstart' in window) ||
179+
(navigator.maxTouchPoints > 0) ||
180+
(navigator.msMaxTouchPoints > 0))
181+
}

assets/js/search-bar.js

+44-24
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ import {
77
AUTOCOMPLETE_CONTAINER_SELECTOR,
88
AUTOCOMPLETE_SUGGESTION_SELECTOR
99
} from './autocomplete/autocomplete-list'
10-
import { isMacOS, qs } from './helpers'
10+
import { isMacOS, isTouchDevice, qs } from './helpers'
1111

1212
const SEARCH_INPUT_SELECTOR = 'form.search-bar input'
1313
const SEARCH_CLOSE_BUTTON_SELECTOR = 'form.search-bar .search-close-button'
14+
const TOP_SEARCH_SELECTOR = '.top-search'
1415

1516
/**
1617
* Initializes the sidebar search box.
@@ -34,6 +35,16 @@ export function setSearchInputValue (value) {
3435
*/
3536
export function focusSearchInput () {
3637
const searchInput = qs(SEARCH_INPUT_SELECTOR)
38+
39+
if (!isTouchDevice()) {
40+
qs(TOP_SEARCH_SELECTOR).classList.add('sticky')
41+
if (window.scrollY === 0) {
42+
qs('.sidebar-button').classList.add('fixed-top')
43+
} else {
44+
qs('.sidebar-button').classList.add('fixed')
45+
}
46+
}
47+
3748
searchInput.focus()
3849
}
3950

@@ -139,38 +150,47 @@ function hideAutocomplete () {
139150
}
140151

141152
let lastScrollTop = window.scrollY
142-
const topSearch = document.querySelector('.top-search')
153+
const topSearch = document.querySelector(TOP_SEARCH_SELECTOR)
143154
const sidebarMenu = document.getElementById('sidebar-menu')
144155
const backgroundLayer = document.querySelector('.background-layer')
145156
const scrollThreshold = 70 // Set a threshold for scroll, adjust as needed
157+
const searchInput = qs(SEARCH_INPUT_SELECTOR)
158+
const sidebarButton = qs('.sidebar-button')
146159

147160
window.addEventListener('scroll', function () {
148161
const currentScroll = window.scrollY
149162

150-
// Add 'fixed' class when not at the top
151-
if (currentScroll > scrollThreshold * 2) {
152-
topSearch.classList.add('sm-fixed')
153-
sidebarMenu.classList.add('sm-fixed')
154-
backgroundLayer.classList.add('sm-fixed')
155-
}
163+
if (isTouchDevice()) {
164+
// Add 'fixed' class when not at the top
165+
if (currentScroll > scrollThreshold * 2) {
166+
topSearch.classList.add('sm-fixed')
167+
sidebarMenu.classList.add('sm-fixed')
168+
backgroundLayer.classList.add('sm-fixed')
169+
}
156170

157-
if (currentScroll === 0) {
158-
// Remove 'fixed' class when at the top
159-
topSearch.classList.remove('sm-fixed')
160-
sidebarMenu.classList.remove('sm-fixed')
161-
backgroundLayer.classList.remove('sm-fixed')
162-
}
171+
if (currentScroll === 0) {
172+
// Remove 'fixed' class when at the top
173+
topSearch.classList.remove('sm-fixed')
174+
sidebarMenu.classList.remove('sm-fixed')
175+
backgroundLayer.classList.remove('sm-fixed')
176+
}
163177

164-
if (currentScroll > lastScrollTop && currentScroll > scrollThreshold) {
165-
// Scrolling down and past the threshold
166-
topSearch.classList.add('sm-hidden')
167-
sidebarMenu.classList.add('sm-hidden')
168-
backgroundLayer.classList.add('sm-hidden')
169-
} else {
170-
// Scrolling up or at the top of the page
171-
topSearch.classList.remove('sm-hidden')
172-
sidebarMenu.classList.remove('sm-hidden')
173-
backgroundLayer.classList.remove('sm-hidden')
178+
if (currentScroll > lastScrollTop && currentScroll > scrollThreshold) {
179+
// Scrolling down and past the threshold
180+
topSearch.classList.add('sm-hidden')
181+
sidebarMenu.classList.add('sm-hidden')
182+
backgroundLayer.classList.add('sm-hidden')
183+
} else {
184+
// Scrolling up or at the top of the page
185+
topSearch.classList.remove('sm-hidden')
186+
sidebarMenu.classList.remove('sm-hidden')
187+
backgroundLayer.classList.remove('sm-hidden')
188+
}
189+
} else if (currentScroll !== lastScrollTop) {
190+
topSearch.classList.remove('sticky')
191+
sidebarButton.classList.remove('fixed')
192+
sidebarButton.classList.remove('fixed-top')
193+
searchInput.blur()
174194
}
175195

176196
lastScrollTop = currentScroll <= 0 ? 0 : currentScroll

lib/ex_doc/formatter/html/templates/sidebar_template.eex

-1
Original file line numberDiff line numberDiff line change
@@ -91,5 +91,4 @@
9191
<span class="sr-only">Settings</span>
9292
</button>
9393
</div>
94-
9594
</div>

0 commit comments

Comments
 (0)