Add new TimeFieldRenderer, make it default for Time fields

Uses a jQuery UI widget similar to datepicker:

https://fgelinas.com/code/timepicker/
This commit is contained in:
Lance Edgar 2016-03-24 00:06:04 -05:00
parent e13a58e808
commit 0f3f39d5c6
7 changed files with 1616 additions and 1 deletions

View file

@ -137,6 +137,7 @@ def make_pyramid_config(settings):
formalchemy.FieldSet.default_renderers[sa.Boolean] = renderers.YesNoFieldRenderer
formalchemy.FieldSet.default_renderers[sa.Date] = renderers.DateFieldRenderer
formalchemy.FieldSet.default_renderers[sa.DateTime] = renderers.DateTimeFieldRenderer
formalchemy.FieldSet.default_renderers[sa.Time] = renderers.TimeFieldRenderer
formalchemy.FieldSet.default_renderers[GPCType] = renderers.GPCFieldRenderer
return config

View file

@ -30,7 +30,7 @@ from .core import CustomFieldRenderer, DateFieldRenderer
from .common import (AutocompleteFieldRenderer,
DecimalFieldRenderer, CurrencyFieldRenderer,
DateTimeFieldRenderer, DateTimePrettyFieldRenderer,
DateTimeFieldRenderer, DateTimePrettyFieldRenderer, TimeFieldRenderer,
EnumFieldRenderer, YesNoFieldRenderer)
from .people import (PersonFieldRenderer, PersonFieldLinkRenderer,

View file

@ -26,7 +26,14 @@ Common Field Renderers
from __future__ import unicode_literals, absolute_import
import datetime
import pytz
from rattail.time import localtime
import formalchemy
from formalchemy import helpers
from formalchemy.fields import FieldRenderer, SelectFieldRenderer, CheckBoxFieldRenderer
from pyramid.renderers import render
@ -102,6 +109,51 @@ class DateTimePrettyFieldRenderer(formalchemy.DateTimeFieldRenderer):
return pretty_datetime(self.request.rattail_config, value)
class TimeFieldRenderer(formalchemy.TimeFieldRenderer):
"""
Custom renderer for time fields. In edit mode, renders a simple text
input, which is expected to become a 'timepicker' widget in the UI.
However the particular magic required for that lives in 'tailbone.js'.
"""
format = '%I:%M %p'
def render(self, **kwargs):
kwargs.setdefault('class_', 'timepicker')
return helpers.text_field(self.name, value=self.value, **kwargs)
def render_readonly(self, **kwargs):
return self.render_value(self.raw_value)
def render_value(self, value):
value = self.convert_value(value)
if isinstance(value, datetime.time):
return value.strftime(self.format)
return ''
def convert_value(self, value):
if isinstance(value, datetime.datetime):
if not value.tzinfo:
value = pytz.utc.localize(value)
return localtime(self.request.rattail_config, value).time()
return value
def stringify_value(self, value, as_html=False):
if not as_html:
return self.render_value(value)
return super(TimeFieldRenderer, self).stringify_value(value, as_html=as_html)
def _serialized_value(self):
return self.params.getone(self.name)
def deserialize(self):
value = self._serialized_value()
if value:
try:
return datetime.datetime.strptime(value, self.format).time()
except ValueError:
pass
class EnumFieldRenderer(SelectFieldRenderer):
"""
Renderer for simple enumeration fields.

View file

@ -0,0 +1,57 @@
/*
* Timepicker stylesheet
* Highly inspired from datepicker
* FG - Nov 2010 - Web3R
*
* version 0.0.3 : Fixed some settings, more dynamic
* version 0.0.4 : Removed width:100% on tables
* version 0.1.1 : set width 0 on tables to fix an ie6 bug
*/
.ui-timepicker-inline { display: inline; }
#ui-timepicker-div { padding: 0.2em; }
.ui-timepicker-table { display: inline-table; width: 0; }
.ui-timepicker-table table { margin:0.15em 0 0 0; border-collapse: collapse; }
.ui-timepicker-hours, .ui-timepicker-minutes { padding: 0.2em; }
.ui-timepicker-table .ui-timepicker-title { line-height: 1.8em; text-align: center; }
.ui-timepicker-table td { padding: 0.1em; width: 2.2em; }
.ui-timepicker-table th.periods { padding: 0.1em; width: 2.2em; }
/* span for disabled cells */
.ui-timepicker-table td span {
display:block;
padding:0.2em 0.3em 0.2em 0.5em;
width: 1.2em;
text-align:right;
text-decoration:none;
}
/* anchors for clickable cells */
.ui-timepicker-table td a {
display:block;
padding:0.2em 0.3em 0.2em 0.5em;
width: 1.2em;
cursor: pointer;
text-align:right;
text-decoration:none;
}
/* buttons and button pane styling */
.ui-timepicker .ui-timepicker-buttonpane {
background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0;
}
.ui-timepicker .ui-timepicker-buttonpane button { margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
/* The close button */
.ui-timepicker .ui-timepicker-close { float: right }
/* the now button */
.ui-timepicker .ui-timepicker-now { float: left; }
/* the deselect button */
.ui-timepicker .ui-timepicker-deselect { float: left; }

File diff suppressed because it is too large Load diff

View file

@ -112,6 +112,13 @@ $(function() {
$('input[type=submit]').button();
$('input[type=reset]').button();
/*
* Apply timepicker behavior to text inputs which are marked for it.
*/
$('input[type=text].timepicker').timepicker({
showPeriod: true
});
/*
* When filter labels are clicked, (un)check the associated checkbox.
*/

View file

@ -131,6 +131,7 @@
${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/lib/jquery.ui.timepicker.js'))}
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.js'))}
</%def>
@ -139,6 +140,7 @@
${self.jquery_theme()}
${h.stylesheet_link(request.static_url('tailbone:static/css/jquery.ui.menubar.css'))}
${h.stylesheet_link(request.static_url('tailbone:static/css/jquery.loadmask.css'))}
${h.stylesheet_link(request.static_url('tailbone:static/css/jquery.ui.timepicker.css'))}
${h.stylesheet_link(request.static_url('tailbone:static/css/jquery.ui.tailbone.css'))}
${h.stylesheet_link(request.static_url('tailbone:static/css/base.css'))}
${h.stylesheet_link(request.static_url('tailbone:static/css/layout.css'))}