I’ve written a widget for dropdowns in the style of toc-expandable
, and it works for the most part, but when it updates, any elements rendered to the same parent that are after my widget get pushed before it.
I’m not sure what causes this, but I have to assume I’m doing something wrong with refreshing, but I can’t work it out. The issue doesn’t manifest if I wrap the reveals in their own span or div, but I’m concerned I’m doing something wrong and maybe shouldn’t just hack around it.
What’s causing this?
The full text of my widget:
/*\
title: $:/plugins/tavi-vi/collapse/collapse.js
type: application/javascript
module-type: widget
Collapse widget
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var Widget = require("$:/core/modules/widgets/widget.js").widget;
var CollapseWidget = function(parseTreeNode,options) {
this.initialise(parseTreeNode,options);
};
/*
Inherit from the base widget class
*/
CollapseWidget.prototype = new Widget();
/*
Render this widget into the DOM
*/
CollapseWidget.prototype.render = function(parent,nextSibling) {
this.computeAttributes();
this.execute();
this.renderChildren(parent,nextSibling);
};
/*
Compute the internal state of the widget
*/
CollapseWidget.prototype.execute = function() {
var addAttributes = function(node, attributes) {
for(const key in attributes) {
$tw.utils.addAttributeToParseTreeNode(node, key, attributes[key]);
}
}
this.collapseSpoiler = this.getAttribute("spoiler");
this.collapseState = this.getAttribute("state");
var buttonOpen = {
type: "reveal",
tag: "$reveal",
children: [
{
type: "button",
tag: "$button",
children: [
{
type: "transclude",
tag: "$transclude",
},
{
type: "element",
tag: "span",
children: [
{ type: "text", text: this.collapseSpoiler }
]
}
]
}
]
};
var buttonClose = JSON.parse(JSON.stringify(buttonOpen));
/* Open attributes */
addAttributes(buttonOpen, {
stateTitle: this.collapseState,
text: "open",
type: "nomatch"
});
addAttributes(buttonOpen.children[0], {
setTitle: this.collapseState,
"class": "tc-btn-invisible",
setTo: "open"
});
addAttributes(buttonOpen.children[0].children[0], {
tiddler: "$:/core/images/right-arrow"
});
/* Close attributes */
addAttributes(buttonClose, {
stateTitle: this.collapseState,
text: "open",
type: "match"
});
addAttributes(buttonClose.children[0], {
setTitle: this.collapseState,
"class": "tc-btn-invisible",
setTo: "close"
});
addAttributes(buttonClose.children[0].children[0], {
tiddler: "$:/core/images/down-arrow"
});
/* Content */
var content = {
type: "reveal",
tag: "$reveal",
children: [
{
type: "element",
tag: "div",
children: this.parseTreeNode.children
}
]
};
addAttributes(content, {
stateTitle: this.collapseState,
text: "open",
type: "match"
});
this.makeChildWidgets([ buttonOpen, buttonClose, content ]);
}
/*
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
*/
CollapseWidget.prototype.refresh = function(changedTiddlers) {
this.computeAttributes();
return this.refreshChildren(changedTiddlers);
};
exports.collapse = CollapseWidget;
})();
An example of an invocation that exhibits the behavior (toggle the dropdown a few times and AFTER appears before the dropdown):
<$collapse spoiler="._." state="$:/state/tavi-vi/collapse-journal">
:o
</$collapse>
AFTER