This is a stripped-down version of a single section of Grok TiddlyWiki, optimized for fast loading and readability by search engines. Some features are missing.

For the full Grok TiddlyWiki experience, please visit the wiki version of this page.

Overriding Built-In Widgets

 16th October 2024 at 7:47pm

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.

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

Exercise: (m) [Ex:LinkTooltipNonReplace]

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.

go to answer

Exercise: (m) [Ex:SaveOptionalParameters]

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

Exercise: (s) [Ex:EqualsPrefixNeeded]

Why did we need to use the = (also called :all) filter prefix in the previous exercise?

go to answer

Takeaways

Takeaways are not available in the static version of Grok TiddlyWiki. Visit the wiki version of this page to study takeaways.

↑ 7: Looking Under the Hood