mirror of
https://github.com/squidfunk/mkdocs-material.git
synced 2026-07-25 07:12:44 -04:00
Implemented basic search modal functionality
This commit is contained in:
@@ -71,19 +71,19 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
|
||||
const query = document.getElementById("query")
|
||||
query.addEventListener("focus", () => {
|
||||
document.querySelector(".md-search").classList.add("md-js__search--locked")
|
||||
document.querySelector(".md-search").dataset.mdLocked = ""
|
||||
})
|
||||
|
||||
/* Intercept click on search mode toggle */
|
||||
let offset = 0
|
||||
const toggle = document.getElementById("search")
|
||||
toggle.addEventListener("click", ev => {
|
||||
const list = document.body.classList
|
||||
toggle.addEventListener("click", () => { // TODO: click may be the wrong event...
|
||||
const list = document.body // classList md bla
|
||||
const lock = !matchMedia("only screen and (min-width: 960px)").matches
|
||||
|
||||
/* Exiting search mode */
|
||||
if (list.contains("md-js__body--locked")) {
|
||||
list.remove("md-js__body--locked")
|
||||
if (list.dataset.mdLocked) {
|
||||
delete list.dataset.mdLocked
|
||||
|
||||
/* Scroll to former position, but wait for 100ms to prevent flashes
|
||||
on iOS. A short timeout seems to do the trick */
|
||||
@@ -109,17 +109,28 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
|
||||
/* This additional check is necessary to handle fast subsequent clicks
|
||||
on the toggle and the timeout to lock the body must be cancelled */
|
||||
if (ev.target.checked) {
|
||||
if (lock)
|
||||
list.add("md-js__body--locked")
|
||||
setTimeout(() => {
|
||||
document.getElementById("md-search").focus()
|
||||
}, 200)
|
||||
}
|
||||
// if (ev.target.checked) {
|
||||
if (lock)
|
||||
list.dataset.mdLocked = ""
|
||||
setTimeout(() => {
|
||||
document.getElementById("query").focus()
|
||||
}, 200)
|
||||
// }
|
||||
}, 450)
|
||||
}
|
||||
})
|
||||
|
||||
// TODO: only do this on MOBILE and TABLET
|
||||
const toggleSearchClose = document.querySelector(".md-search__icon")
|
||||
toggleSearchClose.setAttribute("for", "search") // TODO: override query with search, when on mobile!!!
|
||||
// toggleSearchClose.addEventListener("click", ev => {
|
||||
// ev.preventDefault()
|
||||
// // ev.target
|
||||
//
|
||||
// const search = document.getElementById("search")
|
||||
// search.checked = false
|
||||
// })
|
||||
|
||||
// var toc = new Sidebar('.md-sidebar--secondary');
|
||||
// toc.listen();
|
||||
|
||||
@@ -179,6 +190,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
return response.json()
|
||||
})
|
||||
.then(data => {
|
||||
// console.log(data)
|
||||
const stars = data.stargazers_count
|
||||
const forks = data.forks_count
|
||||
// store in session!!!
|
||||
@@ -223,7 +235,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
// console.log(data)
|
||||
|
||||
/* Create index */
|
||||
const index = lunr(() => {
|
||||
const index = lunr(function() {
|
||||
/* eslint-disable no-invalid-this, lines-around-comment */
|
||||
this.field("title", { boost: 10 })
|
||||
this.field("text")
|
||||
@@ -290,10 +302,21 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
title.innerHTML = article.title
|
||||
link.appendChild(title)
|
||||
|
||||
/* Truncate a string after the given number of characters */
|
||||
const truncate = function(string, n) {
|
||||
let i = n
|
||||
if (string.length > i) {
|
||||
while (string[i] !== " " && --i > 0);
|
||||
return `${string.substring(0, i)}…`
|
||||
}
|
||||
return string
|
||||
}
|
||||
|
||||
/* Create text element */
|
||||
const text = document.createElement("p")
|
||||
text.classList.add("md-search-result__description")
|
||||
text.innerHTML = article.text // .truncate(140);
|
||||
text.innerHTML = truncate(article.text) // .truncate(140);
|
||||
text.innerHTML = truncate(article.text, 140) // .truncate(140);
|
||||
link.appendChild(text)
|
||||
|
||||
container.appendChild(li)
|
||||
@@ -324,4 +347,13 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
})
|
||||
// }, 1000);
|
||||
|
||||
fetch(
|
||||
"https://api.github.com/repos/squidfunk/mkdocs-material/releases/latest")
|
||||
.then(response => {
|
||||
return response.json()
|
||||
})
|
||||
// .then(data => {
|
||||
// // console.log(data)
|
||||
// })
|
||||
|
||||
})
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
* Definition
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
export default
|
||||
class Abstract {
|
||||
export default class Abstract {
|
||||
|
||||
/**
|
||||
* Dispatch update on next repaint
|
||||
@@ -33,8 +32,8 @@ class Abstract {
|
||||
* @constructor
|
||||
*/
|
||||
constructor() {
|
||||
// if (new.target === this.constructor)
|
||||
// throw new TypeError("Cannot construct abstract instance")
|
||||
if (this === Abstract)
|
||||
throw new TypeError("Cannot construct abstract instance")
|
||||
|
||||
/* Dispatch update on next repaint */
|
||||
this.handler_ = ev => {
|
||||
@@ -48,7 +47,6 @@ class Abstract {
|
||||
* Update state
|
||||
*
|
||||
* @abstract
|
||||
* @return {void}
|
||||
*/
|
||||
update() {
|
||||
throw new Error("update(): not implemented")
|
||||
@@ -58,7 +56,6 @@ class Abstract {
|
||||
* Reset state
|
||||
*
|
||||
* @abstract
|
||||
* @return {void}
|
||||
*/
|
||||
reset() {
|
||||
throw new Error("reset(): not implemented")
|
||||
@@ -66,8 +63,6 @@ class Abstract {
|
||||
|
||||
/**
|
||||
* Register listener for all relevant events
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
listen() {
|
||||
["scroll", "resize", "orientationchange"].forEach(name => {
|
||||
@@ -80,8 +75,6 @@ class Abstract {
|
||||
|
||||
/**
|
||||
* Unregister listener for all relevant events
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
unlisten() {
|
||||
["scroll", "resize", "orientationchange"].forEach(name => {
|
||||
|
||||
@@ -26,14 +26,13 @@ import Abstract from "./Abstract"
|
||||
* Definition
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
export default
|
||||
class Marker extends Abstract {
|
||||
export default class Marker extends Abstract {
|
||||
|
||||
/**
|
||||
* Mark anchors within the table of contents above current page y-offset
|
||||
*
|
||||
* @constructor
|
||||
* @param {(string|HTMLCollection)} els - Selector or HTML elements
|
||||
* @param {(string|NodeList<HTMLElement>)} els - Selector or HTML elements
|
||||
*/
|
||||
constructor(els) {
|
||||
super()
|
||||
@@ -57,16 +56,16 @@ class Marker extends Abstract {
|
||||
* Update anchor states
|
||||
*
|
||||
* @param {Event} ev - Event (omitted)
|
||||
* @return {void}
|
||||
*/
|
||||
update() {
|
||||
const offset = window.pageYOffset
|
||||
|
||||
/* Scroll direction is down */
|
||||
if (this.offset_ <= window.pageYOffset) {
|
||||
if (this.offset_ <= offset) {
|
||||
for (let i = this.index_ + 1; i < this.els_.length; i++) {
|
||||
if (this.anchors_[i].offsetTop <= window.pageYOffset) {
|
||||
if (this.anchors_[i].offsetTop <= offset) {
|
||||
if (i > 0)
|
||||
this.els_[i - 1].dataset.mdMarked = true
|
||||
this.els_[i - 1].dataset.mdMarked = ""
|
||||
this.index_ = i
|
||||
} else {
|
||||
break
|
||||
@@ -76,7 +75,7 @@ class Marker extends Abstract {
|
||||
/* Scroll direction is up */
|
||||
} else {
|
||||
for (let i = this.index_; i >= 0; i--) {
|
||||
if (this.anchors_[i].offsetTop > window.pageYOffset) {
|
||||
if (this.anchors_[i].offsetTop > offset) {
|
||||
if (i > 0)
|
||||
delete this.els_[i - 1].dataset.mdMarked
|
||||
} else {
|
||||
@@ -87,13 +86,11 @@ class Marker extends Abstract {
|
||||
}
|
||||
|
||||
/* Remember current offset for next iteration */
|
||||
this.offset_ = window.pageYOffset
|
||||
this.offset_ = offset
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset anchor states
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
reset() {
|
||||
[].forEach.call(this.els_, el => {
|
||||
|
||||
@@ -26,8 +26,7 @@ import Abstract from "./Abstract"
|
||||
* Definition
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
export default
|
||||
class Position extends Abstract {
|
||||
export default class Position extends Abstract {
|
||||
|
||||
/**
|
||||
* Set sidebars to locked state and limit height to parent node
|
||||
@@ -44,8 +43,15 @@ class Position extends Abstract {
|
||||
: el
|
||||
|
||||
/* Index inner and outer container */
|
||||
this.inner_ = this.el_.parentNode
|
||||
this.outer_ = this.el_.parentNode.parentNode
|
||||
const inner = this.el_.parentNode
|
||||
const outer = this.el_.parentNode.parentNode
|
||||
|
||||
/* Get top and bottom bounds */
|
||||
this.offset_ = outer.offsetTop
|
||||
this.bounds_ = {
|
||||
top: inner.offsetTop,
|
||||
bottom: inner.offsetTop + inner.offsetHeight
|
||||
}
|
||||
|
||||
/* Initialize current height */
|
||||
this.height_ = 0
|
||||
@@ -55,45 +61,34 @@ class Position extends Abstract {
|
||||
* Update locked state and height
|
||||
*
|
||||
* @param {Event} ev - Event (omitted)
|
||||
* @return {void}
|
||||
*/
|
||||
update() {
|
||||
const bounds = this.inner_.getBoundingClientRect() // TODO: thresholds can be calculated as a function
|
||||
const parent = this.outer_.offsetTop
|
||||
|
||||
/* Determine top and bottom offsets */
|
||||
const top = bounds.top + window.pageYOffset,
|
||||
bottom = bounds.bottom + window.pageYOffset
|
||||
|
||||
/* Determine current y-offset at top and bottom of window */
|
||||
const upper = window.pageYOffset,
|
||||
lower = window.pageYOffset + window.innerHeight
|
||||
const scroll = window.pageYOffset
|
||||
const expand = window.innerHeight
|
||||
|
||||
/* Calculate new bounds */
|
||||
const offset = top - upper
|
||||
const height = window.innerHeight
|
||||
- Math.max(lower - bottom, 0)
|
||||
- Math.max(offset, parent)
|
||||
const offset = this.bounds_.top - scroll
|
||||
const height = expand
|
||||
- Math.max(0, scroll + expand - this.bounds_.bottom)
|
||||
- Math.max(offset, this.offset_)
|
||||
|
||||
/* If height changed, update element */
|
||||
if (height !== this.height_)
|
||||
this.el_.style.height = `${this.height_ = height}px`
|
||||
|
||||
/* Sidebar should be locked, as we're below parent offset */
|
||||
if (offset < parent) {
|
||||
if (offset < this.offset_) {
|
||||
if (!this.el_.dataset.mdLocked)
|
||||
this.el_.dataset.mdLocked = true
|
||||
this.el_.dataset.mdLocked = ""
|
||||
|
||||
/* Sidebar should be unlocked, if locked */
|
||||
} else if (this.el_.dataset.mdLocked) {
|
||||
} else if (typeof this.el_.dataset.mdLocked === "string") {
|
||||
delete this.el_.dataset.mdLocked
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset locked state and height
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
reset() {
|
||||
delete this.el_.dataset.mdLocked
|
||||
|
||||
@@ -21,71 +21,18 @@
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Navigation expander
|
||||
* Definition
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
class Expander {
|
||||
export default
|
||||
class Abstract {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Dispatch update on next repaint
|
||||
*
|
||||
* @constructor
|
||||
* @param {(string|HTMLElement)} el - Selector or HTML element
|
||||
*/
|
||||
constructor(el) {
|
||||
this.el_ = (typeof el === "string")
|
||||
? document.querySelector(el)
|
||||
: el
|
||||
|
||||
/* Event listener */
|
||||
this.handler_ = ev => {
|
||||
this.update(ev)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update state of expandable element
|
||||
*
|
||||
* @param {Event} ev - Event
|
||||
* @return {void}
|
||||
*/
|
||||
update() {}
|
||||
|
||||
/**
|
||||
* Reset state of expandable element
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
reset() {}
|
||||
|
||||
/**
|
||||
* Register listener for all relevant events
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
listen() {
|
||||
["click"].forEach(name => {
|
||||
window.addEventListener(name, this.handler_, false)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister listener for all relevant events
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
unlisten() {
|
||||
["click"].forEach(name => {
|
||||
window.removeEventListener(name, this.handler_, false)
|
||||
})
|
||||
|
||||
/* Perform reset */
|
||||
this.reset()
|
||||
}
|
||||
// constructor() {
|
||||
//
|
||||
// }
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Exports
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
export default Expander
|
||||
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Sidebar scroll-spy
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
export default
|
||||
class ScrollSpy {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @constructor
|
||||
* @param {(string|HTMLCollection)} els - Selector or HTML elements
|
||||
*/
|
||||
constructor(els) {
|
||||
this.els_ = (typeof els === "string")
|
||||
? document.querySelectorAll(els)
|
||||
: els
|
||||
|
||||
/* Initialize index for currently active element */
|
||||
this.index_ = 0
|
||||
this.offset_ = window.pageYOffset
|
||||
|
||||
/* Index anchor nodes for fast lookup */
|
||||
this.anchors_ = [].map.call(this.els_, el => {
|
||||
return document.querySelector(el.hash)
|
||||
})
|
||||
|
||||
/* Event listener */
|
||||
this.handler_ = ev => {
|
||||
this.update(ev)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update state of sidebar
|
||||
*
|
||||
* @param {Event} ev - Event (omitted)
|
||||
* @return {void}
|
||||
*/
|
||||
update() {
|
||||
|
||||
/* Scroll direction is down */
|
||||
if (this.offset_ <= window.pageYOffset) {
|
||||
for (let i = this.index_ + 1; i < this.els_.length; i++) {
|
||||
if (this.anchors_[i].offsetTop <= window.pageYOffset) {
|
||||
if (i > 0)
|
||||
this.els_[i - 1].dataset.mdMarked = true
|
||||
this.index_ = i
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
/* Scroll direction is up */
|
||||
} else {
|
||||
for (let i = this.index_; i >= 0; i--) {
|
||||
if (this.anchors_[i].offsetTop > window.pageYOffset) {
|
||||
if (i > 0)
|
||||
delete this.els_[i - 1].dataset.mdMarked
|
||||
} else {
|
||||
this.index_ = i
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Remember current offset for next cycle */
|
||||
this.offset_ = window.pageYOffset
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset state of sidebar
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
reset() {
|
||||
[].forEach.call(this.els_, el => {
|
||||
delete el.dataset.mdMarked
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Register listener for all relevant events
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
listen() {
|
||||
["scroll", "resize", "orientationchange"].forEach(name => {
|
||||
window.addEventListener(name, this.handler_, false)
|
||||
})
|
||||
|
||||
/* Initial update */
|
||||
this.update()
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister listener for all relevant events
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
unlisten() {
|
||||
["scroll", "resize", "orientationchange"].forEach(name => {
|
||||
window.removeEventListener(name, this.handler_, false)
|
||||
})
|
||||
|
||||
/* Perform reset */
|
||||
this.reset()
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Search
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
class Search {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @constructor
|
||||
* @param {(string|HTMLElement)} el - Selector or HTML element
|
||||
*/
|
||||
constructor(el) {
|
||||
this.el_ = (typeof el === "string") ? document.querySelector(el) : el
|
||||
|
||||
/* Event listener */
|
||||
this.handler_ = ev => {
|
||||
this.update(ev)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update state and height of sidebar
|
||||
*
|
||||
* @param {Event} ev - Event
|
||||
* @return {void}
|
||||
*/
|
||||
update() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset state and height of sidebar
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
reset() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Register listener for all relevant events
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
listen() {
|
||||
// ['scroll', 'resize', 'orientationchange'].forEach(name => {
|
||||
// window.addEventListener(name, this.handler_, false);
|
||||
// });
|
||||
|
||||
/* Initial update */
|
||||
this.update()
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister listener for all relevant events
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
unlisten() {
|
||||
// ['scroll', 'resize', 'orientationchange'].forEach(name => {
|
||||
// window.removeEventListener(name, this.handler_, false);
|
||||
// });
|
||||
|
||||
/* Perform reset */
|
||||
this.reset()
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Exports
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
export default Search
|
||||
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Sidebar sticky-scroll handler
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
class Sidebar {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @constructor
|
||||
* @param {(string|HTMLElement)} el - Selector or HTML element
|
||||
*/
|
||||
constructor(el) {
|
||||
this.el_ = (typeof el === "string")
|
||||
? document.querySelector(el)
|
||||
: el
|
||||
|
||||
/* Grab inner and outer container */
|
||||
this.inner_ = this.el_.parentNode
|
||||
this.outer_ = this.el_.parentNode.parentNode
|
||||
|
||||
/* Initialize parameters */
|
||||
this.height_ = 0
|
||||
this.locked_ = false
|
||||
|
||||
/* Event listener */
|
||||
this.handler_ = ev => {
|
||||
this.update(ev)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update state and height of sidebar
|
||||
*
|
||||
* @param {Event} ev - Event
|
||||
* @return {void}
|
||||
*/
|
||||
update() {
|
||||
const bounds = this.inner_.getBoundingClientRect()
|
||||
const parent = this.outer_.offsetTop
|
||||
|
||||
/* Determine top and bottom offsets */
|
||||
const top = bounds.top + window.pageYOffset,
|
||||
bottom = bounds.bottom + window.pageYOffset
|
||||
|
||||
/* Determine current y-offset at top and bottom of window */
|
||||
const upper = window.pageYOffset,
|
||||
lower = window.pageYOffset + window.innerHeight
|
||||
|
||||
/* Calculate new bounds */
|
||||
const offset = top - upper
|
||||
const height = window.innerHeight - Math.max(lower - bottom, 0)
|
||||
- Math.max(offset, parent)
|
||||
|
||||
/* If height changed, update element */
|
||||
if (height !== this.height_)
|
||||
this.el_.style.height = `${this.height_ = height}px`
|
||||
|
||||
/* Sidebar should be locked, as we're below parent offset */
|
||||
if (offset < parent) {
|
||||
if (!this.locked_) {
|
||||
this.el_.classList.add("md-js__sidebar--locked")
|
||||
this.locked_ = true
|
||||
}
|
||||
|
||||
/* Sidebar should be unlocked, if locked */
|
||||
} else if (this.locked_) {
|
||||
this.el_.classList.remove("md-js__sidebar--locked")
|
||||
this.locked_ = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset state and height of sidebar
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
reset() {
|
||||
this.el_.classList.remove("md-js__sidebar--locked")
|
||||
this.el_.style.height = ""
|
||||
|
||||
/* Reset parameters */
|
||||
this.height_ = 0
|
||||
this.locked_ = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Register listener for all relevant events
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
listen() {
|
||||
["scroll", "resize", "orientationchange"].forEach(name => {
|
||||
window.addEventListener(name, this.handler_, false)
|
||||
})
|
||||
|
||||
/* Initial update */
|
||||
this.update()
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister listener for all relevant events
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
unlisten() {
|
||||
["scroll", "resize", "orientationchange"].forEach(name => {
|
||||
window.removeEventListener(name, this.handler_, false)
|
||||
})
|
||||
|
||||
/* Perform reset */
|
||||
this.reset()
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Exports
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
export default Sidebar
|
||||
Reference in New Issue
Block a user