TiddlyWiki5
TiddlyWiki5 copied to clipboard
[IDEA] Provide another generic slot widget
Is your feature request related to a problem? Please describe.
When writing custom widgets we can use <$slot $name="ts-raw"/>
to access the content of the widget.
We can also use fill widgets to to divide the content into multiple slots
However as soon as we use one or more fill widgets it is no longer possible to retrieve the content of the widgets body outside fill widgets, without <$slot $name="ts-raw"/>
retrieving all the contents including all fill widgets.
- To make use of the body not in a fill widget would need some kind of parsing/
Describe the solution you'd like
An additional hard coded slot name for example <$slot $name="ts-body"/>
that returns the content of the widget without any fill widgets included. Simply stripped from the content.
Describe alternatives you've considered The best/only alternative I have is the solution I propose.
Additional context It is nice we can write custom widgets that respond to the content of the widget. And it is great we can subsequently extend it by dividing the body using the fill/slot mechanism. I would like however not be forced to use a fill widget every time, just so I can access the content with or without additional fill widgets at another time, it allows simpler widgets to be written that are also extensible.
Thanks @AnthonyMuscio could you give a wikitext example or two that illustrate what you'd like to see?
Here is an example of a widget that initially just displays the content of the widget as it is found.
- It is for illustrative purposes only, there are other ways to achieve the same result.
\widget $my.widget(label:"")
<fieldset>
<legend> <$text text=<<label>>/> </legend>
<$slot $name="ts-raw"/>
</fieldset>
\end $my.widget
<$my.widget label="importiant">
content of widget
</$my.widget>
With a result like this;
Now I would like to introduce an optional fill widget to display a header; However on this occasion I do not want to use this feature;
\widget $my.widget(label:"")
<$slot $name="header"/>
<fieldset>
<legend> <$text text=<<label>>/> </legend>
<$slot $name="ts-raw"/>
</fieldset>
\end $my.widget
<$my.widget label="important">
content of widget
</$my.widget>
- This result is identical to the last above
But now I decide to use the header;
<$my.widget label="important">
<$fill $name=header>My widget header</$fill>
content of widget
</$my.widget>
The result I get is;
The red highlight shows how when using <$slot $name="ts-raw"/>
it pulls in the whole body including the fill widgets therein.
The idea would be to introduce another generic slot widget such as <$slot $name="ts-body"/>
that would represent any content not inside a fill widget, ie strip all fill widgets from <$slot $name="ts-raw"/>
.
With this new feature I could write;
\widget $my.widget(label:"")
<$slot $name="header"/>
<fieldset>
<legend> <$text text=<<label>>/> </legend>
<$slot $name="ts-body"/>
</fieldset>
\end $my.widget
- using the generic slot ts-body
So now using the same widget call I did before but providing my header $fill
<$my.widget label="important">
<$fill $name=header>My widget header</$fill>
content of widget
</$my.widget>
I would get this instead;
- Any and all fill widgets will not be included in
<$slot $name="ts-body"/>
just what remains (if anything)
Finaly;
- It would be nice if either or both the fill and slot widgets permited the setting of mode inline or block, this could save asking users to provide the empty line, in the widget content to force block mode.
- This would allow the inside the widget to define the output as block or inline, by each fill/slot, but allow a fill widget to provide it.
I have already written a number of custom widgets with multiple optional fill widgets that work very well but I cannot use the simple widget content alone, or with the provision of any fill.
It looks like this is the same as the request I made in https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1145756052 and https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1147226905. Glad to have it pulled out as its own issue; it will make it much easier to keep track of this request. (The comments on #6666 got so long that GitHub folded most of them away, and you have to click on the "Load more" link half a dozen times to see them all).
Here's a basic implementation that works, but is highly inefficient. If I can figure out how to make the "ts-body" slot only be calculated on demand, then I'll submit a PR.
diff --git a/core/modules/widgets/transclude.js b/core/modules/widgets/transclude.js
index d30ab1fa7..0b20fa13d 100755
--- a/core/modules/widgets/transclude.js
+++ b/core/modules/widgets/transclude.js
@@ -155,6 +155,7 @@ TranscludeWidget.prototype.collectSlotFillParameters = function() {
this.slotFillParseTrees["ts-raw"] = this.parseTreeNode.children;
var noFillWidgetsFound = true,
searchParseTreeNodes = function(nodes) {
+ var withoutFill = [];
$tw.utils.each(nodes,function(node) {
if(node.type === "fill") {
if(node.attributes["$name"] && node.attributes["$name"].type === "string") {
@@ -163,11 +164,19 @@ TranscludeWidget.prototype.collectSlotFillParameters = function() {
}
noFillWidgetsFound = false;
} else {
- searchParseTreeNodes(node.children);
+ var childrenWithoutFill = searchParseTreeNodes(node.children);
+ if(node.children && node.children.length === childrenWithoutFill.length) {
+ withoutFill.push(node);
+ } else {
+ var nodeCopy = $tw.utils.extend({}, node);
+ nodeCopy.children = childrenWithoutFill;
+ withoutFill.push(nodeCopy);
+ }
}
});
+ return withoutFill;
};
- searchParseTreeNodes(this.parseTreeNode.children);
+ this.slotFillParseTrees["ts-body"] = searchParseTreeNodes(this.parseTreeNode.children);
if(noFillWidgetsFound) {
this.slotFillParseTrees["ts-missing"] = this.parseTreeNode.children;
}
The problem with this is that that withoutFill
parse tree, a copy of the main parse tree, is done every time, whether or not it's needed. That's quite a lot of arrays that are sitting around unused taking up memory, and needing to be garbage-collected later. It would be better to do that work only when the ts-body
slot is requested. However, I've tested this and it works. Do the following:
-
git clone https://github.com/Jermolene/TiddlyWiki5
- Save the patch above into a file
-
git apply filename.patch
-
node ./tiddlywiki.js editions/tw5.com --listen
- Create a tiddler containing the following copy of @AnthonyMuscio's example:
\widget $my.widget(label:"")
<$slot $name="header"/>
<fieldset>
<legend> <$text text=<<label>>/> </legend>
<$slot $name="ts-raw"/>
</fieldset>
\end $my.widget
<$my.widget label="important">
<$fill $name=header>My widget header</$fill>
content of widget
</$my.widget>
- Replace "ts-raw" in that example with "ts-body" and notice the duplicated header disappear.
An alternative way to address this might be to change the behaviour of the <$fill>
widget so that it doesn't render its content when encountered directly in wikitext (just as we did for the <$list-template>
etc widgets). It wouldn't be fully backwards compatible, but there is a strong argument that it is more useful than the current behaviour.
Thanks for the contributions so far, I don't have the technical skills to implement this but If I can do anything to help please ask.
change the behaviour of the
<$fill>
widget so that it doesn't render its content when encountered directly in wikitext
I would go with @Jermolene's solution. There is no reason anyone would ever use ts-raw and want the fill widgets included. No need to introduce additional hard-coded slots.
I would go with @Jermolene's solution. There is no reason anyone would ever use ts-raw and want the fill widgets included. No need to introduce additional hard-coded slots.
This is making an assumption that may inhibit innovation in the future. There may be value in being able to interrogate the body and parse the content, including to identify fill widgets within. If we let <$slot $name="ts-raw"/>
just remain and provide another logical slot <$slot $name="ts-body"/>
it remains available and totally backward compatible.
- I concede not many people would have used this yet, but more important than backward compatibility here is future innovation.
- I do not know the technical issues behind this but it seems it could be simple. Perhaps implementing @Jermolene suggestion then redefining ts-raw to access the raw content (as it does now), with ts-body not rendering the content.
We certainly need to improve documentation on <$slot $name="ts-raw"/>
but I am not proceeding until this issue is delt with.
If people wanted to parse and interrogate the body of a custom widget, having the $fill widgets not visibly render would not prevent them from being available to something scraping text.
Seems to me like it would be simpler just having the $fill widgets not output their contents than to go through a custom widget and cut out the parts that shouldn't be rendered.
I know what @Jermolene is suggesting, and what I'm seconding, isn't strictly backward compatible, but I contend that nobody between the heaven and earth is currently using ts-raw along with other custom slots. It totally guarantees the duplication of your fill, and I cannot conceive of a reason why anyone would have used that.
Actually, before I saw your post, I didn't think anyone was using custom slot widgets at all. I was discussing the issue over here. I believe they are super clunky as is, and I haven't found a problem where they provide a more elegant solution than something else like macros.
I didn't think anyone was using custom slot widgets at all. ... and I haven't found a problem where they provide a more elegant solution than something else like macros.
It is actually only a very new feature and it demands a focused effort to learn how to use. I have a list.report widget that allows for both default report elements like headers and footers which can be overridden with a custom fill widget, I believe if this issue is addressed it will only get more useful.
- Yes, as that discussion addresses less clunky would be nice.
I would like this issue to be progressed. It is stopping a whole class of possible custom widgets just because the ts-raw also contains the various fill widgets. Whilst I do like the idea of a separate ts-body that is only the body without any additional fill widgets I have also realised the following;
It is hard to make use of the ts-raw when it is only available as a slot widget.
So I had this thought could ts-raw, ts-body and any fill widget $name just be made available as a variable of that name containing the relevant content?
- This will then allow us to pass the content of the body or any slot as a parameter to any subsequent widget/procedure etc...
What do you think @Jermolene and @flibbles?
We still need to make the ts-body.
- It is possible to write custom widgets, driven by parameters;
- It is possible to write custom widgets driven by custom widget body fill and slots
As far as I can see It is not possible to write a custom widget that operates in the first way, then if desired the second way.
- If slots were available as variables this would be easy.
- If it made it easier perhaps the $parameters widget could be used to map a slot to a parameter/variable.