diff --git a/core/modules/widgets/list/list.js b/core/modules/widgets/list/list.js
index 94d0769a9..06e42fb6c 100644
--- a/core/modules/widgets/list/list.js
+++ b/core/modules/widgets/list/list.js
@@ -166,8 +166,16 @@ Remove a list element from the list, along with the attendant DOM nodes
ListWidget.prototype.removeListElement = function(index) {
// Get the list element
var listElement = this.children[index];
- // Remove the DOM node
- listElement.domNode.parentNode.removeChild(listElement.domNode);
+ // Invoke the listview to animate the removal
+ if(this.listview && this.listview.remove) {
+ if(!this.listview.remove(index)) {
+ // Only delete the DOM element if the listview.remove() returned false
+ listElement.domNode.parentNode.removeChild(listElement.domNode);
+ }
+ } else {
+ // Always remove the DOM node if we didn't invoke the listview
+ listElement.domNode.parentNode.removeChild(listElement.domNode);
+ }
// Then delete the actual renderer node
this.children.splice(index,1);
};
@@ -266,6 +274,10 @@ ListWidget.prototype.handleListChanges = function(changedTiddlers) {
// The list element isn't there, so we need to insert it
this.children.splice(t,0,this.renderer.renderTree.createRenderer(this.renderer.renderContext,this.createListElement(this.list[t])));
this.renderer.domNode.insertBefore(this.children[t].renderInDom(),this.renderer.domNode.childNodes[t]);
+ // Ask the listview to animate the insertion
+ if(this.listview && this.listview.insert) {
+ this.listview.insert(t);
+ }
} else {
// Delete any list elements preceding the one we want
for(var n=index-1; n>=t; n--) {
diff --git a/core/modules/widgets/list/listviews/classic.js b/core/modules/widgets/list/listviews/classic.js
new file mode 100644
index 000000000..b2d0e5ce6
--- /dev/null
+++ b/core/modules/widgets/list/listviews/classic.js
@@ -0,0 +1,95 @@
+/*\
+title: $:/core/modules/widgets/list/listviews/classic.js
+type: application/javascript
+module-type: listview
+
+Views the list as a linear sequence
+
+\*/
+(function(){
+
+/*jslint node: true, browser: true */
+/*global $tw: false */
+"use strict";
+
+var ClassicListView = function(listWidget) {
+ this.listWidget = listWidget;
+}
+
+ClassicListView.prototype.navigateTo = function(historyInfo) {
+ var listElementIndex = this.listWidget.findListElementByTitle(0,historyInfo.title),
+ listElementNode = this.listWidget.children[listElementIndex],
+ targetElement = listElementNode.domNode;
+ // Scroll the node into view
+ var scrollEvent = document.createEvent("Event");
+ scrollEvent.initEvent("tw-scroll",true,true);
+ targetElement.dispatchEvent(scrollEvent);
+};
+
+ClassicListView.prototype.insert = function(index) {
+ var listElementNode = this.listWidget.children[index],
+ targetElement = listElementNode.domNode;
+ // Get the current height of the tiddler
+ var currHeight = targetElement.offsetHeight + parseInt(window.getComputedStyle(targetElement).marginTop,10);
+ // Reset the margin once the transition is over
+ var transitionEventName = $tw.utils.convertEventName("transitionEnd");
+ targetElement.addEventListener(transitionEventName,function handler(event) {
+ $tw.utils.setStyle(targetElement,[
+ {transition: "none"},
+ {transform: "none"},
+ {marginBottom: ""}
+ ]);
+ targetElement.removeEventListener(transitionEventName,handler,false);
+ },false);
+ // Set up the initial position of the element
+ $tw.utils.setStyle(targetElement,[
+ {transition: "none"},
+ {marginBottom: (-currHeight) + "px"},
+ {opacity: "0.0"}
+ ]);
+ $tw.utils.forceLayout(targetElement);
+ // Transition to the final position
+ $tw.utils.setStyle(targetElement,[
+ {transition: "opacity " + $tw.config.preferences.animationDurationMs + " ease-in-out, " +
+ "margin-bottom " + $tw.config.preferences.animationDurationMs + " ease-in-out"},
+ {transform: "rotateX(0deg)"},
+ {marginBottom: "0px"},
+ {opacity: "1.0"}
+ ]);
+};
+
+ClassicListView.prototype.remove = function(index) {
+ var listElementNode = this.listWidget.children[index],
+ targetElement = listElementNode.domNode;
+ // Get the current height of the tiddler
+ var currWidth = targetElement.offsetWidth,
+ currHeight = targetElement.offsetHeight + parseInt(window.getComputedStyle(targetElement).marginTop,10);
+ // Attach an event handler for the end of the transition
+ targetElement.addEventListener($tw.utils.convertEventName("transitionEnd"),function(event) {
+ if(targetElement.parentNode) {
+ targetElement.parentNode.removeChild(targetElement);
+ }
+ },false);
+ // Animate the closure
+ $tw.utils.setStyle(targetElement,[
+ {transition: "none"},
+ {transform: "translateX(0px)"},
+ {marginBottom: "0px"},
+ {opacity: "1.0"}
+ ]);
+ $tw.utils.forceLayout(targetElement);
+ $tw.utils.setStyle(targetElement,[
+ {transition: $tw.utils.roundTripPropertyName("transform") + " " + $tw.config.preferences.animationDurationMs + " ease-in-out, " +
+ "opacity " + $tw.config.preferences.animationDurationMs + " ease-in-out, " +
+ "margin-bottom " + $tw.config.preferences.animationDurationMs + " ease-in-out"},
+ {transform: "translateX(" + currWidth + "px)"},
+ {marginBottom: (-currHeight) + "px"},
+ {opacity: "0.0"}
+ ]);
+ // Returning true causes the DOM node not to be deleted
+ return true;
+};
+
+exports.classic = ClassicListView;
+
+})();
diff --git a/core/templates/PageTemplate.tid b/core/templates/PageTemplate.tid
index f5c9b59b2..a412cd697 100644
--- a/core/templates/PageTemplate.tid
+++ b/core/templates/PageTemplate.tid
@@ -8,7 +8,7 @@ title: $:/templates/PageTemplate
{{SiteTitle}}.title
{{SiteSubtitle}}
-<$list filter="[list[$:/StoryList]]" history="$:/HistoryList" listview=classic itemClass="tw-menu-list-item"/>
+<$list filter="[list[$:/StoryList]]" history="$:/HistoryList" itemClass="tw-menu-list-item"/>