From 4c9c85aec50ff5b9c0dc4f3f29b76d361ed5cba1 Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Sat, 14 Oct 2023 09:31:11 +0100 Subject: [PATCH] Add support for list-template and list-empty widgets for specifying list widget templates (#7784) Cherry picked from #7710 --- core/modules/widgets/list.js | 58 ++++++++++++++++--- .../list-widget/WithExplicitTemplates.tid | 29 ++++++++++ .../WithExplicitTemplatesInBlockMode.tid | 32 ++++++++++ ...xplicitTemplatesOverriddenByAttributes.tid | 33 +++++++++++ .../tests/data/list-widget/WithLimit.tid | 25 ++++++++ .../data/list-widget/WithMissingTemplate.tid | 26 +++++++++ .../tw5.com/tiddlers/widgets/ListWidget.tid | 5 +- 7 files changed, 198 insertions(+), 10 deletions(-) create mode 100644 editions/test/tiddlers/tests/data/list-widget/WithExplicitTemplates.tid create mode 100644 editions/test/tiddlers/tests/data/list-widget/WithExplicitTemplatesInBlockMode.tid create mode 100644 editions/test/tiddlers/tests/data/list-widget/WithExplicitTemplatesOverriddenByAttributes.tid create mode 100644 editions/test/tiddlers/tests/data/list-widget/WithLimit.tid create mode 100644 editions/test/tiddlers/tests/data/list-widget/WithMissingTemplate.tid diff --git a/core/modules/widgets/list.js b/core/modules/widgets/list.js index 75592e669..39c7e1b84 100755 --- a/core/modules/widgets/list.js +++ b/core/modules/widgets/list.js @@ -60,6 +60,7 @@ 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"); @@ -67,6 +68,8 @@ ListWidget.prototype.execute = function() { 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 = [], @@ -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) { @@ -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} ]}]}]; } } diff --git a/editions/test/tiddlers/tests/data/list-widget/WithExplicitTemplates.tid b/editions/test/tiddlers/tests/data/list-widget/WithExplicitTemplates.tid new file mode 100644 index 000000000..aad322f54 --- /dev/null +++ b/editions/test/tiddlers/tests/data/list-widget/WithExplicitTemplates.tid @@ -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=<>> + <$list-template> + <$text text=<>/> + + <$list-empty> + None! + + +\end + +<> + +<> + ++ +title: ExpectedResult + +

123

None!

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/list-widget/WithExplicitTemplatesInBlockMode.tid b/editions/test/tiddlers/tests/data/list-widget/WithExplicitTemplatesInBlockMode.tid new file mode 100644 index 000000000..8e61c2e24 --- /dev/null +++ b/editions/test/tiddlers/tests/data/list-widget/WithExplicitTemplatesInBlockMode.tid @@ -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=<>> + + <$list-template> + <$text text=<>/> + + + <$list-empty> + None! + + + +\end + +<> + +<> + ++ +title: ExpectedResult + +123None! \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/list-widget/WithExplicitTemplatesOverriddenByAttributes.tid b/editions/test/tiddlers/tests/data/list-widget/WithExplicitTemplatesOverriddenByAttributes.tid new file mode 100644 index 000000000..0ce5780af --- /dev/null +++ b/editions/test/tiddlers/tests/data/list-widget/WithExplicitTemplatesOverriddenByAttributes.tid @@ -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=<> emptyMessage="Zero" template="Template"> + <$list-template> + <$text text=<>/> + + <$list-empty> + None! + + +\end + +<> + +<> + ++ +title: Template + +<$text text=<>/><$text text=<>/> ++ +title: ExpectedResult + +

112233

Zero

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/list-widget/WithLimit.tid b/editions/test/tiddlers/tests/data/list-widget/WithLimit.tid new file mode 100644 index 000000000..2f630a1dc --- /dev/null +++ b/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=<>/> ++ +title: ExpectedResult + +

Zero:

One: 1

Two: 12

Minus Two: 34 +

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/list-widget/WithMissingTemplate.tid b/editions/test/tiddlers/tests/data/list-widget/WithMissingTemplate.tid new file mode 100644 index 000000000..40fb2f07b --- /dev/null +++ b/editions/test/tiddlers/tests/data/list-widget/WithMissingTemplate.tid @@ -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=<>> + <$list-empty> + None! + + +\end + +<> + +<> + ++ +title: ExpectedResult + +

123

None!

\ No newline at end of file diff --git a/editions/tw5.com/tiddlers/widgets/ListWidget.tid b/editions/tw5.com/tiddlers/widgets/ListWidget.tid index b36d0f3bf..592185d36 100644 --- a/editions/tw5.com/tiddlers/widgets/ListWidget.tid +++ b/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 @@ -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 @@ -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'' |