How can I add my own options in an existing widgets?

I have been checking out the widgets in the core, and I am specifically looking at this one:

$:/core/modules/widgets/scrollable.js

I am wondering how I can add my own options in this section:

/*
Handle a scroll event
*/
ScrollableWidget.prototype.handleScrollEvent = function(event) {
	// Pass the scroll event through if our offsetsize is larger than our scrollsize
	if(this.outerDomNode.scrollWidth <= this.outerDomNode.offsetWidth && this.outerDomNode.scrollHeight <= this.outerDomNode.offsetHeight && this.fallthrough === "yes") {
		return true;
	}
	var options = {};
	if($tw.utils.hop(event.paramObject,"animationDuration")) {
		options.animationDuration = event.paramObject.animationDuration;
	}
	if(event.paramObject && event.paramObject.selector) {
		this.scrollSelectorIntoView(null,event.paramObject.selector,null,options);
	} else {
		this.scrollIntoView(event.target,null,options);
	}
	return false; // Handled event
};

I attempted to add my own paramObject, and called “position”, it didn’t work. Ideally, my widget would look something like this:

<$button>
<$action-sendmessage $position="center" $message="tm-scroll" $name="selector" $value="#{{!!field-name}}"/>
</$button>

I tried entering my own if statement, which looked like this:

/*
Handle a scroll event
*/
ScrollableWidget.prototype.handleScrollEvent = function(event) {
	// Pass the scroll event through if our offsetsize is larger than our scrollsize
	if(this.outerDomNode.scrollWidth <= this.outerDomNode.offsetWidth && this.outerDomNode.scrollHeight <= this.outerDomNode.offsetHeight && this.fallthrough === "yes") {
		return true;
	}
	var options = {};
	if($tw.utils.hop(event.paramObject,"animationDuration")) {
		options.animationDuration = event.paramObject.animationDuration;
	}
    // My option
    if($tw.utils.hop(event.paramObject,"position")) {
        if (position == "center") {
		   this.scrollIntoView({behavior: 'auto', block: 'center'});
        }
        else { //other positions not really important right now. }
	}
	if(event.paramObject && event.paramObject.selector) {
		this.scrollSelectorIntoView(null,event.paramObject.selector,null,options);
        /*I even tried deleting this code and adding 
        this.scrollIntoView({behavior: 'auto', block: 'center'});
        here. */
	} else {
		this.scrollIntoView(event.target,null,options);
	}
	return false; // Handled event
};

No matter what I do, it doesn’t want to cooperate.

In general it is recommend to subclass core widgets and create new versions under new names with your own changes, see https://tiddlywiki.com/dev/#WidgetSubclassingMechanism

Your if statement likely needs to be:

if(event.paramObject.position === "center") instead of if (position == "center") {

If you want this to work in the story you need to patch scroller.js like so:

image

then you can call it with parameter WITHOUT a “$”

image

if(event.paramObject.position === "center") { when I use an alert("test"). But when I add this.scrollIntoView({behavior: 'auto', block: 'center'}); I get an “Internal JavaScript error”: Uncaught TypeError: Cannot read property 'defaultView' of undefined. I figure there must something in the core somewhere that causes this not to behave normally.

If memory serves me well the $tw.utils.hop method changes the value of this.

Replace if($tw.utils.hop(event.paramObject,"position")) with if(event.paramObject.position)

1 Like

That didn’t work either. However, I did come put with a half-baked solution that doesn’t involve JavaScript or modifying any of the code.

This is the CSS and HTML I used to solve my problem:

<style>
.tc-scrollable-demo2 {
  /* The idea was to scroll left and right */
  overflow-y: hidden;
  overflow-x: scroll;
}
.object {
   border: 1px solid red;
   /* Width set to 100%, this is the key because the widget will always insure the entire element is in view if possible. */
   width: 100%;
  /* Then align the contents to the center, this makes it appear that the target object is always in the center. */
   text-align: center;
   /* Have to make the hight 0 so that it does block access to other objects. */
   height: 0px;
   display: block;
}
/* The object I wanted to be centered into view */
.object span {
   border: 1px solid blue;
   width: 30px;
   display: inline-block;
   z-index: 1;
}
</style>
<$scrollable class="tc-scrollable-demo2">
<div id='objects1' class="object" style='position: absolute;  left: 2000px;'><span>Object 1</span></div>
<div id='objects2' class="object" style='position: absolute;  left: 2040px;'><span>Object 2</span></div>
<div></div>
<$button>
<$action-sendmessage $message="tm-scroll" $name="selector" $value="#objects1"/>
Back to Top
</$button>
<span>
<$button style='position: absolute; left: 4000px;'>
<$action-sendmessage $message="tm-scroll" $name="selector" $value="#objects1"/>
Back to Top
</$button>
</span>
</$scrollable>

This does exactly what I was trying to achieve. Thanks for the help anyway.

Now the next step is to execute this on tiddler render/open.

2 Likes