Skip to content

Commit 19af1c0

Browse files
committed
Highlight visible part of the content in the page outline panel
1 parent f4c886e commit 19af1c0

File tree

2 files changed

+61
-25
lines changed

2 files changed

+61
-25
lines changed

templates/html/navtree.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,10 @@ ul.page-outline li {
317317
white-space: nowrap;
318318
}
319319

320+
ul.page-outline li.vis {
321+
background-color: var(--nav-breadcrumb-active-bg);
322+
}
323+
320324
#container.resizing {
321325
cursor: col-resize;
322326
user-select: none;

templates/html/navtree.js

Lines changed: 57 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,10 @@ function initNavTree(toroot,relpath,allMembersFile) {
485485

486486
$(window).resize(function() { adjustSyncIconPosition(); });
487487

488+
let navtree_trampoline = {
489+
updateContentTop : function(c) {}
490+
}
491+
488492
function initResizable() {
489493
let sidenav,mainnav,pagenav,container,navtree,content,header,footer,barWidth=6;
490494
const RESIZE_COOKIE_NAME = ''+'width';
@@ -642,22 +646,22 @@ function initNavTree(toroot,relpath,allMembersFile) {
642646
const newWidth = $(this).width(), newHeight = $(this).height();
643647
if (newWidth!=lastWidth || newHeight!=lastHeight) {
644648
resizeHeight();
649+
navtree_trampoline.updateContentTop(content);
645650
lastWidth = newWidth;
646651
lastHeight = newHeight;
647652
}
648653
});
649654
resizeHeight();
650655
$(window).resize(function() { resizeHeight(); });
651656
content.scroll(function() {
652-
if (typeof navtree_trampoline!=='undefined') {
653-
navtree_trampoline.updateScroll(content.scrollTop());
654-
}
657+
navtree_trampoline.updateContentTop(content);
655658
});
656659
});
657660
}
658661

659662

660663
function initPageToc() {
664+
const topMapping = [];
661665
const toc_contents = $('#page-nav-contents');
662666
const content=$('<ul>').addClass('page-outline');
663667

@@ -700,14 +704,15 @@ function initNavTree(toroot,relpath,allMembersFile) {
700704
function hasSubItems() {
701705
return item.memTitles.length>0 || rows.toArray().some(function(el) { return $(el).is(':visible'); });
702706
}
703-
const li = $('<li>');
707+
const li = $('<li>').attr('id','nav-'+id);
704708
const div = $('<div>').addClass('item');
705-
const span = $('<span>').addClass('<arrow>').css({ paddingLeft:'0' });
709+
const span = $('<span>').addClass('arrow').css({ paddingLeft:'0' });
706710
if (hasSubItems()) {
707-
span.append($('<span>').addClass('arrowHead opened'));
711+
span.append($('<span>').addClass('arrowhead opened'));
708712
}
709713
const ahref = $('<a>').attr('href','#'+id).append(title);
710714
content.append(li.append(div.append(span).append(ahref)));
715+
topMapping.push(id);
711716
const ulStack = [];
712717
ulStack.push(content);
713718
if (hasSubItems()) {
@@ -729,11 +734,12 @@ function initNavTree(toroot,relpath,allMembersFile) {
729734
ulStack.pop();
730735
inMemberGroup=false;
731736
}
732-
const li2 = $('<li>');
737+
const li2 = $('<li>').attr('id','nav-'+id);
733738
const div2 = $('<div>').addClass('item');
734739
const span2 = $('<span>').addClass('arrow').css({ paddingLeft:parseInt(ulStack.length*16)+'px' });
735740
const ahref = $('<a>').attr('href','#'+id).append(escapeHtml(text));
736741
li2.append(div2.append(span2).append(ahref));
742+
topMapping.push(id);
737743
if (isMemberGroupHeader) {
738744
span2.append($('<span>').addClass('arrowhead opened'));
739745
ulStack[ulStack.length-1].append(li2);
@@ -753,11 +759,12 @@ function initNavTree(toroot,relpath,allMembersFile) {
753759
const name = text.replace(/\(\)(\s*\[\d+\/\d+\])?$/, '') // func() [2/8] -> func
754760
id = $(data).find('span.permalink a').attr('href')
755761
if (name!=undefined) {
756-
const li2 = $('<li>');
762+
const li2 = $('<li>').attr('id','nav-'+id.substring(1));
757763
const div2 = $('<div>').addClass('item');
758764
const span2 = $('<span>').addClass('arrow').css({paddingLeft:parseInt(ulStack.length*16)+'px'});
759765
const ahref = $('<a>').attr('href',id).append(escapeHtml(name));
760766
ulStack[ulStack.length-1].append(li2.append(div2.append(span2).append(ahref)));
767+
topMapping.push(id.substring(1));
761768
}
762769
});
763770
}
@@ -773,9 +780,9 @@ function initNavTree(toroot,relpath,allMembersFile) {
773780
const pageName = url.split('/').pop().split('#')[0].replace(/(\.[^/.]+)$/, '-members$1');
774781
const li = $('<li>');
775782
const div = $('<div>').addClass('item');
776-
const span = $('<span>').addClass('<arrow>').css({ paddingLeft:'0' });
783+
const span = $('<span>').addClass('arrow').css({ paddingLeft:'0' });
777784
const ahref = $('<a>').attr('href',srcBaseUrl+dstBaseUrl+pageName).addClass('noscroll');
778-
content.append(li.append(div.append(span.append(ahref.append(LISTOFALLMEMBERS)))));
785+
content.append(li.append(div.append(span).append(ahref.append(LISTOFALLMEMBERS))));
779786
}
780787

781788
if (groupSections.length==0) {
@@ -789,18 +796,18 @@ function initNavTree(toroot,relpath,allMembersFile) {
789796
(sectionStack.length ? sectionStack[sectionStack.length - 1].children : sectionTree).push(node);
790797
sectionStack.push({ ...node, level });
791798
});
792-
if (sectionTree.length>0)
793-
{
799+
if (sectionTree.length>0) {
794800
function render(nodes, level=0) {
795801
return $('<ul>').addClass('nested').append(nodes.map(n => {
796-
const li = $('<li>');
802+
const li = $('<li>').attr('id','nav-'+n.id);
797803
const div = $('<div>').addClass('item');
798804
const span = $('<span>').addClass('arrow').attr('style','padding-left:'+parseInt(level*16)+'px;');
799805
if (n.children.length > 0) { span.append($('<span>').addClass('arrowhead opened')); }
800806
const url = $('<a>').attr('href','#'+n.id);
801807
url.append(escapeHtml(n.text))
802808
div.append(span).append(url)
803809
li.append(div,render(n.children,level+1));
810+
topMapping.push(n.id);
804811
return li;
805812
}));
806813
}
@@ -810,24 +817,49 @@ function initNavTree(toroot,relpath,allMembersFile) {
810817

811818
toc_contents.append(content);
812819

813-
$('.page-outline .arrow').on('click', function() {
814-
var nestedList = $(this).parent().siblings('.nested');
815-
var arrowhead = $(this).find('.arrowhead');
816-
if (nestedList.is(':visible')) {
817-
nestedList.slideUp('fast');
818-
arrowhead.removeClass('opened');
819-
} else {
820-
nestedList.slideDown('fast');
821-
arrowhead.addClass('opened');
822-
}
823-
});
824-
825820
$(".page-outline a[href]:not(.noscroll)").click(function(e) {
826821
e.preventDefault();
827822
const aname = $(this).attr("href");
828823
gotoAnchor($(aname),aname);
829824
});
830825

826+
navtree_trampoline.updateContentTop = function(c) {
827+
const pagenavcontents = $("#page-nav-contents");
828+
if (pagenavcontents.length) {
829+
const height = $('#doc-content').height();
830+
const navy = pagenavcontents.offset().top;
831+
const yc = $(c).offset().top;
832+
let offsets = []
833+
for (let i=0;i<topMapping.length;i++) {
834+
offsets.push({id:topMapping[i],y:$('#'+topMapping[i]).offset().top-yc});
835+
}
836+
offsets.push({id:'',y:1000000});
837+
let scrollTarget = undefined, minNavY=100000, maxNavY=0;
838+
for (let i=0;i<topMapping.length;i++) {
839+
const ys = offsets[i].y;
840+
const ye = offsets[i+1].y;
841+
const id = offsets[i].id;
842+
const nav = $('#nav-'+id);
843+
if ((ys>2 || ye>2) && (ys<height-2 || ye<height-2)) {
844+
if (!scrollTarget) scrollTarget=nav;
845+
nav.addClass('vis');
846+
const ny = nav.offset().top;
847+
minNavY = Math.min(minNavY,ny);
848+
maxNavY = Math.max(maxNavY,ny);
849+
} else {
850+
nav.removeClass('vis');
851+
}
852+
}
853+
if (scrollTarget) {
854+
const my = (maxNavY-minNavY)/2;
855+
pagenavcontents.scrollTo(scrollTarget,{offset:-height/2+my});
856+
}
857+
}
858+
}
859+
// TODO: find out how to avoid a timeout
860+
setTimeout(() => {
861+
navtree_trampoline.updateContentTop(content);
862+
},200);
831863
}
832864
$(document).ready(function() { initPageToc(); initResizable(); });
833865

0 commit comments

Comments
 (0)