diff --git a/tailbone/newgrids/alchemy.py b/tailbone/newgrids/alchemy.py
index 8719359f..40fc3ba5 100644
--- a/tailbone/newgrids/alchemy.py
+++ b/tailbone/newgrids/alchemy.py
@@ -159,5 +159,10 @@ class AlchemyGrid(Grid):
self._fa_grid._set_active(row, orm.object_session(row))
yield row
+ def get_row_key(self, row):
+ mapper = orm.object_mapper(row)
+ assert len(mapper.primary_key) == 1
+ return getattr(row, mapper.primary_key[0].key)
+
def render_cell(self, row, column):
return column.field.render_readonly()
diff --git a/tailbone/newgrids/core.py b/tailbone/newgrids/core.py
index 0b1397c3..d4b2658c 100644
--- a/tailbone/newgrids/core.py
+++ b/tailbone/newgrids/core.py
@@ -42,7 +42,8 @@ class Grid(object):
joiners={}, filterable=False, filters={},
sortable=False, sorters={}, default_sortkey=None, default_sortdir='asc',
pageable=False, default_pagesize=20, default_page=1,
- width='auto', checkboxes=False, **kwargs):
+ width='auto', checkboxes=False, row_attrs={}, cell_attrs={},
+ **kwargs):
self.key = key
self.request = request
self.columns = columns
@@ -73,6 +74,8 @@ class Grid(object):
self.width = width
self.checkboxes = checkboxes
+ self.row_attrs = row_attrs
+ self.cell_attrs = cell_attrs
def get_default_filters(self):
"""
@@ -473,6 +476,8 @@ class Grid(object):
classes = ['newgrid']
if self.width == 'full':
classes.append('full')
+ if self.checkboxes:
+ classes.append('selectable')
return {'class_': ' '.join(classes),
'data-url': self.request.current_route_url(_query=None),
'data-permalink': self.request.current_route_url()}
@@ -533,16 +538,14 @@ class Grid(object):
def get_row_attrs(self, row, i):
"""
- Returns a properly-formatted set of attributes which will be applied to
- the ``
`` element for the given row. Note that ``i`` will be a
- 1-based index value for the row within its table. The meaning of
- ``row`` is basically not defined; it depends on the type of data the
- grid deals with.
+ Returns a dict of HTML attributes which is to be applied to the row's
+ ``
`` element. Note that ``i`` will be a 1-based index value for
+ the row within its table. The meaning of ``row`` is basically not
+ defined; it depends on the type of data the grid deals with.
"""
- # attrs = {'class_': self.get_row_class(row, i)}
- # attrs = {}
- # return format_attrs(**attrs)
- return {}
+ if callable(self.row_attrs):
+ return self.row_attrs(row, i)
+ return self.row_attrs
# def get_row_class(self, row, i):
# class_ = self.default_row_class(row, i)
@@ -552,18 +555,34 @@ class Grid(object):
# class_ = '{0} {1}'.format(class_, extra)
# return class_
- # def checkbox(self, key):
- # """
- # Render a checkbox using the given key.
- # """
- # return tags.checkbox('checkbox-{0}-{1}'.format(self.key, key))
+ def get_row_key(self, row):
+ raise NotImplementedError
+
+ def checkbox(self, row):
+ return True
+
+ def checked(self, row):
+ return False
+
+ def render_checkbox(self, row):
+ """
+ Returns a boolean indicating whether ot not a checkbox should be
+ rendererd for the given row. Default implementation returns ``True``
+ in all cases.
+ """
+ if not self.checkbox(row):
+ return ''
+ return tags.checkbox('checkbox-{0}-{1}'.format(self.key, self.get_row_key(row)),
+ checked=self.checked(row))
def get_cell_attrs(self, row, column):
"""
Returns a dictionary of HTML attributes which should be applied to the
```` element in which the given row and column "intersect".
"""
- return {}
+ if callable(self.cell_attrs):
+ return self.cell_attrs(row, column)
+ return self.cell_attrs
def render_cell(self, row, column):
return ''
diff --git a/tailbone/static/css/base.css b/tailbone/static/css/base.css
index b596d96b..f939d915 100644
--- a/tailbone/static/css/base.css
+++ b/tailbone/static/css/base.css
@@ -44,7 +44,6 @@ p {
}
.right {
- float: right;
text-align: right;
}
diff --git a/tailbone/static/css/newgrids.css b/tailbone/static/css/newgrids.css
index ae49fe5f..057d5918 100644
--- a/tailbone/static/css/newgrids.css
+++ b/tailbone/static/css/newgrids.css
@@ -133,18 +133,46 @@
* tbody
******************************/
-.newgrid table tbody td {
+.newgrid tbody td {
padding: 5px 6px;
}
-.newgrid table tbody tr:nth-child(odd) {
+.newgrid.selectable tbody td {
+ cursor: default;
+}
+
+.newgrid tbody tr:nth-child(odd) {
background-color: #e0e0e0;
}
-.newgrid table tbody tr.hovering {
+.newgrid tbody tr.hovering {
background-color: #bbbbbb;
}
+.newgrid tbody tr.notice {
+ background-color: #fd6;
+}
+
+.newgrid tbody tr.notice:nth-child(odd) {
+ background-color: #fe8;
+}
+
+.newgrid tbody tr.notice.hovering {
+ background-color: #ec7;
+}
+
+.newgrid tbody tr.warning {
+ background-color: #fcc;
+}
+
+.newgrid tbody tr.warning:nth-child(odd) {
+ background-color: #ebb;
+}
+
+.newgrid tbody tr.warning.hovering {
+ background-color: #daa;
+}
+
/******************************
* main actions
diff --git a/tailbone/static/js/jquery.ui.tailbone.js b/tailbone/static/js/jquery.ui.tailbone.js
index f0f810a6..c23618f6 100644
--- a/tailbone/static/js/jquery.ui.tailbone.js
+++ b/tailbone/static/js/jquery.ui.tailbone.js
@@ -115,6 +115,29 @@
$(this).removeClass('hovering');
});
+ // Do some extra stuff for grids with checkboxes.
+ if (this.grid.hasClass('selectable')) {
+
+ // (Un-)Check all rows when clicking check-all box in header.
+ this.element.on('click', 'thead th.checkbox input', function() {
+ var checked = $(this).prop('checked');
+ that.grid.find('tbody td.checkbox input').prop('checked', checked);
+ });
+
+ // Select current row when clicked, unless clicking checkbox
+ // (since that already does select the row) or a link (since
+ // that does something completely different).
+ this.element.on('click', 'tbody td.checkbox input', function(event) {
+ event.stopPropagation();
+ });
+ this.element.on('click', 'tbody a', function(event) {
+ event.stopPropagation();
+ });
+ this.element.on('click', 'tbody tr', function() {
+ $(this).find('td.checkbox input').click();
+ });
+ }
+
// Show 'more' actions when user hovers over 'more' link.
this.element.on('mouseenter', '.actions a.more', function() {
that.grid.find('.actions div.more').hide();
diff --git a/tailbone/static/js/tailbone.js b/tailbone/static/js/tailbone.js
index eacfacc3..fb84dd4b 100644
--- a/tailbone/static/js/tailbone.js
+++ b/tailbone/static/js/tailbone.js
@@ -112,11 +112,6 @@ $(function() {
$('input[type=submit]').button();
$('input[type=reset]').button();
- /*
- * Enhance new-style grids.
- */
- $('.newgrid-wrapper').gridwrapper();
-
/*
* When filter labels are clicked, (un)check the associated checkbox.
*/
diff --git a/tailbone/templates/base.mako b/tailbone/templates/base.mako
index 83ad9c3e..8ef4abcb 100644
--- a/tailbone/templates/base.mako
+++ b/tailbone/templates/base.mako
@@ -131,7 +131,6 @@
${h.javascript_link('https://code.jquery.com/ui/1.11.4/jquery-ui.min.js')}
${h.javascript_link(request.static_url('tailbone:static/js/lib/jquery.ui.menubar.js'))}
${h.javascript_link(request.static_url('tailbone:static/js/lib/jquery.loadmask.min.js'))}
- ${h.javascript_link(request.static_url('tailbone:static/js/jquery.ui.tailbone.js'))}
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.js'))}
%def>
diff --git a/tailbone/templates/master/edit.mako b/tailbone/templates/master/edit.mako
index df7f379c..a1c9dd40 100644
--- a/tailbone/templates/master/edit.mako
+++ b/tailbone/templates/master/edit.mako
@@ -5,10 +5,10 @@
<%def name="context_menu_items()">
${h.link_to("Back to {0}".format(model_title_plural), url(route_prefix))}
- % if request.has_perm('{0}.view'.format(permission_prefix)):
+ % if master.viewable and request.has_perm('{0}.view'.format(permission_prefix)):
${h.link_to("View this {0}".format(model_title), action_url('view', instance))}
% endif
- % if request.has_perm('{0}.delete'.format(permission_prefix)):
+ % if master.deletable and request.has_perm('{0}.delete'.format(permission_prefix)):
${h.link_to("Delete this {0}".format(model_title), action_url('delete', instance))}
% endif
%def>
diff --git a/tailbone/templates/master/index.mako b/tailbone/templates/master/index.mako
index 6a6bfe0c..eccb7bac 100644
--- a/tailbone/templates/master/index.mako
+++ b/tailbone/templates/master/index.mako
@@ -9,8 +9,18 @@
<%def name="title()">${grid.model_title_plural}%def>
+<%def name="head_tags()">
+ ${parent.head_tags()}
+ ${h.javascript_link(request.static_url('tailbone:static/js/jquery.ui.tailbone.js'))}
+
+%def>
+
<%def name="context_menu_items()">
- % if request.has_perm('{0}.create'.format(grid.permission_prefix)):
+ % if master.creatable and request.has_perm('{0}.create'.format(grid.permission_prefix)):
${h.link_to("Create a new {0}".format(grid.model_title), url('{0}.create'.format(grid.route_prefix)))}
% endif
%def>
diff --git a/tailbone/templates/master/view.mako b/tailbone/templates/master/view.mako
index 2579d4d7..d419d9a8 100644
--- a/tailbone/templates/master/view.mako
+++ b/tailbone/templates/master/view.mako
@@ -5,10 +5,10 @@
<%def name="context_menu_items()">
${h.link_to("Back to {0}".format(model_title_plural), url(route_prefix))}
- % if request.has_perm('{0}.edit'.format(permission_prefix)):
+ % if master.editable and request.has_perm('{0}.edit'.format(permission_prefix)):
${h.link_to("Edit this {0}".format(model_title), action_url('edit', instance))}
% endif
- % if request.has_perm('{0}.delete'.format(permission_prefix)):
+ % if master.deletable and master.deletable_instance(instance) and request.has_perm('{0}.delete'.format(permission_prefix)):
${h.link_to("Delete this {0}".format(model_title), action_url('delete', instance))}
% endif
%def>
diff --git a/tailbone/templates/newgrids/grid.mako b/tailbone/templates/newgrids/grid.mako
index 4f3f1fe2..9cb73c31 100644
--- a/tailbone/templates/newgrids/grid.mako
+++ b/tailbone/templates/newgrids/grid.mako
@@ -3,9 +3,9 @@
-## % if grid.checkboxes:
-## ${h.checkbox('check-all')} |
-## % endif
+ % if grid.checkboxes:
+ ${h.checkbox('check-all')} |
+ % endif
% for column in grid.iter_visible_columns():
${grid.column_header(column)}
% endfor
@@ -17,9 +17,9 @@
% for i, row in enumerate(grid.iter_rows(), 1):
-## % if grid.checkboxes:
-## ${grid.checkbox(row)} |
-## % endif
+ % if grid.checkboxes:
+ ${grid.render_checkbox(row)} |
+ % endif
% for column in grid.iter_visible_columns():
${grid.render_cell(row, column)} |
% endfor
@@ -32,7 +32,7 @@
% endfor
- % if grid.pageable:
+ % if grid.pageable and grid.pager:
|