Added tablesorter and improved Table()

This commit is contained in:
Deon George 2013-05-07 11:38:28 +10:00
parent 74a9c291e4
commit e3c93e1d38
16 changed files with 451 additions and 134 deletions

View File

@ -8,10 +8,35 @@
* @author Deon George
* @copyright (c) 2009-2013 Deon George
* @license http://dev.leenooks.net/license.html
* @uses Style
*/
abstract class lnApp_Table {
public static function resolve($d,$key) {
private $data = NULL;
private $columns = array(); // Our columns that we need to display
private $jssort = FALSE; // Use the JS pager and sorter
private $other = ''; // If we are only listing x items, this will be the other summary line
private $other_col = NULL; // If we are only listing x items, this will be the other summary line
private $po = NULL; // If we are paging the results, this is the page we are displaying
private $page = FALSE; // If we should page the results
private $page_rows = 0; // Number of rows per page
private $prepend = array(); // Our datafomrating that we need to use
public function __toString() {
return (string) $this->render();
}
public function columns($cols) {
$this->columns = $cols;
return $this;
}
public function data($data) {
$this->data = $data;
return $this;
}
private function evaluate($d,$key) {
if (is_array($d) AND isset($d[$key]))
$x = $d[$key];
@ -25,26 +50,185 @@ abstract class lnApp_Table {
else
$x = $d->display($key);
if (isset($this->prepend[$key])) {
foreach ($this->prepend[$key] as $act => $data) {
switch ($act) {
case 'url': $x = HTML::anchor($data.$x,$x);
break;
}
}
}
return $x;
}
public static function factory() {
return new Table;
}
public function jssort($bool) {
$this->jssort = $bool;
return $this;
}
public function page_items($num) {
$this->page = TRUE;
$this->page_rows = $num;
return $this;
}
public function prepend($cols) {
$this->prepend = $cols;
return $this;
}
private function process() {
$result = array();
$row = 0;
foreach ($this->data as $o) {
$row++;
$c = 0;
// If we are HTML paging
if ($this->page) {
if ($row < $this->po->current_first_item())
continue;
elseif ($row > $this->po->current_last_item())
break;
}
// If we are just listing page_rows items and a summary line as a result of exceeding that
if ($this->other_col AND $row>$this->page_rows) {
$this->other += Table::resolve($o,$this->other_col);
// Otherwise rendering our rows
} else {
foreach ($this->columns as $col => $label) {
$c++;
$result[$row]['val'][$c] = $this->evaluate($o,$col);
}
}
}
return $result;
}
public function render() {
$output = '';
// If we need to page the results
if ($this->page) {
$this->po = new Pagination(array(
'total_items'=>count($this->data),
'items_per_page'=>$this->page_rows,
));
$output .= (string)$this->po;
}
$output .= View::factory('table')
->set('jssort',$this->jssort AND ! $this->page)
->set('other',$this->other)
->set('th',$this->columns)
->set('td',$this->process());
// Use the javascript paging
if ($this->jssort AND ! $this->page) {
Script::factory()
->type('stdin')
->data('
$(document).ready(function() {
$.extend($.tablesorter.themes.bootstrap, {
// these classes are added to the table. To see other table classes available,
// look here: http://twitter.github.com/bootstrap/base-css.html#tables
table : "table table-striped",
header : "bootstrap-header", // give the header a gradient background
footerRow : "",
footerCells: "",
icons : "", // add "icon-white" to make them white; this icon class is added to the <i> in the header
sortNone : "bootstrap-icon-unsorted",
sortAsc : "icon-chevron-up",
sortDesc : "icon-chevron-down",
active : "", // applied when column is sorted
hover : "", // use custom css here - bootstrap class may not override it
filterRow : "", // filter row class
even : "", // odd row zebra striping
odd : "" // even row zebra striping
});
$("#list-table").tablesorter({
theme: "bootstrap",
widthFixed : true,
headerTemplate : "{content} {icon}", // Add icon for jui theme; new in v2.7!
widgets: [ "uitheme", "stickyHeaders", "filter" ],
}).tablesorterPager({
// target the pager markup - see the HTML block below
container : $(".pager"),
output : "{startRow} - {endRow} / {filteredRows} ({totalRows})",
fixedHeight: true,
removeRows : false,
cssGoto : ".gotoPage"
});
});
');
Script::factory()
->type('file')
->data('media/vendor/mottie-tablesorter/js/jquery.tablesorter.min.js');
Script::factory()
->type('file')
->data('media/vendor/mottie-tablesorter/js/jquery.tablesorter.widgets.min.js');
Script::factory()
->type('file')
->data('media/vendor/mottie-tablesorter/js/jquery.tablesorter.pager.min.js');
Style::factory()
->type('file')
->data('media/vendor/mottie-tablesorter/css/theme.bootstrap.css');
Style::factory()
->type('file')
->data('media/vendor/mottie-tablesorter/css/jquery.tablesorter.pager.css');
}
return $output;
}
//ZZ
public static function display($data,$rows,array $cols,array $option) {
if (! (array)$data)
return '';
$prepend = $headers = array();
$pag = NULL;
$view = $output = $button = '';
foreach ($cols as $k=>$v) {
if (isset($v['label']))
$headers[$k] = $v['label'];
if (isset($v['url']))
$prepend[$k] = array('url'=>$v['url']);
}
if (isset($option['type']) AND $option['type'])
switch ($option['type']) {
case 'select':
$view = 'table/select';
return static::factory()
->data($data)
->jssort(TRUE)
->page_items($rows)
->columns($headers)
->prepend($prepend);
/*
if (! empty($option['button']))
$button = implode('',$option['button']);
else
$button = Form::button('Submit','View/Edit',array('class'=>'form_button','type'=>'submit'));
// This JS is failing, any pressing of select all, unselect all, toggle is propaging up and submitting the form
Script::factory()
->type('stdin')
->data('
@ -153,104 +337,17 @@ $(document).ready(function() {
});
});
');
$output .= Form::open((isset($option['form']) ? $option['form'] : ''));
if (! empty($option['hidden']))
$output .= '<div>'.implode('',$option['hidden']).'</div>';
break;
case 'list':
default:
Script::factory()
->type('stdin')
->data('
// Bind our actions
$(document).ready(function() {
// Our mouse over row highlight
$("#list-table tr:not(.head)").hover(function() {
$(this).children().toggleClass("highlight");
},
function() {
$(this).children().toggleClass("highlight");
});
});
');
}
If (! $view)
$view = 'table/list';
if (isset($option['page']) AND $option['page']) {
$pag = new Pagination(array(
'total_items'=>count($data),
'items_per_page'=>$rows,
));
$output .= (string)$pag;
}
$other = $i = 0;
$td = $th = array();
foreach ($cols as $col => $details) {
$th[$col] = isset($details['label']) ? $details['label'] : '';
$td[$col]['class'] = isset($details['class']) ? $details['class'] : '';
$td[$col]['url'] = isset($details['url']) ? $details['url'] : '';
}
$output .= View::factory($view.'_head')
->set('th',array_values($th));
foreach ($data as $do) {
if ($pag) {
if (++$i < $pag->current_first_item())
continue;
elseif ($i > $pag->current_last_item())
break;
}
if ($pag OR ($i++ < $rows) OR is_null($rows)) {
foreach (array_keys($cols) as $col)
$td[$col]['value'] = Table::resolve($do,$col);
$output .= View::factory($view.'_body')
->set('td',$td);
} elseif (isset($option['show_other']) AND ($col=$option['show_other'])) {
$other += Table::resolve($do,$col);
}
}
if ($other)
$output .= View::factory($view.'_xtra')
->set('td',array_values($cols))
->set('count',$i-$rows)
->set('other',$other);
$output .= View::factory($view.'_foot')
->set('button',$button);
if (isset($option['type']) AND $option['type'])
switch ($option['type']) {
case 'select':
$output .= Form::close();
}
return $output;
}
public static function post($key,$i='id') {
if (isset($_POST[$i]))
Session::instance()->set('page_table_view'.$key,$_POST[$i]);
*/
}
// This enables us to page through many selected items
// @todo This is currently not usable, since our JS above needs to be fixed to work with tablesorter
public static function page($key) {
// We have preference for parameters passed to the action.
if (is_null($id = Request::current()->param('id'))) {
// First save our POST id data into the session, so we dont need it when going to each page
if (isset($_POST['id']) AND is_array($_POST['id']))
Table::post($key,'id');
Session::instance()->set('page_table_view'.$key,'id');
if ($ids = Session::instance()->get('page_table_view'.$key)) {
$pag = new Pagination(array(
@ -260,7 +357,6 @@ $(document).ready(function() {
return array($ids[$pag->current_first_item()-1],(string)$pag);
}
}
// If we get here, then there is no previous data to retrieve.

View File

@ -0,0 +1,25 @@
.error-container {
margin-top: 4em;
margin-bottom: 4em;
text-align: center;
}
.error-container h1 {
margin-bottom: .5em;
font-size: 120px;
line-height: 1em;
font-weight: normal;
}
.error-container h2 {
margin-bottom: .75em;
font-size: 28px;
}
.error-container .error-details {
margin-bottom: 1.5em;
font-size: 16px;
}
.error-container .error-actions a {
margin: 0 .5em;
}
#footer {
display: none;
}

View File

@ -0,0 +1,40 @@
/* pager wrapper, div */
.tablesorter-pager {
font-size: 14px;
}
/* pager navigation arrows */
.tablesorter-pager [class^="icon-"] {
vertical-align: middle;
margin-right: 4px;
cursor: pointer;
}
/* pager output text */
.tablesorter-pager .pagedisplay {
padding: 0 5px 0 5px;
width: auto;
white-space: nowrap;
text-align: center;
}
/* pager element reset (needed for bootstrap) */
.tablesorter-pager select {
margin: 0;
padding: 0;
height: 25px;
font-size: 12px;
}
/*** css used when "updateArrows" option is true ***/
/* the pager itself gets a disabled class when the number of rows is less than the size */
.tablesorter-pager.disabled {
display: none;
}
/* hide or fade out pager arrows when the first or last row is visible */
.tablesorter-pager .disabled {
/* visibility: hidden */
opacity: 0.5;
filter: alpha(opacity=50);
cursor: default;
}

View File

@ -0,0 +1,136 @@
/*************
Bootstrap theme
*************/
/* jQuery Bootstrap Theme */
.tablesorter-bootstrap {
width: 100%;
}
.tablesorter-bootstrap .tablesorter-header,
.tablesorter-bootstrap tfoot th,
.tablesorter-bootstrap tfoot td {
font: bold 14px/20px Arial, Sans-serif;
position: relative;
padding: 8px;
margin: 0 0 18px;
list-style: none;
background-color: #FBFBFB;
background-image: -moz-linear-gradient(top, white, #efefef);
background-image: -ms-linear-gradient(top, white, #efefef);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(white), to(#efefef));
background-image: -webkit-linear-gradient(top, white, #efefef);
background-image: -o-linear-gradient(top, white, #efefef);
background-image: linear-gradient(to bottom, white, #efefef);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#efefef', GradientType=0);
background-repeat: repeat-x;
-webkit-box-shadow: inset 0 1px 0 white;
-moz-box-shadow: inset 0 1px 0 #ffffff;
box-shadow: inset 0 1px 0 white;
}
.tablesorter-bootstrap .tablesorter-header {
cursor: pointer;
}
.tablesorter-bootstrap .tablesorter-header-inner {
position: relative;
padding: 4px 18px 4px 4px;
}
/* bootstrap uses <i> for icons */
.tablesorter-bootstrap .tablesorter-header i {
position: absolute;
right: 2px;
top: 50%;
margin-top: -7px; /* half the icon height; older IE doesn't like this */
width: 14px;
height: 14px;
background-repeat: no-repeat;
line-height: 14px;
display: inline-block;
}
.tablesorter-bootstrap .bootstrap-icon-unsorted {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOCAYAAAD5YeaVAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAWVJREFUeNqUUL9Lw2AUTGP8mqGlpBQkNeCSRcckEBcHq1jImMElToKuDvpHFMGhU0BQcHBwLji6CE1B4uB/INQsDi4d2jQ/fPeZxo764OV6915f7lLJ81xot9tCURXqdVEUr7IsO6ffH9Q5BlEUCaLwWxWqTcbYnaIoh0Dw4gAvcWlxq1qt9hqNxg6hUGAP+uIPUrGs0qXLer2+v/pTX6QpxLtkc2U2m53ACb8sSdIDXerSEms2m6+DweAICA4d89KGbduf9MpEVdXQ9/2LVqv1CASHjjn3iq/x1xKFfxQPqGnada1W86bT6SiO42OS3qk3KPStLMvbk8nkfjwen/LLuq6blFymMB0KdUPSGhAcOualjX6/f0bCiC7NaWGPQr0BwaFjzn0gYJqmLAiCA8/zni3LmhuGkQPBoWPOPwQeaPIqD4fDruu6L6Zp5kBw6IudchmdJAkLw3DXcZwnIPjy/FuAAQCiqqWWCAFKcwAAAABJRU5ErkJggg==);
}
/* since bootstrap (table-striped) uses nth-child(), we just use this to add a zebra stripe color */
.tablesorter-bootstrap tr.odd td {
background-color: #f9f9f9;
}
.tablesorter-bootstrap tbody > .odd:hover > td,
.tablesorter-bootstrap tbody > .even:hover > td {
background-color: #f5f5f5;
}
.tablesorter-bootstrap tr.even td {
background-color: #fff;
}
/* processing icon */
.tablesorter-bootstrap .tablesorter-processing {
background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=');
position: absolute;
z-index: 1000;
}
/* caption */
caption {
background: #fff;
}
/* filter widget */
.tablesorter-bootstrap .tablesorter-filter-row .tablesorter-filter {
width: 98%;
height: auto;
margin: 0 auto;
padding: 4px 6px;
background-color: #fff;
color: #333;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-transition: height 0.1s ease;
-moz-transition: height 0.1s ease;
-o-transition: height 0.1s ease;
transition: height 0.1s ease;
}
.tablesorter-bootstrap .tablesorter-filter-row .tablesorter-filter.disabled {
background: #eee;
}
.tablesorter-bootstrap .tablesorter-filter-row td {
background: #eee;
line-height: normal;
text-align: center;
padding: 4px 6px;
vertical-align: middle;
-webkit-transition: line-height 0.1s ease;
-moz-transition: line-height 0.1s ease;
-o-transition: line-height 0.1s ease;
transition: line-height 0.1s ease;
}
/* hidden filter row */
.tablesorter-bootstrap .tablesorter-filter-row.hideme td {
padding: 2px; /* change this to modify the thickness of the closed border row */
margin: 0;
line-height: 0;
}
.tablesorter-bootstrap .tablesorter-filter-row.hideme .tablesorter-filter {
height: 1px;
min-height: 0;
border: 0;
padding: 0;
margin: 0;
/* don't use visibility: hidden because it disables tabbing */
opacity: 0;
filter: alpha(opacity=0);
}
/* pager plugin */
.tablesorter-bootstrap .tablesorter-pager select {
padding: 4px 6px;
}
.tablesorter-bootstrap .tablesorter-pager .pagedisplay {
border: 0;
}
.table-striped td {
padding-left: 15px;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

28
views/table.php Normal file
View File

@ -0,0 +1,28 @@
<table class="table table-striped table-condensed table-hover" id="list-table">
<thead><tr><th><?php echo implode('</th><th>',$th); ?></th></tr></thead>
<tbody>
<?php foreach ($td as $details) : ?>
<tr><td><?php echo implode('</td><td>',$details['val']); ?></td></tr></tr>
<?php endforeach ?>
<?php if ($other) : ?>
<tr><td>Other</td><td colspan="<?php #echo count($data)-1; ?>"><?php #printf('(%s) %s',$count,$other); ?></td></tr>
<?php endif ?>
</tbody>
</table>
<?php if ($jssort) : ?>
<div class="pager">
Page: <select class="input-mini gotoPage"></select>
<i class="icon-fast-backward first"></i>
<i class="icon-step-backward prev"></i>
<span class="pagedisplay"></span> <!-- this can be any element, including an input -->
<i class="icon-step-forward next"></i>
<i class="icon-fast-forward last"></i>
<select class="input-mini pagesize">
<option selected="selected" value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
Items per page
</div>
<?php endif ?>

View File

@ -1,7 +0,0 @@
<tr>
<?php foreach ($td as $col => $details) { ?>
<td>
<?php echo $details['url'] ? sprintf(HTML::anchor($details['url'].$details['value'],$details['value'])) : $details['value']; ?>
</td>
<?php } ?>
</tr>

View File

@ -1 +0,0 @@
</table>

View File

@ -1,2 +0,0 @@
<table class="table table-striped table-condensed" id="list-table">
<tr><th><?php echo implode('</th><th>',$th); ?></th></tr>

View File

@ -1 +0,0 @@
<tr><td>Other</td><td colspan="<?php echo count($td)-1; ?>"><?php printf('(%s) %s',$count,$other); ?></td></tr>

View File

@ -1,8 +0,0 @@
<tr>
<td><?php echo Form::checkbox('id[]',$td['id']['value']); ?></td>
<?php foreach ($td as $col => $details) { ?>
<td>
<?php echo $details['url'] ? sprintf(HTML::anchor($details['url'].$details['value'],$details['value'])) : $details['value']; ?>
</td>
<?php } ?>
</tr>

View File

@ -1,9 +0,0 @@
</table>
<!--
<div class="btn-group pull-center">
<?php #echo $button; ?>
<button type="submit" name="Submit" class="btn" id="all_on">Select All</button>
<button type="submit" name="Submit" class="btn" id="all_off">Deselect All</button>
<button type="submit" name="Submit" class="btn" id="toggle">Toggle Select</button>
</div>
-->

View File

@ -1,2 +0,0 @@
<table class="table table-striped table-condensed table-hover" id="select-table">
<tr><th>&nbsp;</th><th><?php echo implode('</th><th>',$th); ?></th></tr>

View File

@ -1 +0,0 @@
<tr><td>Other</td><td colspan="<?php echo count($td)-1; ?>"><?php printf('(%s) %s',$count,$other); ?></td></tr>