Livetable Pagination Size

Last modified by Vincent Massol on 2024/02/26 17:57

 XWiki
 Design
 Completed
 

Description

Community Feedback

Discussed: Support choosing pagination size in livetable UI: http://xwiki.markmail.org/thread/3sychusbsijj2jny 

Description

Currently the number of rows displayed in a livetable is fixed at creation time.

I propose to add an UI controls (currently select) that allows to change the pagination size freely.

This patch is both a change in livetable.js to support these controls (one or more), and a change in the livetable macro to allow displaying them.

#livetable macro changes improvements

The #livetable macro default to display a pageSize control. It a select control placed where the ajax-loader bar is located. It is hidden during loading, so that it swaps with the ajax-loader bar. The control is prefixed by a label "per page of". (see screenshots below)

You can disable this new feature by setting options.pageSize to false.

A new options.pageSizeBounds, that should be a valid code to initialize an array of 3 numbers (not sure this is the best option, you may have your own ideas). If this options is present, the necessary markup is added to the table. 

Livetable.js improvements

The added feature work similarly to the pagination feature. It defaults to add its controls in nodes styled ".xwiki-livetable-pagesize-content" under nodes styled ".xwiki-livetable-pagesize" which can be customized with options.pageSizeNodes.

The added controls are select nodes styled ".pagesizeselect". These select nodes contains options from options.pageSizeBounds[0] (defaults to 10) to options.pageSizeBounds[1] (default to 100), by step of options.pageSizeBounds[2]  (default to 10). If the current size is not in these options, it is also inserted properly between the correct steps.

When an options is selected by the user, the livetable cache is cleared, and the user returned to the first page with the new pagination in place (this could be improved, not so easy however, I am investigating).

Colateral improvements

To support hiding the control during loading, I had to add a loadingComplete custom even on the livetable. My feeling is that is was missing, since it was impossible to pair the loadingEntries events in case of failure. I have also adapted the livetable stylesheet for proper display and also improve the display when no pagesize select is used.

Screenshots

screenshot01.png

screenshot02.png

screenshot03.png

screenshot04.png

Current Patch

Index: web/standard/src/main/webapp/resources/js/xwiki/table/livetable.js
===================================================================
--- web/standard/src/main/webapp/resources/js/xwiki/table/livetable.js (revision 28668)
+++ web/standard/src/main/webapp/resources/js/xwiki/table/livetable.js (revision )
@@ -60,10 +60,13 @@
       || $(options.filtersNode)  // Deprecated option (kept for backward compatibility)
        || $(domNodeName).down(".xwiki-livetable-display-filters") // Default filter node when none precised
     ].flatten().compact();
-    
+
    // Array of nodes under which pagination for this livetable will be displayed.
     this.paginationNodes = options.paginationNodes || $(this.domNodeName).select(".xwiki-livetable-pagination");

+     // Array of nodes under which a page size control will be displayed
+    this.pageSizeNodes = options.pageSizeNodes || $(this.domNodeName).select(".xwiki-livetable-pagesize");
+    
    if (typeof options == "undefined") {
        options = {};
     }
@@ -73,6 +76,11 @@

     this.permalinks = options.permalinks || true;

+    // Initialize page size control bounds
+    if (typeof this.pageSizeNodes != "undefined") {
+      this.pageSizer = new LiveTablePageSizer(this, this.pageSizeNodes, options.pageSizeBounds, this.limit);
+    }
+
    // Initialize pagination
     if (typeof this.paginationNodes != "undefined") {
        this.paginator = new LiveTablePagination(this, this.paginationNodes, options.maxPages || 10);
@@ -106,8 +114,19 @@
    // Show initial rows
     this.showRows(this.currentOffset, this.limit);
   },
-
+  
  /**
+   * Set the page size of the table and refresh the display
+   * @param pageSize The new maximum number of rows to display per page
+   **/
+  setPageSize: function(pageSize)
+  {
+    this.limit = pageSize;
+    this.clearCache();
+    this.showRows(1, parseInt(this.limit));
+  },
+
+  /**
   * Re-write location hash with current page and filters values
    */
   updateLocationHash: function()
@@ -225,6 +244,17 @@
    {
       method: 'get',
       onComplete: function( transport ) {
+        // Let code know loading is finished
+        // 1. Named event (for code interested by that table only)
+        document.fire("xwiki:livetable:" + self.domNodeName + ":loadingComplete", {
+          "status" : transport.status
+        });
+        // 2. Generic event (for code potentially interested in any livetable)
+        document.fire("xwiki:livetable:loadingComplete", {
+          "status" : transport.status,
+          "tableId" : self.domNodeName
+        });
+
        self.loadingStatus.addClassName("hidden");
       },

@@ -573,7 +603,67 @@
    }
 });

+/**
+ * Helper class to display the page size control
+ **/
+ var LiveTablePageSizer = Class.create({
+   /**
+    * Create a new instance
+    * @param table The LiveTable
+    * @param domNodes An array of nodes indicating where the controls will be created
+    * @param pageSizeBounds The bounds specification for acceptable values (an array of min, max, step)
+    * @param currentPageSize The currently selected page size.
+    **/
+  initialize: function(table, domNodes, pageSizeBounds, currentPageSize) {
+    this.table = table;
+    this.currentValue = currentPageSize;
+    var bounds = pageSizeBounds || [];
+    this.startValue = bounds[0] || 10;
+    this.step = bounds[2] || 10;
+    this.maxValue = bounds[1] || 100;
-
+    
+    var self = this;
+    this.pageSizeNodes = [];
+    domNodes.each(function(elem) {
+      self.pageSizeNodes.push(elem.down(".xwiki-livetable-pagesize-content"));
+    });
+    
+    this.pageSizeNodes.each(function(elem) {
+      elem.insert(self.createPageSizeSelectControl());
+    });
+  },
+  
+  /**
+   * Create the page size control using a select node and returns it
+   * @return an Element containing the select
+   **/
+  createPageSizeSelectControl: function() {
+    var select = new Element('select', {'class':'pagesizeselect'});
+    for (var i=this.startValue; i<=this.maxValue; i += this.step) {
+      var attrs = {'value':i, 'text':i}
+      if (i == this.currentValue)
+        attrs.selected = true;
+      else {
+        var prevStep = i - this.step;
+        if (this.currentValue > prevStep && this.currentValue < i) {
+          select.appendChild(new Element('option', {'value':this.currentValue, 'text':this.currentValue, selected:true}).update(this.currentValue));
+        }
+      }
+      select.appendChild(new Element('option', attrs).update(i));
+    }
+    select.observe("change", this.changePageSize.bind(this));
+    return select;
+  },
+  
+  /**
+   * Change the page size of the table
+   **/
+  changePageSize: function(event) {
+    var newLimit =  parseInt($F(Event.element(event)));
+    this.table.setPageSize(newLimit);
+  }
+});
+
/*
  * The class that deals with the filtering in a table
  */
@@ -879,4 +969,3 @@
}

 })();
-
Index: web/standard/src/main/webapp/resources/js/xwiki/table/livetable.css
===================================================================
--- web/standard/src/main/webapp/resources/js/xwiki/table/livetable.css (revision 28561)
+++ web/standard/src/main/webapp/resources/js/xwiki/table/livetable.css (revision )
@@ -8,13 +8,19 @@
}
 .xwiki-livetable-loader , .xwiki-livetable-loader img {
   float: left;
+  line-height: 22px;
}
 .xwiki-livetable-pagination .xwiki-livetable-limits {
   float: left;
+  line-height: 22px;
}
 .xwiki-livetable-pagination .xwiki-livetable-limits strong {
   font-weight: normal;
 }
+.xwiki-livetable-pagination .xwiki-livetable-pagesize {
+  line-height: 22px;
+  margin-left: 5px;
+}
.xwiki-livetable-pagination .xwiki-livetable-pagination-content, .xwiki-livetable-pagination .xwiki-livetable-pagination-text {
   display: inline;
 }
@@ -37,6 +43,7 @@
.pagination {
   float: right;
   margin-right: 10px;
+  line-height: 22px;
}
 .controlPagination {
   position: relative;
Index: web/standard/src/main/webapp/templates/macros.vm
===================================================================
--- web/standard/src/main/webapp/templates/macros.vm (revision 28545)
+++ web/standard/src/main/webapp/templates/macros.vm (revision )
@@ -1521,6 +1521,7 @@
  #if("$!options.tagCloud" == '' || $options.tagCloud == false) #set($tagcloud=false) #else #set($tagcloud = true) #end
   #if("$!options.javascriptName" != '') #set($jsName = $options.javascriptName) #else #set($jsName = 'livetable') #end
   #if("$!options.topFilters" != '') #set($topfilters = $options.topFilters) #end
+  #if("$!options.pageSizeBounds" != '') #set($pageSizeBounds = $options.pageSizeBounds) #end
  #set($classname = "$!options.className")
   #set($extraparams = "$!options.extraParams")
   ##
@@ -1553,6 +1554,7 @@
  ##
 <div class="xwiki-livetable-container">
   #if("$!topfilters" !='') #set($hasTopFilters = true) #else #set($hasTopFilters = false) #end
+  #if("$!pageSizeBounds" !='') #set($hasPageSize = true) #else #set($hasPageSize = false) #end
  #if($tagcloud || $hasTopFilters)
   <div class="tipfilters">
   #end
@@ -1591,6 +1593,12 @@
    <tr>
       <td class="xwiki-livetable-pagination">
         <span id="${divid}-limits" class="xwiki-livetable-limits"></span>
+  #if($hasPageSize)
+        <span id="${divid}-pagesize" class="xwiki-livetable-pagesize">
+            <span>$msg.get('xe.livetable.pagesize.label')</span>
+            <span class="xwiki-livetable-pagesize-content" ></span>
+        </span>
+  #end
        <span id="${divid}-ajax-loader" class="xwiki-livetable-loader hidden">
           <img src="$xwiki.getSkinFile('icons/xwiki/ajax-loader-large.gif')" alt="$msg.get('xe.livetable.loading')" title="" />
         </span>
@@ -1713,14 +1721,20 @@
                         "maxPages":$maxPages
                      ,"limit":$count
      #if($hasTopFilters),"filterNodes" : [$("${divid}").down(".xwiki-livetable-display-filters"), $('${divid}-topfilters')]#end
+     #if($hasPageSize),"pageSizeBounds" : $pageSizeBounds#end
           });
   #else
     $jsName = new XWiki.widgets.LiveTable("$dataurl", "${divid}", #livetablecallback($divid $collist $colprops $transprefix), {             
                          "maxPages":$maxPages
                      ,"limit":$count
      #if($hasTopFilters),"filterNodes" : [$("${divid}").down(".xwiki-livetable-display-filters"), $('${divid}-topfilters')]#end
+     #if($hasPageSize),"pageSizeBounds" : $pageSizeBounds#end
           });
   #end
+  #if($!hasPageSize)
+    document.observe("xwiki:livetable:${divid}:loadingEntries", function() { $('${divid}-pagesize').addClassName("hidden"); } );
+    document.observe("xwiki:livetable:${divid}:loadingComplete", function() { $('${divid}-pagesize').removeClassName("hidden"); } );
+  #end
  }
    //]]>
   </script>

 

Get Connected