Problem converting one value to another for use within a macro

Hello everyone.

I’ve been mulling over some code for a couple of days now, and I can’t get it to work the way I want. I’ve tried solving it with some AI, and although their explanations seemed convincing, the code they provided didn’t work.

What I want is to set the value of the “estado” parameter to “abierto” instead of “open” when invoking the macro. However, for the macro to work properly, that “abierto” value must be internally changed to “open” for the macro to work properly. I’ve tried to solve this problem, among many other ways, with this code, but it simply doesn’t work: I’ve tried solving it, among many other variants, with this code, but it just doesn’t work:

\define advertencia-detalles(tipo:"nota", fuente, titulo, estado, ancho:"100%", clase)
<$let theme-class   = {{{ [[$:/palette]get[text]get[color-scheme]else[light]addprefix[theme-]] [<__clase__>] :and[join[ ]] }}} 
      callout-title = {{{ [<__titulo__>!is[blank]] :else[<__tipo__>titlecase[]]  }}}
      icon-tiddler  = {{{ [all[tiddlers+shadows]tag[$:/tags/TimitAdvertencias/Imagen]contains:callout-type<__tipo__>]
                          :else[[$:/plugins/timit/advertencia/imagenes/nota]] }}}
      source        = {{{ [<__fuente__>get[text]else<__fuente__>] }}}
      estado-valido = {{{ [<__estado__>match[abierto]then[open]] }}} >
<div class=<<theme-class>> style="width:$ancho$;">
<details data-callout=<<__tipo__>> class="callout $clase$" $estado-valido$>
<summary class="callout-title">
<div class="callout-icon"><$transclude tiddler=<<icon-tiddler>> field=text/></div>
<div class="callout-title-inner"><<callout-title>></div>
<div class="callout-fold">{{$:/plugins/timit/advertencia/imagenes/chevron}}</div>
</summary>
<div class="callout-content">
<$transclude tiddler=<<source>> field=title mode=block />
</div>
</details>
</div>
</$let>
\end advertencia-detalles

I also reassigned the value of the “state” variable to the desired value using another “let,” but that didn’t work either.

  <$let // Sobrescribimos la variable 'estado' dentro de este $let
      estado = {{{ [<__estado__>match[abierto]then[open]else<__estado__>] }}}
   >

I understand that the value I get from transforming “abierto” to “open” isn’t in the same format as it should be, and that’s why it isn’t processed, but I’m not sure how to do that.

Any ideas on how to solve this problem?

Thank you very much.

Perhaps you could document the use case rather than force us to read your code. Give us an explanation where you want this change to occur, is it an attribute, a macro parameter etc…

  • Of note: different handling is required to just include a parameter with no value

eg; open here

<details open>

Content
</details>

The standard approach with attribute values is to use the backticks but only for the value, after =

  • I have solved this previously so if this is your question I can find how I solved it.
1 Like

It looks like is should work. But I can confirm it does not. Need to have a closer look at the code.

The problem with your code is, that \define variable substitution only works with parameters that are part of the macro definition. $estado-valide$ is calculated in the right was. So the filter works. But variables defined in $let are not substituted with \define

<details data-callout=<<__tipo__>> class="callout $clase$" $estado-valido$>

If you really need language specific parameters, I would go “the old way” using reveal-widgets See the second example.

1 Like

Sorry I don’t have much time to answer, it’s very late here, but have a look at the <$genesis> widget documentation.

Here is an example you can start to study:

<$genesis $type="details" $names="data-callout class open" $values="X myClass">
<summary>My label</summary>
content
</$genesis>

You can elaborate both $names and $values parameters values based on filters to selectively contain an open attribute name in $names when needed (without need for a corresponding value in $values).

Hope this helps,

Fred

1 Like

One issue (though not the only one, apparently) is that you’re trying to use $estado-valido$ in

<details data-callout=<<__tipo__>> class="callout $clase$" $estado-valido$>

to set the boolean attribute open. But $estado-valido$ is a stand-in for the macro parameter estado-valido, and your macro doesn’t specify a parameter by that name. Instead, you’ve defined estado-valido as a variable using a $let widget inside the macro. To substitute the value of a variable <<estado-valido>>, you’d use $(estado-valido)$ — note the parentheses! — instead (see “Textual substitution of parameters and variables”).

But I think the real issue here is that when $let is used to define a variable <<var>> within a macro definition, that value of that variable isn’t substituted for $(var)$. Here’s a much simpler example adapted from the “Macro Parameter Handling” tiddler linked above, which illustrates the same issue: $let variable substitution testing.tid (454 Bytes)

\define name() Bugs

\define say-hi-using-variables()
Hi, I'm $(name)$ and I live in $(address)$.

* <<address>>
* $(address)$
\end

\define say-hi-2()
<$let address="Rabbit Hole Hill">

Hi, I'm $(name)$ and I live in $(address)$.

* <<address>>
* $(address)$
\end

<$let address="Rabbit Hole Hill">

<<say-hi-using-variables>>
</$let>

<<say-hi-2>>

Here we can see that the $(var)$ substitution works when <<var>> is defined by another \define pragma (like $(name)$) or by a widget outside the macro definition (like $(address)$ in say-hi-using-variables), but not when that widget appears inside the macro definition. I’m not sure whether this constitutes a bug, but it certainly surprised me, too!

So how can you solve this?

  • I think the “best practices” solution is to use the $genesis widget as @tw-FRed suggests. In this case, the $names value might look something like this:
$names=`data-callout class ${ [<__estado__>match[abierto]then[open]] }$`

Note that I’m using the backtick substitution syntax (as linked by @TW_Tones above) rather than standard quotes around the attribute value. This makes it easy to mix literal and filter-determined values.

  • If you don’t mind using the English/HTML attribute directly, it’s probably easiest to adapt your current code to reference the $estado$ parameter directly:
\define advertencia-detalles(...)
<details data-callout=<<__tipo__>> class="callout $clase$" $estado$>
...
</details>
\end

<<advertencia-detalles estado:"open" fuente:"text goes here">>
2 Likes

Hi Tony.

The macro I’m modifying is from Mohanmad’s Callouts add-on. I’ve adapted the style to the new MKdocs model and translated the parameters and macro name to make it more accessible to people with less English knowledge.

I have no problems with the normal macro, but I do with the one that uses “details,” since it forces me to use “open.” Hence the question of how to first declare a variable with the value “abierto” and then process it as “open.”

Greetings.

Hi Emily.

Thanks for the thorough explanation of the problem and its causes.

I’ve been trying the code you suggested, but I can’t figure out the syntax. This is the code I’m trying:

<$genesis $type="details" $names=`data-callout $clase$ ${ [<__estado__>match[abierto]then[open]] }$`>
<summary class="callout-title">
<div class="callout-icon"><$transclude tiddler=<<icon-tiddler>> field=text/></div>
<div class="callout-title-inner"><<callout-title>></div>
<div class="callout-fold">{{$:/plugins/timit/advertencia/imagenes/chevron}}</div>
</summary>
<div class="callout-content">
<$transclude tiddler=<<source>> field=title mode=block />
</div>
</$genesis>

I’ve tried passing the value of the “clase” parameter in the following ways, but it doesn’t work:

<<clase>>, <__clase__>, [<__clase__>]

This is the result of the code:

And this is the code I use to invoke the macro:

<<advertencia-detalles advertencia fuente:"Texto primario" estado="abierto" clase:"texto-primario
">>

I’m not sure what I’m doing wrong, but is wrong. :sweat_smile:

Another iteration I’ve tried is this one but it keeps failing:

\define advertencia-detalles(tipo:"nota", fuente, titulo, estado, ancho:"100%", clase)
<$let theme-class   = {{{ [[$:/palette]get[text]get[color-scheme]else[light]addprefix[theme-]] [<__clase__>] :and[join[ ]] }}} 
      callout-title = {{{ [<__titulo__>!is[blank]] :else[<__tipo__>titlecase[]]  }}}
      icon-tiddler  = {{{ [all[tiddlers+shadows]tag[$:/tags/TimitAdvertencias/Imagen]contains:callout-type<__tipo__>]
                          :else[[$:/plugins/timit/advertencia/imagenes/nota]] }}}
      source        = {{{ [<__fuente__>get[text]else<__fuente__>] }}}
estado-genesis={{{ [<__estado__>match[abierto]then[open]else[]] }}} >
<div class=<<theme-class>> style="width:$ancho$;">
<$genesis $type="details" $names="data-callout class open" $values="<<tipo>> <<clase>> <<estado-genesis>>" >
<summary class="callout-title">
<div class="callout-icon"><$transclude tiddler=<<icon-tiddler>> field=text/></div>
<div class="callout-title-inner"><<callout-title>></div>
<div class="callout-fold">{{$:/plugins/timit/advertencia/imagenes/chevron}}</div>
</summary>
<div class="callout-content">
<$transclude tiddler=<<source>> field=title mode=block />
</div>
</$genesis>
</div>
</$let>
\end advertencia-detalles

The changes are:

...
estado-genesis={{{ [<__estado__>match[abierto]then[open]else[]] }}} >
...
<$genesis $type="details" $names="data-callout class open" $values="<<tipo>> <<clase>> <<estado-genesis>>" >

You’re on the right track with your second iteration: you do need both a $names and a $values attribute in your $genesis widget. Here’s what I’d try instead:

\define advertencia-detalles(tipo:"nota", fuente, titulo, estado, ancho:"100%", clase)
<$let
	theme-class={{{ [[$:/palette]get[text]get[color-scheme]else[light]addprefix[theme-]] [<__clase__>] :and[join[ ]] }}} 
	callout-title={{{ [<__titulo__>!is[blank]] :else[<__tipo__>titlecase[]]  }}}
	icon-tiddler={{{ [all[tiddlers+shadows]tag[$:/tags/TimitAdvertencias/Imagen]contains:callout-type<__tipo__>]
		:else[[$:/plugins/timit/advertencia/imagenes/nota]] }}}
    source={{{ [<__fuente__>get[text]else<__fuente__>] }}}
>
<div class=<<theme-class>> style="width: $ancho$;">
<$genesis $type="details"
	$names=`data-callout class ${ [<__estado__>match[abierto]then[open]] }$`
	$values="$tipo$ $clase$"
>
<summary class="callout-title">
<div class="callout-icon"><$transclude tiddler=<<icon-tiddler>> field=text/></div>
<div class="callout-title-inner"><<callout-title>></div>
<div class="callout-fold">{{$:/plugins/timit/advertencia/imagenes/chevron}}</div>
</summary>
<div class="callout-content">
<$transclude tiddler=<<source>> field=title mode=block />
</div>
</$genesis>
</div>
</$let>
\end advertencia-detalles

<<advertencia-detalles advertencia fuente:"Texto primario" estado:"abierto" clase:"texto-primario">>

I made two major changes here:

  • In the $names line: In the HTML <details> element, open is a boolean attribute: this means that if it’s present at all, with or without a value, the details element will be open. In plain HTML, that could look like <details open> or <details open="true"> or <details open="yes"> or <details open="yellow">… the value of open is totally irrelevant. In terms of our $genesis widget, this means that we want to include open among the values of $names only when [<__estado__>match[abierto]] is true — so that filter needs to appear somewhere in $names.

There are two basic ways to do this. In the code above, I used the special syntax for substituted attribute values — note the backticks and the ${ ... }$ wrapper around the filter.

I tend to prefer the backtick syntax because it’s a little shorter and it makes it easy to mix literal values and variables or filter results. But if you prefer, you can also do it the old-fashioned way, with a filtered attribute value:

$names={{{ data-callout class [<__estado__>match[abierto]then[open]] +[join[ ]] }}}

In this case, data-callout and class go inside the filter braces along with the filter run, and you need to add +[join[ ]] at the end to concatenate all the results into a single string — just as we see in theme-class={{{ ... }}} in the $let widget. (The +[join[ ]] step isn’t necessary if you’re using backtick syntax because all spaces outside the filter run are automatically preserved.)

  • In the $values line: As discussed above, we don’t need to provide a value for “open”. But we do need to make sure that the values of your other attributes are correct.
    • In TW syntax, quotation marks mean a literal value, so "<<tipo>> <<clase>>" will give you <<tipo>> <<clase>> — the variables will never get evaluated.
    • And in this case, <<tipo>> wouldn’t give you the correct value anyway — since tipo is a parameter of the macro, the corresponding variable would be <<__tipo__>>.
    • But since tipo and clase are parameters of the macro, we can use the substituted forms "$tipo$ $clase$", which is easier anyway. This gives us $values="$tipo$ $clase$".

You may have noticed that I am using quotation marks here; that’s because macros can perform a special kind of substitution before the macro code gets evaluated. This is distinct from backtick substitution (which came later), and its syntax is (confusingly) slightly different.

I hope this is clear enough! Feel free to ask if you need further clarification.


Also note that as of 5.3.8, you can’t use = in a short-form macrocall like <<advertencia-detalles...>>, so fuente:"Texto primario" estado="abierto" won’t work: you’d need to use estado:"abierto" instead.

This can be especially confusing because estado="abierto" would be correct if you’re using a widget-style macrocall like <$macrocall $name="advertencia-detalles" /> or <$transclude $variable="advertencia-detalles" />, and it’s possible that later versions of TW will allow us to use : and = interchangeably to specify parameters. But for now, we just have to be extra-vigilant about searching for typos.

2 Likes

Hi Emily.

Almost at the same time you were explaining the problem to me, I was working on a “close” solution using this code. But I still need to figure out why the chevron isn’t aligned correctly downwards.

\define advertencia-detalles(tipo:"nota", fuente, titulo, estado, ancho:"100%", clase)
<$let theme-class   = {{{ [[$:/palette]get[text]get[color-scheme]else[light]addprefix[theme-]] [<__clase__>] :and[join[ ]] }}} 
      callout-title = {{{ [<__titulo__>!is[blank]] :else[<__tipo__>titlecase[]]  }}}
      icon-tiddler  = {{{ [all[tiddlers+shadows]tag[$:/tags/TimitAdvertencias/Imagen]contains:callout-type<__tipo__>]
                          :else[[$:/plugins/timit/advertencia/imagenes/nota]] }}}
      source        = {{{ [<__fuente__>get[text]else<__fuente__>] }}}
      estado-valido={{{ [<__estado__>match[abierto]then[open]] }}}
>
<div class=<<theme-class>> style="width:$ancho$;">
<$genesis $type="details" $names="data-callout class open" $values="<<__tipo__>> 'callout '<<__clase__>> <<__estado-valido__>>" >
<summary class="callout-title">
<div class="callout-icon"><$transclude tiddler=<<icon-tiddler>> field=text/></div>
<div class="callout-title-inner"><<callout-title>></div>
<div class="callout-fold">{{$:/plugins/timit/advertencia/imagenes/chevron}}</div>
</summary>
<div class="callout-content">
<$transclude tiddler=<<source>> field=title mode=block />
</div>
</$genesis>
</div>
</$let>
\end advertencia-detalles

This is the result:

I’ll take a closer look at your explanation and then comment on the results.

Thank you so much.

Hi Emily.

I was struggling with the code and couldn’t get the classes to apply correctly until I tried your solution, and now everything works fine. I just had to add a text (callout) to get the class to build correctly, and the rest is the same.
This is your solution with the small fix:

\define advertencia-detalles(tipo:"nota", fuente, titulo, estado, ancho:"100%", clase)
<$let theme-class   = {{{ [[$:/palette]get[text]get[color-scheme]else[light]addprefix[theme-]] [<__clase__>] :and[join[ ]] }}} 
      callout-title = {{{ [<__titulo__>!is[blank]] :else[<__tipo__>titlecase[]]  }}}
      icon-tiddler  = {{{ [all[tiddlers+shadows]tag[$:/tags/TimitAdvertencias/Imagen]contains:callout-type<__tipo__>]
                          :else[[$:/plugins/timit/advertencia/imagenes/nota]] }}}
      source        = {{{ [<__fuente__>get[text]else<__fuente__>] }}}
>
<div class=<<theme-class>> style="width:$ancho$;">
<$genesis $type="details" $names={{{ data-callout class [<__estado__>match[abierto]then[open]] +[join[ ]] }}}
$values="$tipo$ 'callout'$clase$"
 >
<summary class="callout-title">
<div class="callout-icon"><$transclude tiddler=<<icon-tiddler>> field=text/></div>
<div class="callout-title-inner"><<callout-title>></div>
<div class="callout-fold">{{$:/plugins/timit/advertencia/imagenes/chevron}}</div>
</summary>
<div class="callout-content">
<$transclude tiddler=<<source>> field=title mode=block />
</div>
</$genesis>
</div>
</$let>
\end advertencia-detalles

Adapting my code to your way of setting variables works almost perfectly for me:

\define advertencia-detalles(tipo:"nota", fuente, titulo, estado, ancho:"100%", clase)
<$let theme-class   = {{{ [[$:/palette]get[text]get[color-scheme]else[light]addprefix[theme-]] [<__clase__>] :and[join[ ]] }}} 
      callout-title = {{{ [<__titulo__>!is[blank]] :else[<__tipo__>titlecase[]]  }}}
      icon-tiddler  = {{{ [all[tiddlers+shadows]tag[$:/tags/TimitAdvertencias/Imagen]contains:callout-type<__tipo__>]
                          :else[[$:/plugins/timit/advertencia/imagenes/nota]] }}}
      source        = {{{ [<__fuente__>get[text]else<__fuente__>] }}}
      estado-valido={{{ [<__estado__>match[abierto]then[open]] }}}
>
<div class=<<theme-class>> style="width:$ancho$;">
<$genesis $type="details" $names=" data-callout class open "
$values="$tipo$ 'callout'$clase$ <<__estado-valido__>>"
 >
<summary class="callout-title">
<div class="callout-icon"><$transclude tiddler=<<icon-tiddler>> field=text/></div>
<div class="callout-title-inner"><<callout-title>></div>
<div class="callout-fold">{{$:/plugins/timit/advertencia/imagenes/chevron}}</div>
</summary>
<div class="callout-content">
<$transclude tiddler=<<source>> field=title mode=block />
</div>
</$genesis>
</div>
</$let>
\end advertencia-detalles

But I have a problem: the details element always appears to be open. How can I make it so that if there’s no value for the “status” parameter, it appears to be closed?

The same thing happens to me with this other variation:

      estado-valido={{{ [<__estado__>match[abierto]then[open]]else[]] }}}

And with this:

      estado-valido={{{ [<__estado__>match[abierto]then[open]]else[<__estado__>] }}}

Best regards

Take a closer look at my suggested code: I took estado-valido out entirely! Instead, the filter logic goes in $names (where you do have it in your modified version of my suggestion):

$names={{{ data-callout class [<__estado__>match[abierto]then[open]] +[join[ ]] }}} 

This value does not need to be paired with a corresponding value in $values, so you don’t need <<estado-valido>> at all. 1

  • If your macrocall contains estado:"abierto", the $names filtered transclusion will be evaluated to $names="data-callout class open".
  • If open is present in $names, you’ll get a <details> element that includes open="". This element will always default to being open.
  • This means open should not appear in $names in situations when we don’t want the element to be open - so you can’t use $names="data-callout class open" directly in your code.

Rather than modifying your original code while looking at mine, I’d recommend replacing your entire definition with the one I suggested, then making your modifications there. I think this may be less confusing than doing it one piece at a time, and you shouldn’t have to make as many changes.


1 Here, <<__estado-valido__>> wouldn’t produce a value anyway — you’ve defined estado-valido in the $let widget rather than as a parameter of the macro itself, so the syntax is different. Contrast estado and estado-valido:

  • estado: a named parameter appearing in \define advertencia-detalles(tipo:"nota", fuente, titulo, estado, ancho:"100%", clase). The variable form of a macro parameter (using \define) is <<__estado__>> or <<__tipo__>>, or <__estado__> in a filter, e.g. [<__estado__>match[abierto]then[open]]
  • estado-valido: a normal variable defined by $let. The variable form is <<estado-valido>>, or <estado-valido> in a filter, e.g. [<estado-valido>!match[]]

If you’ve used procedures — defined with \procedure my-procedure(variable1, variable2) — this is a major difference to be aware of. Procedure parameters look just like normal variables: <<variable1>>, <<variable2>>. Macro parameters need the double underscores.

However, procedures don’t support the $ancho$ $estado$ substitution syntax, so they can’t be used interchangeably with macros, even though the variable handling is a little easier.

3 Likes

I am sorry I don’t have a lot of time right now, Will later, so I have not read the recent details above. I just thought I would share a simple tip to combat cases like the existence and non existence of a parameter (without a value) such as “open” in the details widget.

Simply generate two instances of the details widget one with Open, one without. Then use list, reveal or %if statements to choose which of the two to display. To save code generalise the content of the details widget and simply repeat it (eg Macro or transclusion) to get the same result.

  • This is simply setting which details widget to use, which determins if open or closed by default.

I know this is not perfect but it is practical and fast to code.

  • I think my prior solution was to hide these inside a custom widget, in which the Genesis widget is used to present the details element so we can pass into them all parameters given to the custom widget (allowing their use inside the details widget).
1 Like

Hi Emily.

Thank you so much for your kind explanation. I think I’ve gotten a clear idea of ​​how to use the different types of variables in different contexts. I’ve made a cheat sheet so I don’t forget. :sweat_smile:

But this morning, when I was trying to modify the other macro I have, which performs a different type of “admonition,” something went wrong.
Everything was working fine yesterday, but I must have forgotten something when documenting the process, and I can’t get it to work properly now. :man_facepalming:

What’s the problem I’m having now? Well, it applies the styles correctly but doesn’t open the content despite having the “state” variable set to “open.” Unless I’m overlooking something, which I’m sure I am, I’m using the code that worked yesterday.

This is the code I use to invoke the macro:

\define advertencia-detalles(tipo:"nota", fuente, titulo, estado, ancho:"100%", clase)
<$let theme-class   = {{{ [[$:/palette]get[text]get[color-scheme]else[light]addprefix[theme-]] [<__clase__>] :and[join[ ]] }}} 
      callout-title = {{{ [<__titulo__>!is[blank]] :else[<__tipo__>titlecase[]]  }}}
      icon-tiddler  = {{{ [all[tiddlers+shadows]tag[$:/tags/TimitAdvertencias/Imagen]contains:callout-type<__tipo__>]
                          :else[[$:/plugins/timit/advertencia/imagenes/nota]] }}}
      source        = {{{ [<__fuente__>get[text]else<__fuente__>] }}}
>
<div class=<<theme-class>> style="width:$ancho$;">
<$genesis $type="details" 
$names={{{ data-callout class [<__estado__>match[abierto]then[open]] +[join[ ]] }}}
$values="$tipo$ 'callout'$clase$" 
>
<summary class="callout-title">
<div class="callout-icon"><$transclude tiddler=<<icon-tiddler>> field=text/></div>
<div class="callout-title-inner"><<callout-title>></div>
<div class="callout-fold">{{$:/plugins/timit/advertencia/imagenes/chevron}}</div>
</summary>
<div class="callout-content">
<$transclude tiddler=<<source>> field=title mode=block />
</div>
</$genesis>
</div>
</$let>
\end advertencia-detalles

And this is the code for the macro that isn’t working quite right:

<<advertencia-detalles advertencia fuente:"Texto primario" estilo:"abierto" clase:"text-primary">>

And this is what I get:

What am I missing?

Thank you very much.

I’ll answer myself.

The error was in the code to invoke the macro, which was changing “estado” to “estilo”, and of course it didn’t work that way.
I need to take a break from looking at code; I’m not used to it and I’m already feeling saturated… :joy: :joy: :joy:

And this is the final result after applying Emily’s instructions to the code:

Thanks so much for the help.
Best regards.

2 Likes