From 077763cb74824633d7da68702c7b5b51606e3123 Mon Sep 17 00:00:00 2001 From: Jeremy Thomas Date: Wed, 11 Apr 2018 01:14:29 +0100 Subject: [PATCH] Add scroll spy --- docs/_includes/elements/improve-page.html | 2 +- docs/_javascript/main.js | 96 +++++++++++++++++++++- docs/_sass/main.sass | 12 ++- docs/css/bulma-docs.css | 16 +++- docs/lib/main.js | 98 ++++++++++++++++++++++- 5 files changed, 209 insertions(+), 15 deletions(-) diff --git a/docs/_includes/elements/improve-page.html b/docs/_includes/elements/improve-page.html index 05b3c6fc7..f431d11c7 100644 --- a/docs/_includes/elements/improve-page.html +++ b/docs/_includes/elements/improve-page.html @@ -1,4 +1,4 @@ -
+

Made with Bulma diff --git a/docs/_javascript/main.js b/docs/_javascript/main.js index 7df0b12f7..b3392c8da 100644 --- a/docs/_javascript/main.js +++ b/docs/_javascript/main.js @@ -33,16 +33,31 @@ document.addEventListener('DOMContentLoaded', () => { const anchors_el = document.getElementById('anchors'); const anchor_links_el = getAll('.bd-anchor-link'); + let anchors_by_id = {}; + let anchors_order = []; + let anchor_nav_els = []; + if (anchors_el && anchor_links_el.length > 0) { const anchors_el_list = anchors_el.querySelector('.bd-anchors-list'); - anchor_links_el.forEach(el => { + anchor_links_el.forEach((el, index) => { const link_target = el.getAttribute('href'); const link_text = el.previousElementSibling.innerText; if (link_text != '') { const item_el = createAnchorLink(link_text, link_target); anchors_el_list.appendChild(item_el); + + const anchor_key = link_target.substring(1); // #target -> target + anchors_by_id[anchor_key] = { + id: anchor_key, + index, + target: link_target, + text: link_text, + nav_el: item_el, + }; + anchors_order.push(anchor_key); + anchor_nav_els.push(item_el); } }); @@ -272,18 +287,76 @@ document.addEventListener('DOMContentLoaded', () => { } }); - function whenScrolling(lastY, currentY) { + // Anchors highlight + + let past_anchors = []; + anchor_links_el.reverse(); + const trigger_offset = 24 ; // In pixels + const typo_el = document.getElementById('typo'); + + function whenScrolling() { if (anchors_ref_el) { const bounds = anchors_ref_el.getBoundingClientRect(); + const anchors_height = anchors_el.clientHeight; + const typo_bounds = typo_el.getBoundingClientRect(); + const typo_height = typo_el.clientHeight; - if (bounds.top < 1) { + if (bounds.top < 1 && typo_bounds.top - anchors_height + typo_height > 0) { anchors_el.classList.add('is-pinned'); } else { anchors_el.classList.remove('is-pinned'); } + + anchor_links_el.some(el => { + const bounds = el.getBoundingClientRect(); + const href = el.getAttribute('href'); + const key = href.substring(1); // #target -> target + + if (bounds.top < 1 + trigger_offset && past_anchors.indexOf(key) == -1) { + past_anchors.push(key); + highlightAnchor(); + return; + } else if (bounds.top > 0 + trigger_offset && past_anchors.indexOf(key) != -1) { + removeFromArray(past_anchors, key); + highlightAnchor(); + return; + } + }); + } + } + + function highlightAnchor() { + const future_anchors = anchors_order.diff(past_anchors); + let highest_index = -1; + let highest_anchor_key = ''; + + if (past_anchors.length > 0) { + past_anchors.forEach((key, index) => { + const anchor = anchors_by_id[key]; + anchor.nav_el.className = 'is-past'; + + // Keep track of the bottom most item + if (anchor.index > highest_index) { + highest_index = anchor.index; + highest_anchor_key = key; + } + }); + + if (highest_anchor_key in anchors_by_id) { + anchors_by_id[highest_anchor_key].nav_el.className = 'is-current'; + } + } + + if (future_anchors.length > 0) { + future_anchors.forEach((key, index) => { + const anchor = anchors_by_id[key]; + anchor.nav_el.className = ''; + }); } } + // Scroll + function upOrDown(lastY, currentY) { if (currentY >= lastY) { return goingDown(currentY); @@ -362,7 +435,7 @@ document.addEventListener('DOMContentLoaded', () => { if (!ticking) { window.requestAnimationFrame(function() { // upOrDown(lastY, currentY); - whenScrolling(lastY, currentY); + whenScrolling(); ticking = false; lastY = currentY; }); @@ -371,4 +444,19 @@ document.addEventListener('DOMContentLoaded', () => { ticking = true; }); + // Utils + + function removeFromArray(array, value) { + if (array.includes(value)) { + const value_index = array.indexOf(value); + array.splice(value_index, 1); + } + + return array; + } + + Array.prototype.diff = function(a) { + return this.filter(function(i) {return a.indexOf(i) < 0;}); + }; + }); diff --git a/docs/_sass/main.sass b/docs/_sass/main.sass index 53f2fb923..ac739fddb 100644 --- a/docs/_sass/main.sass +++ b/docs/_sass/main.sass @@ -82,6 +82,9 @@ li &:not(:last-child) margin-bottom: 0.5em + &.is-past + a + color: $grey-light &.is-current a color: $link @@ -132,9 +135,10 @@ .bd-anchors padding-top: calc(1.5rem - 1px) - &.is-pinned - position: fixed - top: 0 + +tablet + &.is-pinned + position: fixed + top: 0 .bd-anchors-title color: $grey-light @@ -147,6 +151,8 @@ li &:last-child margin-top: 1em + a + color: $text-strong +touch .bd-lead, diff --git a/docs/css/bulma-docs.css b/docs/css/bulma-docs.css index 975aec779..6804174eb 100644 --- a/docs/css/bulma-docs.css +++ b/docs/css/bulma-docs.css @@ -9612,6 +9612,10 @@ label.panel-block:hover { margin-bottom: 0.5em; } +.bd-category-list li.is-past a, .bd-anchors-list li.is-past a { + color: #b5b5b5; +} + .bd-category-list li.is-current a, .bd-anchors-list li.is-current a { color: #3273dc; } @@ -9676,9 +9680,11 @@ label.panel-block:hover { padding-top: calc(1.5rem - 1px); } -.bd-anchors.is-pinned { - position: fixed; - top: 0; +@media screen and (min-width: 769px), print { + .bd-anchors.is-pinned { + position: fixed; + top: 0; + } } .bd-anchors-title { @@ -9692,6 +9698,10 @@ label.panel-block:hover { margin-top: 1em; } +.bd-anchors-list a { + color: #363636; +} + @media screen and (max-width: 1087px) { .bd-lead, .bd-side { diff --git a/docs/lib/main.js b/docs/lib/main.js index 5ff21d05c..880f85517 100644 --- a/docs/lib/main.js +++ b/docs/lib/main.js @@ -35,16 +35,31 @@ document.addEventListener('DOMContentLoaded', function () { var anchors_el = document.getElementById('anchors'); var anchor_links_el = getAll('.bd-anchor-link'); + var anchors_by_id = {}; + var anchors_order = []; + var anchor_nav_els = []; + if (anchors_el && anchor_links_el.length > 0) { var anchors_el_list = anchors_el.querySelector('.bd-anchors-list'); - anchor_links_el.forEach(function (el) { + anchor_links_el.forEach(function (el, index) { var link_target = el.getAttribute('href'); var link_text = el.previousElementSibling.innerText; if (link_text != '') { var item_el = createAnchorLink(link_text, link_target); anchors_el_list.appendChild(item_el); + + var anchor_key = link_target.substring(1); // #target -> target + anchors_by_id[anchor_key] = { + id: anchor_key, + index: index, + target: link_target, + text: link_text, + nav_el: item_el + }; + anchors_order.push(anchor_key); + anchor_nav_els.push(item_el); } }); @@ -274,18 +289,76 @@ document.addEventListener('DOMContentLoaded', function () { } }); - function whenScrolling(lastY, currentY) { + // Anchors highlight + + var past_anchors = []; + anchor_links_el.reverse(); + var trigger_offset = 24; // In pixels + var typo_el = document.getElementById('typo'); + + function whenScrolling() { if (anchors_ref_el) { var bounds = anchors_ref_el.getBoundingClientRect(); + var anchors_height = anchors_el.clientHeight; + var typo_bounds = typo_el.getBoundingClientRect(); + var typo_height = typo_el.clientHeight; - if (bounds.top < 1) { + if (bounds.top < 1 && typo_bounds.top - anchors_height + typo_height > 0) { anchors_el.classList.add('is-pinned'); } else { anchors_el.classList.remove('is-pinned'); } + + anchor_links_el.some(function (el) { + var bounds = el.getBoundingClientRect(); + var href = el.getAttribute('href'); + var key = href.substring(1); // #target -> target + + if (bounds.top < 1 + trigger_offset && past_anchors.indexOf(key) == -1) { + past_anchors.push(key); + highlightAnchor(); + return; + } else if (bounds.top > 0 + trigger_offset && past_anchors.indexOf(key) != -1) { + removeFromArray(past_anchors, key); + highlightAnchor(); + return; + } + }); } } + function highlightAnchor() { + var future_anchors = anchors_order.diff(past_anchors); + var highest_index = -1; + var highest_anchor_key = ''; + + if (past_anchors.length > 0) { + past_anchors.forEach(function (key, index) { + var anchor = anchors_by_id[key]; + anchor.nav_el.className = 'is-past'; + + // Keep track of the bottom most item + if (anchor.index > highest_index) { + highest_index = anchor.index; + highest_anchor_key = key; + } + }); + + if (highest_anchor_key in anchors_by_id) { + anchors_by_id[highest_anchor_key].nav_el.className = 'is-current'; + } + } + + if (future_anchors.length > 0) { + future_anchors.forEach(function (key, index) { + var anchor = anchors_by_id[key]; + anchor.nav_el.className = ''; + }); + } + } + + // Scroll + function upOrDown(lastY, currentY) { if (currentY >= lastY) { return goingDown(currentY); @@ -362,7 +435,7 @@ document.addEventListener('DOMContentLoaded', function () { if (!ticking) { window.requestAnimationFrame(function () { // upOrDown(lastY, currentY); - whenScrolling(lastY, currentY); + whenScrolling(); ticking = false; lastY = currentY; }); @@ -370,4 +443,21 @@ document.addEventListener('DOMContentLoaded', function () { ticking = true; }); + + // Utils + + function removeFromArray(array, value) { + if (array.includes(value)) { + var value_index = array.indexOf(value); + array.splice(value_index, 1); + } + + return array; + } + + Array.prototype.diff = function (a) { + return this.filter(function (i) { + return a.indexOf(i) < 0; + }); + }; }); \ No newline at end of file -- 2.47.3