Skip to content

Commit

Permalink
Add support for list-template and list-empty widgets for specifying l…
Browse files Browse the repository at this point in the history
…ist widget templates (#7784)

Cherry picked from #7710
  • Loading branch information
Jermolene committed Oct 14, 2023
1 parent 7726982 commit 4c9c85a
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 10 deletions.
58 changes: 49 additions & 9 deletions core/modules/widgets/list.js
Expand Up @@ -60,13 +60,16 @@ ListWidget.prototype.render = function(parent,nextSibling) {
Compute the internal state of the widget
*/
ListWidget.prototype.execute = function() {
var self = this;
// Get our attributes
this.template = this.getAttribute("template");
this.editTemplate = this.getAttribute("editTemplate");
this.variableName = this.getAttribute("variable","currentTiddler");
this.counterName = this.getAttribute("counter");
this.storyViewName = this.getAttribute("storyview");
this.historyTitle = this.getAttribute("history");
// Look for <$list-template> and <$list-empty> widgets as immediate child widgets
this.findExplicitTemplates();
// Compose the list elements
this.list = this.getTiddlerList();
var members = [],
Expand All @@ -85,18 +88,48 @@ ListWidget.prototype.execute = function() {
this.history = [];
};

ListWidget.prototype.findExplicitTemplates = function() {
var self = this;
this.explicitListTemplate = null;
this.explicitEmptyTemplate = null;
var searchChildren = function(childNodes) {
$tw.utils.each(childNodes,function(node) {
if(node.type === "list-template") {
self.explicitListTemplate = node.children;
} else if(node.type === "list-empty") {
self.explicitEmptyTemplate = node.children;
} else if(node.type === "element" && node.tag === "p") {
searchChildren(node.children);
}
});
};
searchChildren(this.parseTreeNode.children);
}

ListWidget.prototype.getTiddlerList = function() {
var limit = $tw.utils.getInt(this.getAttribute("limit",""),undefined);
var defaultFilter = "[!is[system]sort[title]]";
return this.wiki.filterTiddlers(this.getAttribute("filter",defaultFilter),this);
var results = this.wiki.filterTiddlers(this.getAttribute("filter",defaultFilter),this);
if(limit !== undefined) {
if(limit >= 0) {
results = results.slice(0,limit);
} else {
results = results.slice(limit);
}
}
return results;
};

ListWidget.prototype.getEmptyMessage = function() {
var parser,
emptyMessage = this.getAttribute("emptyMessage","");
// this.wiki.parseText() calls
// new Parser(..), which should only be done, if needed, because it's heavy!
if (emptyMessage === "") {
return [];
emptyMessage = this.getAttribute("emptyMessage");
// If emptyMessage attribute is not present or empty then look for an explicit empty template
if(!emptyMessage) {
if(this.explicitEmptyTemplate) {
return this.explicitEmptyTemplate;
} else {
return [];
}
}
parser = this.wiki.parseText("text/vnd.tiddlywiki",emptyMessage,{parseAsInline: true});
if(parser) {
Expand All @@ -122,12 +155,19 @@ ListWidget.prototype.makeItemTemplate = function(title,index) {
if(template) {
templateTree = [{type: "transclude", attributes: {tiddler: {type: "string", value: template}}}];
} else {
// Check for child nodes of the list widget
if(this.parseTreeNode.children && this.parseTreeNode.children.length > 0) {
templateTree = this.parseTreeNode.children;
} else {
// Check for a <$list-item> widget
if(this.explicitListTemplate) {
templateTree = this.explicitListTemplate;
} else if (!this.explicitEmptyTemplate) {
templateTree = this.parseTreeNode.children;
}
}
if(!templateTree) {
// Default template is a link to the title
templateTree = [{type: "element", tag: this.parseTreeNode.isBlock ? "div" : "span", children: [{type: "link", attributes: {to: {type: "string", value: title}}, children: [
{type: "text", text: title}
{type: "text", text: title}
]}]}];
}
}
Expand Down
@@ -0,0 +1,29 @@
title: ListWidget/WithExplicitTemplates
description: List widget with explicit templates
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]

+
title: Output

\whitespace trim

\procedure test(filter)
<$list filter=<<filter>>>
<$list-template>
<$text text=<<currentTiddler>>/>
</$list-template>
<$list-empty>
None!
</$list-empty>
</$list>
\end

<<test "1 2 3">>

<<test "">>

+
title: ExpectedResult

<p>123</p><p>None!</p>
@@ -0,0 +1,32 @@
title: ListWidget/WithExplicitTemplatesInBlockMode
description: List widget with explicit templates in block mode
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]

+
title: Output

\whitespace trim

\procedure test(filter)
<$list filter=<<filter>>>

<$list-template>
<$text text=<<currentTiddler>>/>
</$list-template>

<$list-empty>
None!
</$list-empty>

</$list>
\end

<<test "1 2 3">>

<<test "">>

+
title: ExpectedResult

123None!
@@ -0,0 +1,33 @@
title: ListWidget/WithExplicitTemplatesOverriddenByAttributes
description: List widget with explicit templates
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]

+
title: Output

\whitespace trim

\procedure test(filter)
<$list filter=<<filter>> emptyMessage="Zero" template="Template">
<$list-template>
<$text text=<<currentTiddler>>/>
</$list-template>
<$list-empty>
None!
</$list-empty>
</$list>
\end

<<test "1 2 3">>

<<test "">>

+
title: Template

<$text text=<<currentTiddler>>/><$text text=<<currentTiddler>>/>
+
title: ExpectedResult

<p>112233</p><p>Zero</p>
25 changes: 25 additions & 0 deletions editions/test/tiddlers/tests/data/list-widget/WithLimit.tid
@@ -0,0 +1,25 @@
title: ListWidget/WithLimit
description: List widget with limit
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]

+
title: Output

Zero: <$list filter="1 2 3 4" limit="0" template="Template"/>

One: <$list filter="1 2 3 4" limit="1" template="Template"/>

Two: <$list filter="1 2 3 4" limit="2" template="Template"/>

Minus Two: <$list filter="1 2 3 4" limit="-2" template="Template"/>

+
title: Template

<$text text=<<currentTiddler>>/>
+
title: ExpectedResult

<p>Zero: </p><p>One: 1</p><p>Two: 12</p><p>Minus Two: 34
</p>
@@ -0,0 +1,26 @@
title: ListWidget/WithMissingTemplate
description: List widget with explicit templates
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]

+
title: Output

\whitespace trim

\procedure test(filter)
<$list filter=<<filter>>>
<$list-empty>
None!
</$list-empty>
</$list>
\end

<<test "1 2 3">>

<<test "">>

+
title: ExpectedResult

<p><span><a class="tc-tiddlylink tc-tiddlylink-missing" href="#1">1</a></span><span><a class="tc-tiddlylink tc-tiddlylink-missing" href="#2">2</a></span><span><a class="tc-tiddlylink tc-tiddlylink-missing" href="#3">3</a></span></p><p>None!</p>
5 changes: 4 additions & 1 deletion editions/tw5.com/tiddlers/widgets/ListWidget.tid
@@ -1,6 +1,6 @@
caption: list
created: 20131024141900000
modified: 20230725203601441
modified: 20230831182949930
tags: Widgets Lists
title: ListWidget
type: text/vnd.tiddlywiki
Expand Down Expand Up @@ -70,6 +70,8 @@ See GroupedLists for how to generate nested and grouped lists using the ListWidg

The content of the `<$list>` widget is an optional template to use for rendering each tiddler in the list.

<<.from-version "5.3.2">> If the widgets `<$list-template>` or `<$list-empty>` are found as immediate children of the <<.wid "ListWidget">> widget then the content of those widgets are used as the list item template and/or the empty template. Note that the <<.attr "emptyMessage">> and <<.attr "template">> attributes take precedence if they are present.

The action of the list widget depends on the results of the filter combined with several options for specifying the template:

* If the filter evaluates to an empty list, the text of the ''emptyMessage'' attribute is rendered, and all other templates are ignored
Expand All @@ -79,6 +81,7 @@ The action of the list widget depends on the results of the filter combined with

|!Attribute |!Description |
|filter |The [[tiddler filter|Filters]] to display |
|limit |<<.from-version "5.3.2">> Optional numeric limit for the number of results that are returned. Negative values will return the results from the end of the list |
|template |The title of a template tiddler for transcluding each tiddler in the list. When no template is specified, the body of the ListWidget serves as the item template. With no body, a simple link to the tiddler is returned. |
|editTemplate |An alternative template to use for [[DraftTiddlers|DraftMechanism]] in edit mode |
|variable |The name for a [[variable|Variables]] in which the title of each listed tiddler is stored. Defaults to ''currentTiddler'' |
Expand Down

1 comment on commit 4c9c85a

@vercel
Copy link

@vercel vercel bot commented on 4c9c85a Oct 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

tiddlywiki5 – ./

tiddlywiki5.vercel.app
tiddlywiki5-git-master-jermolene.vercel.app
tiddlywiki5-jermolene.vercel.app

Please sign in to comment.