How to Create a Button with Two Selection Levels

tiddlers.json (3.0 KB)

Hello everyone.

I’m trying to create a menu with submenus using buttons. The reason is to have different tools grouped together in the tiddler editing menu to make it easier to create and modify tiddlers. For example, grouping the different types of admonitions, bagels, cards, etc. under a single button.

I’ve been trying with Chat GPT and Gemini and I’ve gotten somewhere, but I can’t get any further. I have a button with two options, and within those options, I can choose suboptions. The thing is, in the options tiddler, if I select the button, I can see the suboptions, but if I go to the main “Menu” button, I can only see the options when I select it. Selecting any of them doesn’t show the suboptions. Well, actually, I can, but in the tiddler corresponding to those options.
Does anyone know how to fix this? If it’s possible…
I’ll leave you the tiddlers I’m using so you can see what I’ve achieved.

Best regards.

Hi @JuanPalomo

First thing: while ChatGPT and other LLMs may be somewhat helpgul for providing programming “clues”… they are notoriously bad at generating code that actually works, and is usually take more effort to fix the suggested code than to learn how to write your own code from the start.

Now… on to your specific issues, which we’ll tackle one at a time:

You are using combined $button/$reveal widgets to display popup content that contains more $button widgets. By default, when you click on popup content (such as a $button), the button is handled and the popup is immediately closed. However, for your use case, you don’t want that popup to be closed. Instead, you want a second-level popup to appear. Fortunately, it is actually easy to prevent the popup from being closed, simply by adding a special classname to the popup contents: tc-popup-keep. This classname tells the TWCore to allow clicks within the popup to be processed without closing the containing popup. Thus, in your “MenuContainer” tiddler, you should change

<div class="tc-drop-down">

to

<div class="tc-drop-down tc-popup-keep">

The next problem is that the display of the “MenuItemA” and “MenuItemB” popups are very hard to read because they don’t have any enclosing border/background styles, so they get mixed in with whatever content they are on top of. Again, it is easy to fix this in “MenuItemA” and “MenuItemB” by changing this:

<div class="tc-submenu" style="padding-left:1em;">

to

<div class="tc-drop-down">

These two small changes should get you further along in your efforts. Let me know how it goes…

enjoy,
-e

3 Likes

Hi Eric.

Thank you very much for the help. Now at least I can get the code to look like what I intended.

You’re right about using AI to program. It certainly helps me understand parts of the code, but when it comes to generating code, it rarely gets it right, and with each new interaction, things get worse. :sweat_smile:

I’d like to know if there’s anything I can do to prevent the submenu from overflowing the bottom of the tiddler, as in this screenshot.

On the other hand, if I have the MenuItemA tiddler and the MenuContainer tiddler open, when I select an option from the Menu icon, the dropdown appears correctly, but at the same time, the suboptions are displayed in the MenuItemA tiddler and in an area where they shouldn’t be.

This can be solved by not having the MenuItemA tiddler open, but I’d like to know if there’s a way to prevent this.

Thank you very much.

To prevent the popup from appearing in two places, you can use the TWCore qualify macro to generate the popup and state titles used by the $button and $reveal widgets. This macro automatically appends a “magic number” to the end of the specified text and ensures that the resulting title is unique to the tiddler in which it occurs.

Thus, change

<$button popup="$:/state/custom/dropdown-menu"

to

<$button popup=<<qualify "$:/state/custom/dropdown-menu">>

and

<$reveal state="$:/state/custom/dropdown-menu"

to

<$reveal state=<<qualify "$:/state/custom/dropdown-menu">>

That will solve your “popup shows in two places” problem, since each tiddler will now be using distinct values for the popup and state parameters.

There is also another refinement to your code that will greatly simplify your menu implementation…

Instead of repeating similar wikitext code for the $button and $reveal widgets in each menu/submenu tiddler, you can define a single \procedure that contains the needed wikitext code with some parameters, and then just invoke that procedure everywhere you need to display a menu/submenu.

Give this a try…

In “MenuContainer”, write:

\procedure menuitem(label,tag,class)
<div class=<<class>>>
<$let popid=<<qualify "$:/state/custom/dropdown-menu">>>
<$button popup=<<popid>> class=tc-btn-invisible><<label>></$button>
<$reveal state=<<popid>> type=popup position=below class=tc-popup-keep>
  <div class=tc-drop-down>
    <$list filter="[tag<tag>!has[draft.of]]"><$transclude/></$list>
  </div>
</$reveal>
</$let>
</div>
\end

<<menuitem "☰ Menú" "Menu-Level1">>

Then, in “MenuItemA”, you can replace the entire contents with:

<<menuitem "Opción A" "Menu-Level2-ItemA" "menu-item">>

and in “MenuItemB”, you can replace the entire contents with:

<<menuitem "Opción B" "Menu-Level2-ItemB" "menu-item">>

Notes:

  • In the menuitem() procedure, we use a $let widget to get the qualified value that will be used for the <<$button popup=...> and <<$reveal state=...> parameters. We then simply refer to <<popid>> instead of repeating the <<qualify>> macro in each widget.
  • Note also that the popid assignment doesn’t need to use a different tiddler title for each menu/submenu tiddler, because the results of the <<qualify "$:/state/custom/dropdown-menu">> macro are automatically uniquely based on the tiddler in which the <<menuitem>> macro is called from.

Let me know how it goes…

enjoy,
-e

2 Likes

Hi Eric.

I tried the modification you suggested to avoid duplicating submenus, but I couldn’t get it to work. I don’t know if I’m doing something wrong. This is the code I modified according to your instructions:

<$button popup=<<qualify "$:/state/custom/dropdown-menu">> class="tc-btn-invisible">
  <span class="tc-btn-text">☰ Menú</span>
</$button>

<$reveal state=<<qualify "$:/state/custom/dropdown-menu">> type="popup" position="below" animate="yes">
<div class="tc-drop-down tc-popup-keep">
    <$list filter="[tag[Menu-Level1]]" variable="menuItem">
      <$transclude tiddler=<<menuItem>>/>
    </$list>
  </div>
</$reveal>

Thank you very much.

Because “MenuItemA” and “MenuItemB” have their own <$button popup=...> and <$reveal state=...> syntax, you need to also use <<qualify "...">> for those instances.

-e

1 Like

Hi @JuanPalomo

Can you describe what’s not working? I tried your code on tiddlywiki.com and it seems to work Ok…

Fred

1 Like

Hi Fred.

Yes, it works correctly, but I forgot to make the changes to the MenuItemA and MenuItemB tiddlers.
Thanks.

1 Like

Hi Eric.
Sorry, but since you referenced $:/state/custom/dropdown-menu in the code, I didn’t see any other places where I needed to make any changes… :sweat_smile:
Now it works as I expected.

Thanks.

Hello everyone.

I’ve managed to move the multi-level dropdown template to the toolbar. Menu-TB_1.json (2.9 KB)

If I wanted the buttons to not be shaded when selected, what would I need to change to do so? Here’s what I mean:

Thank you very much.

Add style="color:revert;background:none;" to the $button widgets in the “MenuContainer-dropdown”, “MenuItemA” and “MenuItemB” tiddlers.

Also, as a general principle, you should NOT name your custom tiddlers starting with “core/ui”, since they are not actually part of the TWCore standard distribution. I suggest using “custom/ui/…” instead.

-e

1 Like

Hi Eric.

Thanks for the instructions, it’s now working as I wanted. You’re absolutely right about the tiddler names. But I was testing, and when cloning other tiddlers from the system to experiment, I left part of the original name.

Now I’m trying something, but it’s not working. In the code you helped me create, the main “Menu” button had two additional levels (MenuItemA-SubItemA), but when I add it to the toolbar, I get three: Menu-MenuItemA-SubItemA, since the toolbar button itself acts as a level.

I’ve tried changing the code so that the button I create in the toolbar becomes the previous Menu button (and so that MenuItemA and SubItemA only appear when I press the button), but I can’t get it to work.
This is the code I used in the custom/ui/EditorToolbar/MenuContainer-dropdown tiddler:

<$button popup=<<qualify "$:/state/custom/dropdown-menu">> class="tc-btn-invisible" style="color:revert;background:none;">
  <span class="tc-btn-text"></span>
</$button>

<$reveal state=<<qualify "$:/state/custom/dropdown-menu">> type="popup" position="below" animate="yes">
<div class="tc-drop-down tc-popup-keep">
    <$list filter="[tag[Menu-Level1]]" variable="menuItem">
      <$transclude tiddler=<<menuItem>>/>
    </$list>
  </div>
</$reveal>

And this is the result:


What I’m looking for is for Option A and Option B to be in the top block, which is currently blank.

Taking advantage of this modification, I managed to generate a three multi-level system. I’m posting the code so you can see it. Menu-TB-doble_2.json (5.3 KB)

Best regards and thanks.

I’ll answer myself. I don’t know if this method is correct, or if it’s the best, but it worked for me. The code used in custom/ui/EditorToolbar/MenuContainer-dropdown was:

    <$list filter="[tag[Menu-Level1]]" variable="menuItem" style="background:grey;">
      <$transclude tiddler=<<menuItem>>/>
    </$list>

And the result is this:

Hi Eric.

The second simplified code suggestion you mentioned was a bit more difficult to understand, but I managed to get it working the way I wanted.
Thank you so much for the advice and help. I’ve learned a little more, and now I’ll see if I can develop something with it. :sweat_smile: