From 75150d47c01e4daab5df5a9b8aeae8ed8234b56f Mon Sep 17 00:00:00 2001 From: Dan Scott Date: Mon, 16 Nov 2015 22:20:28 -0500 Subject: [PATCH] LP#1516867 Make HTML report tables sortable Using the sortable JavaScript library from https://github.com/hubspot/sortable (MIT license), we can make HTML reports much more usable by making them sortable by clicking on the column headers. This should save some downloading of CSV or Excel versions of the reports. To test: [1] Create or use a report template and generate an HTML report; ensure that the report has at most 10,000 rows of output. [2] View the report output and click on one of the column headings; verify that the table sorts itself by that column's values. Signed-off-by: Dan Scott Signed-off-by: Galen Charlton Signed-off-by: Ben Shum --- Open-ILS/src/reporter/clark-kent.pl | 12 ++-- Open-ILS/web/js/sortable/LICENSE | 19 +++++++ .../js/sortable/sortable-theme-minimal.css | 55 +++++++++++++++++++ Open-ILS/web/js/sortable/sortable.min.js | 2 + .../Administration/sortable_html_reports.adoc | 5 ++ 5 files changed, 88 insertions(+), 5 deletions(-) create mode 100644 Open-ILS/web/js/sortable/LICENSE create mode 100644 Open-ILS/web/js/sortable/sortable-theme-minimal.css create mode 100644 Open-ILS/web/js/sortable/sortable.min.js create mode 100644 docs/RELEASE_NOTES_NEXT/Administration/sortable_html_reports.adoc diff --git a/Open-ILS/src/reporter/clark-kent.pl b/Open-ILS/src/reporter/clark-kent.pl index 0e8a99b1b5..758cb2c83d 100755 --- a/Open-ILS/src/reporter/clark-kent.pl +++ b/Open-ILS/src/reporter/clark-kent.pl @@ -553,18 +553,20 @@ sub build_html { table { border-collapse: collapse; } th { background-color: lightgray; } td,th { border: solid black 1px; } - * { font-family: sans-serif; font-size: 10px; } + * { font-family: sans-serif; } + + CSS - print $raw ""; + print $raw "
"; { no warnings; - print $raw "'; - print $raw "' for (@{$r->{data}}); + print $raw "\n"; + print $raw "\n" for (@{$r->{data}}); } - print $raw '
".join('',@{$r->{column_labels}}).'
".join('',@$_ ).'
".join('', @{$r->{column_labels}})."
".join('', @$_)."
'; + print $raw ''; $raw->close; } diff --git a/Open-ILS/web/js/sortable/LICENSE b/Open-ILS/web/js/sortable/LICENSE new file mode 100644 index 0000000000..e19dfc98a6 --- /dev/null +++ b/Open-ILS/web/js/sortable/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2013 Adam Schwartz, http://adamschwartz.co + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/Open-ILS/web/js/sortable/sortable-theme-minimal.css b/Open-ILS/web/js/sortable/sortable-theme-minimal.css new file mode 100644 index 0000000000..96db3140fd --- /dev/null +++ b/Open-ILS/web/js/sortable/sortable-theme-minimal.css @@ -0,0 +1,55 @@ +/* line 2, ../sass/_sortable.sass */ +table[data-sortable] { + border-collapse: collapse; + border-spacing: 0; +} +/* line 6, ../sass/_sortable.sass */ +table[data-sortable] th { + vertical-align: bottom; + font-weight: bold; +} +/* line 10, ../sass/_sortable.sass */ +table[data-sortable] th, table[data-sortable] td { + text-align: left; + padding: 10px; +} +/* line 14, ../sass/_sortable.sass */ +table[data-sortable] th:not([data-sortable="false"]) { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + -o-user-select: none; + user-select: none; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + -webkit-touch-callout: none; + cursor: pointer; +} +/* line 26, ../sass/_sortable.sass */ +table[data-sortable] th:after { + content: ""; + visibility: hidden; + display: inline-block; + vertical-align: inherit; + height: 0; + width: 0; + border-width: 5px; + border-style: solid; + border-color: transparent; + margin-right: 1px; + margin-left: 10px; + float: right; +} +/* line 40, ../sass/_sortable.sass */ +table[data-sortable] th[data-sorted="true"]:after { + visibility: visible; +} +/* line 43, ../sass/_sortable.sass */ +table[data-sortable] th[data-sorted-direction="descending"]:after { + border-top-color: inherit; + margin-top: 8px; +} +/* line 47, ../sass/_sortable.sass */ +table[data-sortable] th[data-sorted-direction="ascending"]:after { + border-bottom-color: inherit; + margin-top: 3px; +} diff --git a/Open-ILS/web/js/sortable/sortable.min.js b/Open-ILS/web/js/sortable/sortable.min.js new file mode 100644 index 0000000000..8278f504c8 --- /dev/null +++ b/Open-ILS/web/js/sortable/sortable.min.js @@ -0,0 +1,2 @@ +/*! sortable.js 0.8.0 */ +(function(){var a,b,c,d,e,f,g;a="table[data-sortable]",d=/^-?[£$¤]?[\d,.]+%?$/,g=/^\s+|\s+$/g,c=["click"],f="ontouchstart"in document.documentElement,f&&c.push("touchstart"),b=function(a,b,c){return null!=a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent("on"+b,c)},e={init:function(b){var c,d,f,g,h;for(null==b&&(b={}),null==b.selector&&(b.selector=a),d=document.querySelectorAll(b.selector),h=[],f=0,g=d.length;g>f;f++)c=d[f],h.push(e.initTable(c));return h},initTable:function(a){var b,c,d,f,g,h;if(1===(null!=(h=a.tHead)?h.rows.length:void 0)&&"true"!==a.getAttribute("data-sortable-initialized")){for(a.setAttribute("data-sortable-initialized","true"),d=a.querySelectorAll("th"),b=f=0,g=d.length;g>f;b=++f)c=d[b],"false"!==c.getAttribute("data-sortable")&&e.setupClickableTH(a,c,b);return a}},setupClickableTH:function(a,d,f){var g,h,i,j,k,l;for(i=e.getColumnType(a,f),h=function(b){var c,g,h,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D;if(b.handled===!0)return!1;for(b.handled=!0,m="true"===this.getAttribute("data-sorted"),n=this.getAttribute("data-sorted-direction"),h=m?"ascending"===n?"descending":"ascending":i.defaultSortDirection,p=this.parentNode.querySelectorAll("th"),s=0,w=p.length;w>s;s++)d=p[s],d.setAttribute("data-sorted","false"),d.removeAttribute("data-sorted-direction");if(this.setAttribute("data-sorted","true"),this.setAttribute("data-sorted-direction",h),o=a.tBodies[0],l=[],m){for(D=o.rows,v=0,z=D.length;z>v;v++)g=D[v],l.push(g);for(l.reverse(),B=0,A=l.length;A>B;B++)k=l[B],o.appendChild(k)}else{for(r=null!=i.compare?i.compare:function(a,b){return b-a},c=function(a,b){return a[0]===b[0]?a[2]-b[2]:i.reverse?r(b[0],a[0]):r(a[0],b[0])},C=o.rows,j=t=0,x=C.length;x>t;j=++t)k=C[j],q=e.getNodeValue(k.cells[f]),null!=i.comparator&&(q=i.comparator(q)),l.push([q,k,j]);for(l.sort(c),u=0,y=l.length;y>u;u++)k=l[u],o.appendChild(k[1])}return"function"==typeof window.CustomEvent&&"function"==typeof a.dispatchEvent?a.dispatchEvent(new CustomEvent("Sortable.sorted",{bubbles:!0})):void 0},l=[],j=0,k=c.length;k>j;j++)g=c[j],l.push(b(d,g,h));return l},getColumnType:function(a,b){var c,d,f,g,h,i,j,k,l,m,n;if(d=null!=(l=a.querySelectorAll("th")[b])?l.getAttribute("data-sortable-type"):void 0,null!=d)return e.typesObject[d];for(m=a.tBodies[0].rows,h=0,j=m.length;j>h;h++)for(c=m[h],f=e.getNodeValue(c.cells[b]),n=e.types,i=0,k=n.length;k>i;i++)if(g=n[i],g.match(f))return g;return e.typesObject.alpha},getNodeValue:function(a){var b;return a?(b=a.getAttribute("data-value"),null!==b?b:"undefined"!=typeof a.innerText?a.innerText.replace(g,""):a.textContent.replace(g,"")):""},setupTypes:function(a){var b,c,d,f;for(e.types=a,e.typesObject={},f=[],c=0,d=a.length;d>c;c++)b=a[c],f.push(e.typesObject[b.name]=b);return f}},e.setupTypes([{name:"numeric",defaultSortDirection:"descending",match:function(a){return a.match(d)},comparator:function(a){return parseFloat(a.replace(/[^0-9.-]/g,""),10)||0}},{name:"date",defaultSortDirection:"ascending",reverse:!0,match:function(a){return!isNaN(Date.parse(a))},comparator:function(a){return Date.parse(a)||0}},{name:"alpha",defaultSortDirection:"ascending",match:function(){return!0},compare:function(a,b){return a.localeCompare(b)}}]),setTimeout(e.init,0),"function"==typeof define&&define.amd?define(function(){return e}):"undefined"!=typeof exports?module.exports=e:window.Sortable=e}).call(this); \ No newline at end of file diff --git a/docs/RELEASE_NOTES_NEXT/Administration/sortable_html_reports.adoc b/docs/RELEASE_NOTES_NEXT/Administration/sortable_html_reports.adoc new file mode 100644 index 0000000000..373418e7de --- /dev/null +++ b/docs/RELEASE_NOTES_NEXT/Administration/sortable_html_reports.adoc @@ -0,0 +1,5 @@ +Sortable HTML reports +^^^^^^^^^^^^^^^^^^^^^ +HTML reports can now be sorted by clicking on the header for a given column. +Clicking on the header toggles between sorting the column in ascending and +descending order. -- 2.43.2