Skip to content

Commit ac40c44

Browse files
authored
Bring search bar into view on desktop (#1872)
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 dcb1938 commit ac40c44

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

@@ -151,38 +162,47 @@ function hideAutocomplete () {
151162
}
152163

153164
let lastScrollTop = window.scrollY
154-
const topSearch = document.querySelector('.top-search')
165+
const topSearch = document.querySelector(TOP_SEARCH_SELECTOR)
155166
const sidebarMenu = document.getElementById('sidebar-menu')
156167
const backgroundLayer = document.querySelector('.background-layer')
157168
const scrollThreshold = 70 // Set a threshold for scroll, adjust as needed
169+
const searchInput = qs(SEARCH_INPUT_SELECTOR)
170+
const sidebarButton = qs('.sidebar-button')
158171

159172
window.addEventListener('scroll', function () {
160173
const currentScroll = window.scrollY
161174

162-
// Add 'fixed' class when not at the top
163-
if (currentScroll > scrollThreshold * 2) {
164-
topSearch.classList.add('sm-fixed')
165-
sidebarMenu.classList.add('sm-fixed')
166-
backgroundLayer.classList.add('sm-fixed')
167-
}
175+
if (isTouchDevice()) {
176+
// Add 'fixed' class when not at the top
177+
if (currentScroll > scrollThreshold * 2) {
178+
topSearch.classList.add('sm-fixed')
179+
sidebarMenu.classList.add('sm-fixed')
180+
backgroundLayer.classList.add('sm-fixed')
181+
}
168182

169-
if (currentScroll === 0) {
170-
// Remove 'fixed' class when at the top
171-
topSearch.classList.remove('sm-fixed')
172-
sidebarMenu.classList.remove('sm-fixed')
173-
backgroundLayer.classList.remove('sm-fixed')
174-
}
183+
if (currentScroll === 0) {
184+
// Remove 'fixed' class when at the top
185+
topSearch.classList.remove('sm-fixed')
186+
sidebarMenu.classList.remove('sm-fixed')
187+
backgroundLayer.classList.remove('sm-fixed')
188+
}
175189

176-
if (currentScroll > lastScrollTop && currentScroll > scrollThreshold) {
177-
// Scrolling down and past the threshold
178-
topSearch.classList.add('sm-hidden')
179-
sidebarMenu.classList.add('sm-hidden')
180-
backgroundLayer.classList.add('sm-hidden')
181-
} else {
182-
// Scrolling up or at the top of the page
183-
topSearch.classList.remove('sm-hidden')
184-
sidebarMenu.classList.remove('sm-hidden')
185-
backgroundLayer.classList.remove('sm-hidden')
190+
if (currentScroll > lastScrollTop && currentScroll > scrollThreshold) {
191+
// Scrolling down and past the threshold
192+
topSearch.classList.add('sm-hidden')
193+
sidebarMenu.classList.add('sm-hidden')
194+
backgroundLayer.classList.add('sm-hidden')
195+
} else {
196+
// Scrolling up or at the top of the page
197+
topSearch.classList.remove('sm-hidden')
198+
sidebarMenu.classList.remove('sm-hidden')
199+
backgroundLayer.classList.remove('sm-hidden')
200+
}
201+
} else if (currentScroll !== lastScrollTop) {
202+
topSearch.classList.remove('sticky')
203+
sidebarButton.classList.remove('fixed')
204+
sidebarButton.classList.remove('fixed-top')
205+
searchInput.blur()
186206
}
187207

188208
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)