TiddlyWiki5
TiddlyWiki5 copied to clipboard
[IDEA] List widget allows for optional header and/or footer, shown only if list is non-empty
Is your feature request related to a problem? Please describe. I often want to show lists preceded by some kind of header, but only show the header if the list is non-empty. E.g., in a tiddler that represents one task on a todo list, I might want it to look like this:
Task Name Task description, with some details of what it involves
Subtasks
- Link to first subtask tiddler
- Link to second subtask tiddler
The list of subtasks is presented by a $list
widget, drawing from the subtasks
field of the task tiddler. If the subtasks
field is missing or empty, then I'd like to not show the Subtasks header at all. It would be nice if I could do this entirely within the $list
widget, so that the "Subtasks" section of my todo template could be kept simple.
Describe the solution you'd like
The $list
widget would acquire two new attributes, possibly named header
and footer
(though I've also considered prefix
and suffix
as possible names). Their contents would be wikitext that would be rendered before and after the list, respectively. If the list's filter resolves to empty output (so that the emptyMessage
attribute is rendered instead) then the header and footer are not rendered. (If the user wants the header to always be rendered whether or not the list is empty, it's trivial to move that text to just before the list widget, or to include it as part of the emptyMessage
).
It might also be a good idea to have attributes called headerTemplate
and footerTemplate
, to parallel the template
and editTemplate
attributes that already exist. Those templates would be rendered with the currentTiddler value being unchanged (i.e., it's probably the tiddler that contains the $list
widget), just as if they were a normal transclusion. I haven't decided yet if both headerTemplate
and header
should be allowed as attributes, or if just one would be better. If both were allowed, we would need to decide which one "wins" if both are present and non-empty; I'd be inclined to say that headerTemplate
wins over header
, to parallel how other widgets tend to work (e.g., how the $set
widget has the tiddler
attribute trump the value
attribute if both are set).
Describe alternatives you've considered The way to achieve this right now would be to use a combination of widgets, such as:
<$vars header={{{ [list[!!subtasks]then[''Subtasks'']] }}}>
<<header>>
<$list filter="[list[!!subtasks]]" template="SubtaskTemplate" />
</$vars>
But I'd rather write that as:
<$list filter="[list[!!subtasks]]" template="SubtaskTemplate" header="''Subtasks''" />
Related #2202 - Add preMessage parameter to listwidget
Thx Mat. I knew there has been a discussion somewhere. ... You saved me from writing my concerns again.
I think the problem is, that the OP is a relatively specialized usecase, which from my point of view, isn't "best practice". I'm no fan of "hidden information". So if there is a possibility of "Subtasks" the user should be aware, that there are no subtasks. ... Hiding the "header" shouldn't be an option here. ... But that's only my point of view.
Thanks @rmunn, that's very clear. To answer your question, I think one would indeed need both headerTemplate
and header
for consistency.
However, there are some downsides with this approach. The first is that it doesn't work well for the usecase you mention of an optional heading before a list. The trouble is that we would normally wrap the <ul>
or <ol>
around the list widget, and so the heading would end up within the list.
The other downside is that it makes the list widget even more complex. It was added quite early in TW5's development to get the story river up and running. There is a lot of "magic" in the implementation (e.g. having separate edit and view templates) that we would handle differently now that we have so much more flexibility.
One interesting proposal we had in the past was to add counter variables to the list widget -- see https://github.com/Jermolene/TiddlyWiki5/pull/1330#issuecomment-74339195 and the later discussion at https://github.com/Jermolene/TiddlyWiki5/issues/3384, which would provide an alternate means to support the OP. I made a later proposal in https://github.com/Jermolene/TiddlyWiki5/issues/1523, but I think now I would advocate a new widget <$for>
that is a stripped down list widget with support index counters.
I think now I would advocate a new widget
<$for>
that is a stripped down list widget with support index counters.
+1
I would rather introduce a new widget than add more complexity to the <$list>
widget. The extra optimization for refresh performance in the list widget is useful in situations outside of the story river as well when dealing with larger data sets, and should not be compromised.
I agree about a new widget, and the coder in me wants to call it foreach instead of for, but I don't know if that would be clearer for most people.
Solving the OT and related issues would stand to propel tiddlywiki to wards a 4th Generation report language.
I believe there is now a way to obtain an index value, However it is not so efficient; You must test the original filter every time.
<$list filter="[tag[TableOfContents]]">
{{{ [tag[TableOfContents]allbefore<currentTiddler>count[]add[1]] }}}. <$link/><br>
</$list><br>
Finding a list item of 1 would allow the list widget contents (or template) to trigger a heading. The only way to detect the footer would be first to count the number of items and test for the last.
Perhaps a simple solution would be for the list widget to have a built in count, ie given the filter result +[count[]]
the number of items, then use this to determine the last, and this be able to test if a footer is needed. eg; itemsVariable=total
then gives the variable <<total>>
inside the list widget/template (calculated only if asked for)
Not withstanding the above given the list widgets overloading perhaps if a new widget was to be written for more general purposes perhaps it could be a reportWidget including a total item count, header and footer triggers and an item count incremented for each item listed. An emptyReport=<<macroname>>
would be used to present both headers and footers with the emptyReport message when empty. eg "No items", emptyMessage could do as current.
The idea of a reportWidget is in keeping with tiddlywikis excellent list handling facilities and could include multiple field based accumulators, like the new Reduce Operator. We could even introduce break points eg on change in fieldname value "display this" eg on newState.
I would be happy to prepare design requirements if someone can help build it.
Tones
Post script,
Other page handling features would also be appropriate inside a reportWidget that may not be valid in the listWidget.
Eg; variable that exist only within the report widget, that can be reset according to conditions in the report widget.
Tones
Perhaps a simple solution would be for the list widget to have a built in count
I think that is what is discussed over at #3384.
@rmunn has the requirement been met by the introduction of the counter and other new variables in the listWidget?
It's almost met, but not quite. I want to be able to produce something like the following:
<b>Header</b>
<ul>
<li>One</li>
<li>Two</li>
</ul>
<b>Footer</b>
Which renders as:
Header
- One
- Two
But if the list is empty, I want "Header" and "Footer" to be completely absent. I don't think I can produce this HTML with the list widget's new counter-first and counter-last variables. I'd like to do something like this:
<$list filter="One Two" variable="item">
<$if filter="[<counter-first>match[true]]">
<b>Header</b>
<ul>
</$if>
<li><<item>></li>
<$if filter="[<counter-last>match[true]]">
</ul>
<b>Footer</b>
</$if>
</$list>
But 1) there's no <$if>
widget that takes the result of a filter and shows or hides the contents based on the filter (the <$reveal>
widget uses state fields to track the hidden/revealed state, not filters, and setting a state field inside the <$list>
widget would be a headache worse than the <$vars>
workaround I've come up with). And 2) even if there was an <$if>
widget like that, I don't know if the HTML fragments inside that <$if>
widget would assemble properly into a syntactically valid tree, or whether the unmatched <ul>
in the first <$if>
block would end up causing some sort of error.
There is an if, it's named <$list
or <$reveal
<$list filter="[tag[About]sort[title]]" counter="counter">
<$list filter="[<counter-first>match[yes]]" >
header list no surrounding html element<br>
</$list>
<$reveal type=nomatch text={{{ [<counter-first>match[yes]] }}} >
header reveal surrounded by SPAN - see: docs!<br>
</$reveal>
''<$text text=<<currentTiddler>>/>''<br>
</$list>
The only problem is the performance hit.
@Jermolene ... Would the Widget.prototype.getStateQualifier = function(name) {
mechanism be able to count, without a performance hit?
just an idea
Hi @rmunn @saqimtiaz the subtlety is that these header and footer entries need to be outside the <ol>/<ul>
of a list. That's the clue that these headers and footers can't be part of the list widget, but need to be a feature wrapped around it.
Would the
Widget.prototype.getStateQualifier = function(name) {
mechanism be able to count, without a performance hit?
No, for the same reason: the values of variables can only be changed by re-rendering their child widgets, which wouldn't happen if we were moving nodes around.
<$list filter="One Two" variable="item"> <$if filter="[<counter-first>match[true]]"> <b>Header</b> <ul> </$if> <li><<item>></li> <$if filter="[<counter-last>match[true]]"> </ul> <b>Footer</b> </$if> </$list>
I think these can be simply addressed by a macro similar to list-links. There are cases like list-search (see Shiraz) where it creates a list of items from given tiddler plus a search box at the top! So, it seems simple macros with current $list widget can cover these use cases! Just my 2 cents!
This should become possible to implement myself once #6666 is implemented: I'll be able to write a $$headed-list
widget looking something like this:
\widget $$headed-list(filter, header)
<$list filter="[enlist<filter>count[]compare:number:gt[0]]"><<header>></$list>
<$list filter=<<filter>>><$slot $name="ts-body"></$slot></$list>
\end
Edit: And with the $genesis
widget added by #6666, I can even make sure that the <ul>
element is added in the correct place:
\widget $$headed-list(filter, header, elem:"ul")
<$list filter="[enlist<filter>count[]compare:number:gt[0]]"><<header>></$list>
<$genesis $tag=<<elem>>>
<$list filter=<<filter>>><$slot $name="ts-body"></$slot></$list>
</$genesis>
\end
Usage:
<$$headed-list filter="one two three" header="<h2>Foo</h2>">
<li><<currentTiddler>></li>
</$$headed-list>
This makes the header stay outside of the <ul>
element. And adding elem="ol"
to my $$headed-list
invocation creates a numbered list instead of bulleted. All very nice, and exactly what I wanted.
So once #6666 is merged, I'll close this issue as I'll be able to implement this feature myself without needing to change core TW.
I would not say that the there is no value in header and footer features however I have already made a working solution by using the counter variable.
But first I use the filter to be used for the list and set a total
- total is also available for use in the header/footer
<$set name=total filter="$filter% +[count][]">
<$list filter="$filter$" counter=item>
<$list filter="[<item>match[1]]">Heading for <<total>> items</$list>
each item
<$list filter="[<item>match<total>]">Footer</$list>
</$list>
</$set>
above code not actually run.
Now that #6666 has been merged and released, it's time to close this feature request as it's possible to do this myself now.
Plus, I'm currently working on PR https://github.com/Jermolene/TiddlyWiki5/pull/7691 that adds an <$if>
widget, similar to <$reveal>
but using the result of a filter. So I could implement that "only print header if list is non-empty" feature even more easily now:
\define my-filter()
[all[shadows+tiddlers]tag[todo]!has[draft.of]]
\end
<$if filter=<<my-filter>> >
<$then>
<h2>Todo items</h2>
<ul>
<$list filter=<<my-filter>> >
<li><$link /></li>
</$list>
</ul>
</$then>
</$if>
That's perfectly adequate to satisfy me, and looks nice and clean and readable too. Closing this feature request as now TW can do what I wanted.
@rmunn FWIW, I just published another method some weeks ago, relying on CSS trickery:
https://liststyles.tiddlyhost.com/#:listheader%20About
I didn't announce it publicly because.... well, I don't expect anyone will use it anyway.
I am yet to publish it but I have resolved this with a custom widget.
Happy to share.