diff --git a/core/modules/widgets/scrollable.js b/core/modules/widgets/scrollable.js index 15b61e0c8..c95820ef3 100644 --- a/core/modules/widgets/scrollable.js +++ b/core/modules/widgets/scrollable.js @@ -171,6 +171,42 @@ ScrollableWidget.prototype.render = function(parent,nextSibling) { parent.insertBefore(this.outerDomNode,nextSibling); this.renderChildren(this.innerDomNode,null); this.domNodes.push(this.outerDomNode); + // If the scroll position is bound to a tiddler + if(this.scrollableBind) { + // After a delay for rendering, scroll to the bound position + setTimeout(this.updateScrollPositionFromBoundTiddler.bind(this),50); + // Save scroll position on DOM scroll event + this.outerDomNode.addEventListener("scroll",function(event) { + var existingTiddler = self.wiki.getTiddler(self.scrollableBind), + newTiddlerFields = { + title: self.scrollableBind, + "scroll-left": self.outerDomNode.scrollLeft.toString(), + "scroll-top": self.outerDomNode.scrollTop.toString() + }; + if(!existingTiddler || (existingTiddler.fields["scroll-left"] !== newTiddlerFields["scroll-left"] || existingTiddler.fields["scroll-top"] !== newTiddlerFields["scroll-top"])) { + self.wiki.addTiddler(new $tw.Tiddler(existingTiddler,newTiddlerFields)); + } + }); + } +}; + +ScrollableWidget.prototype.updateScrollPositionFromBoundTiddler = function() { + var tiddler = this.wiki.getTiddler(this.scrollableBind); + if(tiddler) { + var scrollLeftTo = this.outerDomNode.scrollLeft; + if(parseFloat(tiddler.fields["scroll-left"]).toString() === tiddler.fields["scroll-left"]) { + scrollLeftTo = parseFloat(tiddler.fields["scroll-left"]); + } + var scrollTopTo = this.outerDomNode.scrollTop; + if(parseFloat(tiddler.fields["scroll-top"]).toString() === tiddler.fields["scroll-top"]) { + scrollTopTo = parseFloat(tiddler.fields["scroll-top"]); + } + this.outerDomNode.scrollTo({ + top: scrollTopTo, + left: scrollLeftTo, + behavior: "instant" + }) + } }; /* @@ -178,6 +214,7 @@ Compute the internal state of the widget */ ScrollableWidget.prototype.execute = function() { // Get attributes + this.scrollableBind = this.getAttribute("bind"); this.fallthrough = this.getAttribute("fallthrough","yes"); this["class"] = this.getAttribute("class"); // Make child widgets @@ -193,6 +230,9 @@ ScrollableWidget.prototype.refresh = function(changedTiddlers) { this.refreshSelf(); return true; } + if(changedAttributes.bind || changedTiddlers[this.getAttribute("bind")]) { + this.updateScrollPositionFromBoundTiddler(); + } return this.refreshChildren(changedTiddlers); }; diff --git a/editions/prerelease/tiddlers/system/temp-my-scroll-position.tid b/editions/prerelease/tiddlers/system/temp-my-scroll-position.tid new file mode 100644 index 000000000..c4a164070 --- /dev/null +++ b/editions/prerelease/tiddlers/system/temp-my-scroll-position.tid @@ -0,0 +1,3 @@ +title: $:/my-scroll-position +scroll-left: 0 +scroll-top: 100 diff --git a/editions/tw5.com/tiddlers/widgets/ScrollableWidget.tid b/editions/tw5.com/tiddlers/widgets/ScrollableWidget.tid index 6fda3a974..71aa778bd 100644 --- a/editions/tw5.com/tiddlers/widgets/ScrollableWidget.tid +++ b/editions/tw5.com/tiddlers/widgets/ScrollableWidget.tid @@ -1,6 +1,6 @@ caption: scrollable created: 20140324223413403 -modified: 20220620115347910 +modified: 20230731100903977 tags: Widgets title: ScrollableWidget type: text/vnd.tiddlywiki @@ -16,12 +16,15 @@ The content of the `<$scrollable>` widget is displayed within a pair of wrapper |!Attribute |!Description | |class |The CSS class(es) to be applied to the outer DIV | |fallthrough |See below | +|bind |<<.from-version "5.3.1">> Optional title of tiddler to which the scroll position should be bound | + +Binding the scroll position to a tiddler automatically copies the scroll coordinates into the `scroll-left` and `scroll-top` fields as scrolling occurs. Conversely, setting those field values will automatically cause the scrollable to scroll if it can. <$macrocall $name=".note" _="""If a scrollable widget can't handle the `tm-scroll` message because the inner DIV fits within the outer DIV, then by default the message falls through to the parent widget. Setting the ''fallthrough'' attribute to `no` prevents this behaviour."""/> ! Examples -This example requires the following CSS definitions from [[$:/_tw5.com-styles]]: +These examples require the following CSS definitions from [[$:/_tw5.com-styles]]: ``` .tc-scrollable-demo { @@ -33,6 +36,8 @@ This example requires the following CSS definitions from [[$:/_tw5.com-styles]]: } ``` +!! Simple Usage + This wiki text shows how to display a list within the scrollable widget: < @@ -46,3 +51,23 @@ This wiki text shows how to display a list within the scrollable widget: ">> +!! Binding scroll position to a tiddler + +[[Current scroll position|$:/my-scroll-position]]: {{$:/my-scroll-position!!scroll-left}}, {{$:/my-scroll-position!!scroll-top}} + +<$button> +<$action-setfield $tiddler="$:/my-scroll-position" scroll-left="100" scroll-top="100"/> +Set current scroll position to 100,100 + + +< +<$list filter='[tag[Reference]]'> + +<$view field='title'/>: <$list filter='[all[current]links[]sort[title]]' storyview='pop'> +<$link><$view field='title'/> + + + + +">> +