Linear navigation control

I’m currently in the “concept of a plan” stage of a potential new TW.

What I’m thinking this new wiki will need is a way to display a sequence of tiddlers. I’m wanting to have a tiddler that is “host” to a sequence of other tiddlers selected via a filter and displayed like:


<-Link/button-to-previous-tiddler Title/Caption-of-displayed-tiddler Link/button-to-next-tiddler->

“current”-tiddler-displayed-here-via-transclusion

Bonus points: duplicate-of-top-navigation-controls-here


Clicking the “previous”/“next” buttons/links will change the transcluded tiddler, based on the order of tiddlers produced by the filter.

I’m wondering if something like this has already been done, to save me the trouble of figuring it out myself?

1 Like

Here is a generalized version of a switcher that I use. You can copy all the code below, go to TiddlyWiki.com, edit a new tiddler, paste the code, then play around with it in the preview pane.

\define state-tid() $:/state/appointment-view
\function state() [<state-tid>get[text]] :else[[DefaultTiddlers]]

\function filter.base() [tag[Concepts]!prefix[$:/]!has[draft.of]]
\function get.prev() [filter.base[]sort[title]before<state>is[tiddler]] :else[[]]
\function get.next() [filter.base[]sort[title]after<state>is[tiddler]] :else[[]]

\procedure switch-prev() <$action-setfield $tiddler=<<state-tid>> text=<<get.prev>> />
\procedure switch-next() <$action-setfield $tiddler=<<state-tid>> text=<<get.next>> />

<div class="session-switcher">
    <$tiddler tiddler=<<get.prev>>>
    <$button class="switch-prev"
        actions=<<switch-prev>>
        disabled={{{ [<get.prev>is[tiddler]then[no]else[yes]] }}}>
        {{$:/core/images/left-arrow|1em}}
        <<get.prev>>
    </$button>
    </$tiddler>
    <$tiddler tiddler=<<get.next>>>
    <$button class="switch-next"
        actions=<<switch-next>>
        disabled={{{ [<get.next>is[tiddler]then[no]else[yes]] }}}>
        {{$:/core/images/right-arrow|1em}}
        <<get.next>>
    </$button>
    </$tiddler>
</div>

<h1 style="text-align:center;">{{{ [<state>] :else[[DefaultTiddlers]] }}}</h1>
<blockquote style="margin: 1em 3em;">
<$transclude tiddler={{{ [<state>] :else[[DefaultTiddlers]] }}} mode=block />
</blockquote>

<style>
.session-switcher {
    min-width: 20ch;
    max-width: 60%;
    margin: 1em auto;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.25em 1em;

    .switch-prev {
        grid-column: 1/2;
        border-radius: 0.25em 0 0 0.25em;
    }

    .switch-next {
        grid-column: -2/-1;
        flex-direction: row-reverse;
        border-radius: 0 0.25em 0.25em 0;
    }

    button {
        padding: 0.35em 0.5em;
        display: flex;
        justify-content: space-between;
        align-items: center;
        background-color: <<colour tab-background>>;
        box-shadow: 0 0 0 1px <<colour tab-border>>;
        border: 0;
        font-size: 0.8rem;

        &:not([disabled="true"]):hover,
        &:not([disabled="true"]):focus {
        background-color: <<colour tab-background>>80;
            box-shadow:
                0 0 0 1px <<colour tab-border>>,
                0 0 0 3px <<colour primary>>80;
        }

        &[disabled=true] {
            box-shadow: none;
            cursor: not-allowed;
        }

        svg { padding: 0; }
    }
}
</style>

There was a thread a while back about how to display a small number of tiddlers from a larger set, with pagination controls. While this isn’t precisely your case, the answers probably could be tweaked to include just one.

I also wrote a plugin that has overlapping ideas with your request. There we had no “hosting” tiddler, just a footer that pointed out your path through a sequential set of tiddlers.

It may be that neither will do, but I think both might help show the way.

Keep in mind a tag can be used like this, first/last, next and previous buttons can be displayed for the tiddlers with a given tag, and navigating the tag list. They can be reordered with drag and drop on the tag pill.

I would be happy to put the time in to do the following;

  • Use a tag for a list
  • If the tag tiddler indicates eg show-tag-nav=yes
    • Display First/Last Next/Previous on tiddlers so tagged
1 Like

Here’s my version:

\define navstate() $:/temp/navstate
\define navfilter() [tag[HelloThere]]

\procedure navbar()
<center>
@@float:left;
<$button set=<<navstate>> setTo=<<first>>>{{$:/core/images/chevron-left}}</$button>
<$button set=<<navstate>> setTo=<<prev>>>{{$:/core/images/left-arrow}}</$button>
@@
<$link to=<<here>>><$view tiddler=<<here>> field=caption><$text text=<<here>>/></$view></$link>
@@float:right;
<$button set=<<navstate>> setTo=<<next>>>{{$:/core/images/right-arrow}}</$button>
<$button set=<<navstate>> setTo=<<last>>>{{$:/core/images/chevron-right}}</$button>
@@
</center>
\end

<$set name=tids filter=<<navfilter>>>
<$let
   first={{{ [enlist<tids>first[]] }}}
   last={{{ [enlist<tids>last[]] }}}
   here={{{ [<navstate>get[text]] ~[<first>] }}}
   prev={{{ [enlist<tids>before<here>] ~[<first>] }}}
   next={{{ [enlist<tids>after<here>] ~[<last>] }}}>
<<navbar>><$transclude $tiddler=<<here>> $mode=block/><<navbar>>
</$let>

Notes:

  • navstate() defines the tiddler used to track the title of the content to be displayed
  • navfilter() defines the filter that identifies the tiddlers to be displayed
  • navbar() shows first,previous,next,last buttons and the currently displayed tiddler caption/title
  • $set applies the navfilter to get the list of tiddlers to be displayed
  • $let calculates the first,last,here,prev and next tiddler titles
    • note how here, prev and next have “fallback” defaults
  • Finally, we display the navbar,the current tiddler content, and another copy of the navbar

enjoy,
-e

3 Likes

I recently stumbled across an older plugin repo that had something called “Wizard Wizard” which sounded similar to what you are trying to do but I haven’t really dug into it yet.

https://ooktech.com/jed/ExampleWikis/WizardWizard/

Thanks for all the replies. I’ll look through them and see what I like. Probably a bit of all of them, but that’s a different problem!

After perusing the various solutions, I’ve decided that Eric’s was the closest to what I was looking for. However, I really liked the way Brian’s switcher included the title of the tiddler on the prev/next buttons and how they were disabled when they were the first/last. I also preferred to always show the tiddler title not caption for the currently visible tiddler. However, Eric’s technique of using the caption if present instead of the title was what the prev/next buttons needed.

All up, I ended up with:

\define navstate() $:/temp/navstate
\define navfilter() [tag[Concepts]]
\define btnaction() <$action-navigate $to=<<currentTiddler>>/>

\procedure navbar()
<center>
@@float:left;
<$button actions=<<btnaction>> set=<<navstate>> setTo=<<first>>>{{$:/core/images/chevron-left}}</$button>
<$button actions=<<btnaction>> set=<<navstate>> setTo=<<prev>> disabled={{{ [<prev>is[tiddler]then[no]else[yes]] }}}>{{$:/core/images/left-arrow}} <$view tiddler=<<prev>> field=caption><$text text=<<prev>>/></$view></$button>
@@
<$link to=<<here>>/>
@@float:right;
<$button actions=<<btnaction>> set=<<navstate>> setTo=<<next>> disabled={{{ [<next>is[tiddler]then[no]else[yes]] }}}><$view tiddler=<<next>> field=caption><$text text=<<next>>/></$view> {{$:/core/images/right-arrow}}</$button>
<$button actions=<<btnaction>> set=<<navstate>> setTo=<<last>>>{{$:/core/images/chevron-right}}</$button>
@@
</center>
\end

<$set name=tids filter=<<navfilter>>>
<$let
   first={{{ [enlist<tids>first[]] }}}
   last={{{ [enlist<tids>last[]] }}}
   here={{{ [<navstate>get[text]] ~[<first>] }}}
   prev={{{ [enlist<tids>before<here>] }}}
   next={{{ [enlist<tids>after<here>] }}}>
<<navbar>><$transclude $tiddler=<<here>> $mode=block/><<navbar>>
</$let>
</$set>

Thanks to everyone for your advice and help!

Edit: After using my original code for a bit, I realised that when navigating using the bottom controls I wanted the view to scroll back up to the top. I’ve updated my original code to add button actions to implement that.

2 Likes

This should be the final revision. The main change is to make it a single parameterised and reusable procedure. I also didn’t like the current tiddler title in both navbars, so made it top only.

\procedure linearnav()
\procedure navbar()
\procedure btnaction() <$action-navigate $to=<<currentTiddler>>/>
<div>
@@float:left;
<$button actions=<<btnaction>> set=<<navstate>> setTo=<<first>>>{{$:/core/images/chevron-left}}</$button>
<$button actions=<<btnaction>> set=<<navstate>> setTo=<<prev>> disabled={{{ [<prev>is[tiddler]then[no]else[yes]] }}}>{{$:/core/images/left-arrow}} <$view tiddler=<<prev>> field=caption><$text text=<<prev>>/></$view></$button>
@@
@@float:right;
<$button actions=<<btnaction>> set=<<navstate>> setTo=<<next>> disabled={{{ [<next>is[tiddler]then[no]else[yes]] }}}><$view tiddler=<<next>> field=caption><$text text=<<next>>/></$view> {{$:/core/images/right-arrow}}</$button>
<$button actions=<<btnaction>> set=<<navstate>> setTo=<<last>>>{{$:/core/images/chevron-right}}</$button>
@@
</div>
\end navbar
<$parameters navfilter=`$(navfilter)$` navstate=`$(navstate)$`>
<$set name=tids filter=<<navfilter>>>
<$let
   first={{{ [enlist<tids>first[]] }}}
   last={{{ [enlist<tids>last[]] }}}
   here={{{ [<navstate>get[text]] :else[<first>] }}}
   prev={{{ [enlist<tids>before<here>] }}}
   next={{{ [enlist<tids>after<here>] }}}>
<<navbar>>
<h2 class="tc-title" style="clear:both;text-align:center"><$link to=<<here>>/></h2>
<$transclude $tiddler=<<here>> $mode=block/>
<<navbar>>
<div style="clear:both"></div>
</$let>
</$set>
</$parameters>
\end linearnav

<<linearnav "[tag[HelloThere]]" "$:/temp/navstate">>
1 Like

I’ve now put my procedure online, for easy drag-and-dropping:

Any further updates will be made there.

1 Like

Some minor code suggestions:

  • set disabled=... for first and last buttons and simplify disabled filter syntax by omitting “then[no]” since “no” is the default:
<$button ... setTo=<<first>> disabled={{{ [<prev>match[]then[yes]] }}}>
<$button ... setTo=<<prev>>  disabled={{{ [<prev>match[]then[yes]] }}}>
<$button ... setTo=<<next>>  disabled={{{ [<next>match[]then[yes]] }}}>
<$button ... setTo=<<last>>  disabled={{{ [<next>match[]then[yes]] }}}>
  • use \procedure linearnav(navfilter,navstate) to declare parameters instead of $parameters widget
  • use <div style="clear:both;"/> instead of <div style="clear:both"></div>

-e

  • Re disabled, thanks, I’ll look into that.
  • Re procedure parameters, I did it that way because I found that to be a flexible way to pass values prior to the procedure call by using a $let. See my Shields macro at Andrew's TiddlyWiki — Plugins and Macros specifically the tiddler count one.
  • Re div element, I disagree. As per point 6 at HTML Standard, a div element is not a void element.