forked from elixir-lang/ex_doc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsidebar-drawer.js
128 lines (108 loc) · 4.11 KB
/
sidebar-drawer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import throttle from 'lodash.throttle'
import { qs } from '../helpers'
import { SIDEBAR_CLASS_OPEN, SIDEBAR_CLASS_TRANSITION, SIDEBAR_PREF_CLOSED, SIDEBAR_PREF_OPEN, SIDEBAR_STATE_KEY, SIDEBAR_WIDTH_KEY, SMALL_SCREEN_BREAKPOINT } from './constants'
import { initialize as initializeList } from './sidebar-list'
import { isEmbedded } from '../globals'
const ANIMATION_DURATION = 300
const SIDEBAR_TOGGLE_SELECTOR = '.sidebar-toggle'
const smallScreenQuery = window.matchMedia(`screen and (max-width: ${SMALL_SCREEN_BREAKPOINT}px)`)
if (!isEmbedded) {
window.addEventListener('exdoc:loaded', setDefaultSidebarState)
const sidebar = document.getElementById('sidebar')
const sidebarToggle = qs(SIDEBAR_TOGGLE_SELECTOR)
sidebarToggle.addEventListener('click', toggleSidebar)
// Clicks outside small screen open sidebar should close it.
document.body.addEventListener('click', (event) => {
if (
smallScreenQuery.matches &&
isSidebarOpen() &&
!sidebar.contains(event.target) &&
!sidebarToggle.contains(event.target)
) {
toggleSidebar()
}
})
// Update drawer on width change.
// See https://github.com/elixir-lang/ex_doc/issues/736#issuecomment-307371291
let lastWindowWidth = window.innerWidth
window.addEventListener('resize', throttle(() => {
if (lastWindowWidth === window.innerWidth) return
lastWindowWidth = window.innerWidth
setDefaultSidebarState()
}, 100))
// Save sidebar width changes on user resize only.
// Size is restored on page load in inline_html.js.
const resizeObserver = new ResizeObserver(([entry]) => {
if (!entry) return
const width = entry.contentRect.width
sessionStorage.setItem(SIDEBAR_WIDTH_KEY, width)
document.body.style.setProperty('--sidebarWidth', `${width}px`)
})
// We observe on mousedown because we only care about user resize.
sidebar.addEventListener('mousedown', () => resizeObserver.observe(sidebar))
sidebar.addEventListener('mouseup', () => resizeObserver.unobserve(sidebar))
window.addEventListener('hashchange', maybeCloseSidebarOnNavigate)
}
function setDefaultSidebarState () {
const pref = sessionStorage.getItem(SIDEBAR_STATE_KEY)
const open = pref !== SIDEBAR_PREF_CLOSED && !smallScreenQuery.matches
updateSidebar(open)
}
/**
* Either opens or closes the sidebar depending on the current state.
*
* @returns {Promise} A promise resolving once the animation is finished.
*/
export function toggleSidebar () {
const open = !isSidebarOpen()
sessionStorage.setItem(SIDEBAR_STATE_KEY, open ? SIDEBAR_PREF_OPEN : SIDEBAR_PREF_CLOSED)
return transitionSidebar(open)
}
function isSidebarOpen () {
return document.body.classList.contains(SIDEBAR_CLASS_OPEN)
}
/**
- * Returns if sidebar is fully open.
- */
export function isSidebarOpened () {
return document.body.classList.contains(SIDEBAR_CLASS_OPEN) &&
!document.body.classList.contains(SIDEBAR_CLASS_TRANSITION)
}
function updateSidebar (open) {
// Lazy init list. Only needed when open.
if (open) initializeList()
document.body.classList.toggle(SIDEBAR_CLASS_OPEN, open)
qs(SIDEBAR_TOGGLE_SELECTOR).setAttribute('aria-expanded', open ? 'true' : 'false')
}
let transitionTimeout
function transitionSidebar (open) {
return new Promise((resolve) => {
document.body.classList.add(SIDEBAR_CLASS_TRANSITION)
// Reading scrollTop forces layout so next DOM update can be transitioned.
// eslint-disable-next-line no-unused-expressions
document.body.scrollTop
updateSidebar(open)
clearTimeout(transitionTimeout)
transitionTimeout = setTimeout(() => {
document.body.classList.remove(SIDEBAR_CLASS_TRANSITION)
resolve()
}, ANIMATION_DURATION)
})
}
/**
* Opens the sidebar by applying an animation.
*
* @returns {Promise} A promise resolving once the animation is finished.
*/
export function openSidebar () {
return transitionSidebar(true)
}
/**
* Closes the sidebar on small screens when navigating between sections
* on the page, for consistency with full page transitions
*/
function maybeCloseSidebarOnNavigate () {
if (smallScreenQuery.matches && isSidebarOpen()) {
transitionSidebar(false)
}
}