When we discussed Custom Widgets in chapter 6, we talked about how you can create new widgets. But you can also replace a built-in widget with a custom widget. This obviously must be done with caution, since it can have effects that reach deep into the core code of TiddlyWiki, but it's a powerful feature that can be a very convenient way to add custom behaviors. To achieve this, we simply define a new \widget
with the same name as the one we want to override (in this case, you don't need, and indeed can't have, a .
in the name of the custom widget, like you normally do).
As a silly example, suppose we want to completely inhibit internal linking. We can override the $link
widget to ignore all of its parameters and only render the link text in the body of the widget:
\widget $link()
<$slot $name="ts-raw">
\end
* <$link to="Links">Link</$link> to a tiddler
* [[Links]] are a fundamental part of a TiddlyWiki.
- Link to a tiddler
- Links are a fundamental part of a TiddlyWiki.
Referencing the original widget
In most practical scenarios, we're not trying to replace an existing widget entirely – rather, we want to wrap it in some other wikitext or change the values of its attributes in some systematic way. Therefore, we'll want to use some version of the original widget within the custom widget.
However, if we just directly include a $link
widget inside the definition of the $link
widget, TiddlyWiki will try to transclude the custom widget within itself over and over again, recognize that it's stuck in an infinite loop, and give up on rendering the widget. We instead need to use the $genesis
meta-widget, which creates another widget of an arbitrary type, to retrieve the original widget instead of our custom version.
As an example, here's something more useful we can do by overriding the $link
widget: if a tiddler has a description
field, add a tooltip showing the description
field on the link's tooltip.
\widget $link(to)
<$let tip={{{ [<to>get[description]] }}}>
<$genesis
$type="$link"
$remappable="no"
to=<<to>>
tooltip=<<tip>>
>
<$slot $name="ts-raw"/>
</$genesis>
</$let>
\end
[[Links]] and [[Tags]] combine to organize [[Tiddlers]] within a wiki.
The $type
parameter of the $genesis
widget is the name of the widget we're trying to render. The $remappable
parameter is the magic: setting this to no
forces the original, built-in widget definition to be used, rather than our custom definition. Lastly, all parameters that don't start with dollar signs are passed as parameters to the newly created widget.
Try hovering over the links above – you'll see the descriptions appear as tooltips!
Word to the wise: When overriding a built-in widget, always test your changes within a single tiddler and make sure they work as expected prior to tagging the new version with $:/tags/Global
. If you make a mistake and override a widget critical to TiddlyWiki's operation with a broken version, you may find yourself unable to get back into the editor to restore the default behavior! (If you do make such a mistake, you can manually edit the HTML file or tiddler file in a text editor to remove the faulty wikitext and restart TiddlyWiki – this will not cause permanent data loss, it'll just be annoying.)
Exercises
Having overridden the definition of the $link
widget above, if we explicitly supply a tooltip
attribute on some $link
widget, it's now silently replaced with the description one we added. Change the behavior so that the description tooltip is only applied if one isn't already provided for the widget.
The $link
widget has many optional parameters besides to
and tooltip
: as of the current version, aria-label
, tabindex
, draggable
, tag
, class
, overrideClass
, and any number of CSS data attributes and properties. Right now, we are ignoring these, possibly causing undesired effects. For instance, the following link should be rendered like an external link, but fails to do so:
<$link to="Tiddlers" class="tc-tiddlylink-external">
Tiddlers (fake external)
</$link>
To prevent such lost parameters, we need to pass all the parameters we aren't explicitly changing through to the original $link
widget. We can combine two features to achieve this: (1) the $names
and $values
parameters of $genesis
, which let you set an arbitrary number of parameters by providing two filters that evaluate to a list of parameter names and a list of corresponding values, and (2) the $params
parameter to a widget (or function or procedure), which provides a JSON-formatted collection of all the parameters passed to the widget, even those that weren't specified in the parameters list.
You'll need to know several new things to complete this task:
(1) The $parameters
widget, which is a more verbose way of specifying the parameters of something to be transcluded (including a widget), is needed to use the $params
attribute – we can't specify $params
directly in the (parameter list)
of the \widget
pragma. The value of the $params
attribute is the name of a variable into which the parameters are placed. Here's the syntax:
\widget $link(to, tooltip)
<$parameters $params="params-var">
...here <<params-var>> is JSON containing all the params, and <<to>> and <<tooltip>> contain the values of those specific parameters
</$parameters>
\end
(2) To get the base lists of parameter names and values, use the following functions:
\function .rest-names() [<params-var>jsonindexes[]]
\function .rest-values() [.rest-names[]] :map[<params-var>jsonget<currentTiddler>]
(3) When you call the $genesis
widget, you're supposed to be able to override any values in the $names
and $values
lists by additionally supplying that parameter (so that we could add a new tooltip
parameter here, and that one would be used), but due to a bug in TiddlyWiki, this doesn't currently work. Instead, override parameters directly within the $names
and $values
lists by using new filter runs at the end with the =
prefix, something like this:
$names="[[...blablabla]] =[[tooltip]]"
$values="[[...blablabla]] =[[tooltipvalue]]"
go to answer
Why did we need to use the =
(also called :all
) filter prefix in the previous exercise?