refactor: use history mechanism for block level navigation

This commit is contained in:
linonetwo 2023-09-22 23:14:22 +08:00
parent dfa060024e
commit 436343ce1b
5 changed files with 42 additions and 10 deletions

View File

@ -90,12 +90,12 @@ Story.prototype.saveStoryList = function(storyList) {
));
};
Story.prototype.addToHistory = function(navigateTo,navigateFromClientRect) {
Story.prototype.addToHistory = function(navigateTo,fromPageRect,anchor) {
var titles = $tw.utils.isArray(navigateTo) ? navigateTo : [navigateTo];
// Add a new record to the top of the history stack
var historyList = this.wiki.getTiddlerData(this.historyTitle,[]);
$tw.utils.each(titles,function(title) {
historyList.push({title: title, fromPageRect: navigateFromClientRect});
historyList.push({title: title, fromPageRect: fromPageRect, anchor: anchor});
});
this.wiki.setTiddlerData(this.historyTitle,historyList,{"current-tiddler": titles[titles.length-1]});
};

View File

@ -26,13 +26,26 @@ ClassicStoryView.prototype.navigateTo = function(historyInfo) {
}
var listItemWidget = this.listWidget.children[listElementIndex],
targetElement = listItemWidget.findFirstDomNode();
// If anchor is provided, find the element the anchor pointing to
var foundAnchor = false;
if(targetElement && historyInfo.anchor) {
var anchorElement = targetElement.querySelector("[data-anchor-id='" + historyInfo.anchor + "']");
if(anchorElement) {
targetElement = anchorElement.parentNode;
var isBefore = anchorElement.dataset.anchorPreviousSibling === "true";
if(isBefore) {
targetElement = targetElement.previousSibling;
}
foundAnchor = true;
}
}
// Abandon if the list entry isn't a DOM element (it might be a text node)
if(!targetElement || targetElement.nodeType === Node.TEXT_NODE) {
return;
}
if(duration) {
// Scroll the node into view
this.listWidget.dispatchEvent({type: "tm-scroll", target: targetElement});
this.listWidget.dispatchEvent({type: "tm-scroll", target: targetElement, highlight: foundAnchor});
} else {
targetElement.scrollIntoView();
}

View File

@ -49,7 +49,9 @@ Handle an event
*/
PageScroller.prototype.handleEvent = function(event) {
if(event.type === "tm-scroll") {
var options = {};
var options = {
highlight: event.highlight,
};
if($tw.utils.hop(event.paramObject,"animationDuration")) {
options.animationDuration = event.paramObject.animationDuration;
}
@ -65,14 +67,21 @@ PageScroller.prototype.handleEvent = function(event) {
/*
Handle a scroll event hitting the page document
options:
- animationDuration: total time of scroll animation
- highlight: highlight the element after scrolling, to make it evident. Usually to focus an anchor in the middle of the tiddler.
*/
PageScroller.prototype.scrollIntoView = function(element,callback,options) {
var self = this,
duration = $tw.utils.hop(options,"animationDuration") ? parseInt(options.animationDuration) : $tw.utils.getAnimationDuration(),
highlight = options.highlight || false,
srcWindow = element ? element.ownerDocument.defaultView : window;
// Now get ready to scroll the body
this.cancelScroll(srcWindow);
this.startTime = Date.now();
// toggle class to allow trigger the highlight animation
$tw.utils.removeClass(element,"tc-focus-highlight");
// Get the height of any position:fixed toolbars
var toolbar = srcWindow.document.querySelector(".tc-adjust-top-of-scroll"),
offset = 0;
@ -121,6 +130,15 @@ PageScroller.prototype.scrollIntoView = function(element,callback,options) {
srcWindow.scrollTo(scrollPosition.x + (endX - scrollPosition.x) * t,scrollPosition.y + (endY - scrollPosition.y) * t);
if(t < 1) {
self.idRequestFrame = self.requestAnimationFrame.call(srcWindow,drawFrame);
} else {
// the animation is end.
if(highlight) {
element.focus({ focusVisible: true });
// Using setTimeout to ensure the removal takes effect before adding the class again.
setTimeout(function() {
$tw.utils.addClass(element,"tc-focus-highlight");
}, 50);
}
}
};
drawFrame();

View File

@ -23,8 +23,9 @@ AnchorWidget.prototype.render = function(parent,nextSibling) {
this.idNode = this.document.createElement("span");
this.idNode.setAttribute("data-anchor-id",this.id);
this.idNode.setAttribute("data-anchor-title",this.tiddlerTitle);
if(this.before) {
this.idNode.setAttribute("data-before","true");
// if the actual block is before this node, we need to add a flag to the node
if(this.previousSibling) {
this.idNode.setAttribute("data-anchor-previous-sibling","true");
}
this.idNode.className = "tc-anchor";
parent.insertBefore(this.idNode,nextSibling);

View File

@ -138,9 +138,10 @@ NavigatorWidget.prototype.addToStory = function(title,fromTitle) {
Add a new record to the top of the history stack
title: a title string or an array of title strings
fromPageRect: page coordinates of the origin of the navigation
anchor:optional anchor id in this tiddler
*/
NavigatorWidget.prototype.addToHistory = function(title,fromPageRect) {
this.story.addToHistory(title,fromPageRect,this.historyTitle);
NavigatorWidget.prototype.addToHistory = function(title,fromPageRect,anchor) {
this.story.addToHistory(title,fromPageRect,anchor);
};
/*
@ -150,9 +151,8 @@ NavigatorWidget.prototype.handleNavigateEvent = function(event) {
event = $tw.hooks.invokeHook("th-navigating",event);
if(event.navigateTo) {
this.addToStory(event.navigateTo,event.navigateFromTitle);
event = $tw.hooks.invokeHook("th-navigating-add-history",event);
if(!event.navigateSuppressNavigation) {
this.addToHistory(event.navigateTo,event.navigateFromClientRect);
this.addToHistory(event.navigateTo,event.navigateFromClientRect,event.toAnchor);
}
}
$tw.hooks.invokeHook("th-navigated",event);