SVG <g> element as <$button> widget

I’ve been working on a complicated interactive SVG infographic, and I’ve learned I can use <$link> widgets to make ordinary <a> tags within it.

I’ve been trying to create the SVG element as something within the TiddlyWiki widget tree. So, I’ve been trying to create the tag using <$button tag="g">. But then the particular <g> curve doesn’t render.

Wrapping the <g> in a <$link> does work, and I understand that it creates a neutral <a> element wrapping the <g> element. But using <$link> I can only navigate to another tiddler. I can’t, as far as I know, set a field. So I’ve tried wrapping that link widget with a <$linkcatcher> widget, using its actions attribute to specify action widgets to be activated:

<$linkcatcher actions="""<$action-setfield $field="myfield" $value="new value"/>""">

But, again, this causes the <g> tag that is wrapped by all of that not to render.

The best research I can achieve indicates that I have to separate the SVG from a button widget triggering the set-field action. I guess I’ll have to work on positioning that to appear to be part of the rest of the SVG image.

I have to assume that there’s something about the order of processing for the widget tree that is causing this behavior, but I can’t imagine a straightforward reason why a button widget can’t simply be a <g> tag. I’m really curious to know whether a single SVG approach, without a separated widget, is possible.

linkcatcher works in this example:

\define myacts()
<$action-sendmessage $message="tm-home"/>
\end

 <svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
        <!-- Square -->
        <rect x="10" y="10" width="180" height="180" fill="lightgray" stroke="black" stroke-width="2"/>
       <$linkcatcher to=yyy actions=<<myacts>>> 
       <$link to=xxx>
       <g>
            <circle cx="70" cy="100" r="30" fill="red"/>
            <circle cx="130" cy="100" r="30" fill="blue"/>
       </g>
       </$link>
       </$linkcatcher> 
</svg>
2 Likes

Perhaps the EventCatcherWidget can be used here:

\procedure myacts()
<$action-sendmessage $message="tm-home"/>
\end

<$eventcatcher selector="g.click" $click=<<myacts>>>
 <svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
        <!-- Square -->
        <rect x="10" y="10" width="180" height="180" fill="lightgray" stroke="black" stroke-width="2"/>
       <g class="click">
            <circle cx="70" cy="100" r="30" fill="red"/>
            <circle cx="130" cy="100" r="30" fill="blue"/>
       </g>
</svg>
</$eventcatcher>
4 Likes

Hi @buggyj

Something unexpected -at least for me- happens when I use this code in a new tiddler on tiddlywiki.com: clicking on red or blue circle not only displays default tiddlers like it should, but also creates a tiddler named “yyy” containing “xxx”.

Is this expected <$linkcatcher> + <$link> behavior?

Fred

1 Like

As well as actions the linkcatcher can put the tiddler from the <$link> (xxx in the example) into a tiddler whose title is the value of the ‘to’ attribute, thus xxx get put inside yyy

2 Likes

Thanks for the explanation! Very inspiring!

Both your solutions work in that simple case, @buggyj, @XLBilly, thanks! But I’m not sure I understand this yet… also haven’t had a chance to test it in my full project yet, but let’s try this simplest test case:

\procedure myacts()
<$action-sendmessage $message="tm-home"/>
\end

 <svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
        <!-- Square -->
        <rect x="10" y="10" width="180" height="180" fill="lightgray" stroke="black" stroke-width="2"/>
       <g class="red-circle">
            <circle cx="70" cy="100" r="30" fill="red"/>
       </g>
       <g class ="blue-circle>
            <circle cx="130" cy="100" r="30" fill="blue"/>
       </g>
</svg>

That’s just the two circles, in separate <g> elements. So, say we need to be able to use one of those as a traditional link and the other to trigger our action:

(this works):

\procedure myacts()
<$action-sendmessage $message="tm-home"/>
\end

 <svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
         <!-- Square -->
         <rect x="10" y="10" width="180" height="180" fill="lightgray" stroke="black" stroke-width="2"/>
        <$link to="RedTiddler">
        <g class="red-circle">
             <circle cx="70" cy="100" r="30" fill="red"/>
        </g>
        </$link>
        <$linkcatcher to="NeutralTiddler" actions=<<myacts>>>
        <$link to="BlueTiddler">
        <g class ="blue-circle>
             <circle cx="130" cy="100" r="30" fill="blue"/>
        </g>
         </$link>
        </$linkcatcher>
</svg>

Yeah, that works. And

(this doesn’t work):


\procedure myacts()
<$action-sendmessage $message="tm-home"/>
\end

 <svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
        <!-- Square -->
        <rect x="10" y="10" width="180" height="180" fill="lightgray" stroke="black" stroke-width="2"/>
        <$link to="xxx">
        <g class="red-circle">
             <circle cx="70" cy="100" r="30" fill="red"/>
        </g>
        </$link>
        <$eventcatcher actions=<<myacts>> selector="g.blue-circle">
        <g class ="blue-circle">
             <circle cx="130" cy="100" r="30" fill="blue"/>
       </g>
       </$eventcatcher>
</svg>

Trying the same with eventcatcher, the blue circle does not render.

Can’t wait to try this in a more complicated environment later.

Edit

Days later, I realized I pasted in the wrong snippet for the example demonstrating <$linkcatcher>. Now corrected.

Your $eventcatcher usage is incorrect. Using older, deprecated syntax, you should write:

<$eventcatcher events="click" actions-click=<<myacts>> selector="g.blue-circle">

or, using current recommended syntax:

<$eventcatcher $click=<<myacts>> selector="g.blue-circle">

However, this still doesn’t work because the $eventcatcher widget generates a SPAN element with class=“tc-eventcatcher”, which apparently interferes with rendering the blue circle within the enclosing SVG wrapper. You can avoid this problem by moving the $eventcatcher widget outside the entire <svg>...</svg> wrapper, like this:

<$eventcatcher $click=<<myacts>> selector="g.blue-circle">
 <svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
        <!-- Square -->
        <rect x="10" y="10" width="180" height="180" fill="lightgray" stroke="black" stroke-width="2"/>
        <$link to="xxx">
        <g class="red-circle">
             <circle cx="70" cy="100" r="30" fill="red"/>
        </g>
        </$link>
        <g class ="blue-circle">
             <circle cx="130" cy="100" r="30" fill="blue"/>
       </g>
</svg>
</$eventcatcher>

-e

A custom widget can be created for svg clicks

\widget $svg.click(actions)
       <$linkcatcher  actions=<<actions>>> 
       <$link to="___dummy__">
       <$slot $name="ts-raw" />
        </$link>
       </$linkcatcher>       
\end      
 
\define myacts()
<$action-sendmessage $message="tm-home"/>
\end

\define control()
<$action-navigate $to="$:/ControlPanel"/>
\end

 <svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
        <!-- Square -->
        <rect x="10" y="10" width="180" height="180" fill="lightgray" stroke="black" stroke-width="2"/>

       <g>
        <$svg.click actions=<<myacts>>>
            <circle cx="70" cy="100" r="30" fill="red"/> 
         </$svg.click>
         <$svg.click actions=<<control>>>
            <circle cx="130" cy="100" r="30" fill="blue"/>
          </$svg.click>
       </g>

</svg>

Yes, wrapping the whole thing with the eventcatcher widget works file. It needs one refinement to be the ideal solution–a CSS definition, probably within a <style> element nested inside the <svg> element, to cause the mouse cursor to react as on a link.

\procedure myacts()
<$action-sendmessage $message="tm-home"/>
\end

<$eventcatcher $click=<<myacts>> selector="g.blue-circle">
 <svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
        <!-- Square -->
        <rect x="10" y="10" width="180" height="180" fill="lightgray" stroke="black" stroke-width="2"/>
        <$link to="xxx">
        <g class="red-circle">
             <circle cx="70" cy="100" r="30" fill="red"/>
        </g>
        </$link>
        <g class="blue-circle">
             <circle cx="130" cy="100" r="30" fill="blue"/>
       </g>
    <style>
        .blue-circle {
            pointer-events: all;
            cursor: pointer;
        }
    </style>
</svg>
</$eventcatcher>