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:
parent
e13a58e808
commit
0f3f39d5c6
|
@ -137,6 +137,7 @@ def make_pyramid_config(settings):
|
||||||
formalchemy.FieldSet.default_renderers[sa.Boolean] = renderers.YesNoFieldRenderer
|
formalchemy.FieldSet.default_renderers[sa.Boolean] = renderers.YesNoFieldRenderer
|
||||||
formalchemy.FieldSet.default_renderers[sa.Date] = renderers.DateFieldRenderer
|
formalchemy.FieldSet.default_renderers[sa.Date] = renderers.DateFieldRenderer
|
||||||
formalchemy.FieldSet.default_renderers[sa.DateTime] = renderers.DateTimeFieldRenderer
|
formalchemy.FieldSet.default_renderers[sa.DateTime] = renderers.DateTimeFieldRenderer
|
||||||
|
formalchemy.FieldSet.default_renderers[sa.Time] = renderers.TimeFieldRenderer
|
||||||
formalchemy.FieldSet.default_renderers[GPCType] = renderers.GPCFieldRenderer
|
formalchemy.FieldSet.default_renderers[GPCType] = renderers.GPCFieldRenderer
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
|
@ -30,7 +30,7 @@ from .core import CustomFieldRenderer, DateFieldRenderer
|
||||||
|
|
||||||
from .common import (AutocompleteFieldRenderer,
|
from .common import (AutocompleteFieldRenderer,
|
||||||
DecimalFieldRenderer, CurrencyFieldRenderer,
|
DecimalFieldRenderer, CurrencyFieldRenderer,
|
||||||
DateTimeFieldRenderer, DateTimePrettyFieldRenderer,
|
DateTimeFieldRenderer, DateTimePrettyFieldRenderer, TimeFieldRenderer,
|
||||||
EnumFieldRenderer, YesNoFieldRenderer)
|
EnumFieldRenderer, YesNoFieldRenderer)
|
||||||
|
|
||||||
from .people import (PersonFieldRenderer, PersonFieldLinkRenderer,
|
from .people import (PersonFieldRenderer, PersonFieldLinkRenderer,
|
||||||
|
|
|
@ -26,7 +26,14 @@ Common Field Renderers
|
||||||
|
|
||||||
from __future__ import unicode_literals, absolute_import
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
import pytz
|
||||||
|
|
||||||
|
from rattail.time import localtime
|
||||||
|
|
||||||
import formalchemy
|
import formalchemy
|
||||||
|
from formalchemy import helpers
|
||||||
from formalchemy.fields import FieldRenderer, SelectFieldRenderer, CheckBoxFieldRenderer
|
from formalchemy.fields import FieldRenderer, SelectFieldRenderer, CheckBoxFieldRenderer
|
||||||
from pyramid.renderers import render
|
from pyramid.renderers import render
|
||||||
|
|
||||||
|
@ -102,6 +109,51 @@ class DateTimePrettyFieldRenderer(formalchemy.DateTimeFieldRenderer):
|
||||||
return pretty_datetime(self.request.rattail_config, value)
|
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):
|
class EnumFieldRenderer(SelectFieldRenderer):
|
||||||
"""
|
"""
|
||||||
Renderer for simple enumeration fields.
|
Renderer for simple enumeration fields.
|
||||||
|
|
57
tailbone/static/css/jquery.ui.timepicker.css
vendored
Normal file
57
tailbone/static/css/jquery.ui.timepicker.css
vendored
Normal 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; }
|
||||||
|
|
||||||
|
|
1496
tailbone/static/js/lib/jquery.ui.timepicker.js
vendored
Normal file
1496
tailbone/static/js/lib/jquery.ui.timepicker.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
@ -112,6 +112,13 @@ $(function() {
|
||||||
$('input[type=submit]').button();
|
$('input[type=submit]').button();
|
||||||
$('input[type=reset]').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.
|
* When filter labels are clicked, (un)check the associated checkbox.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -131,6 +131,7 @@
|
||||||
${h.javascript_link('https://code.jquery.com/ui/1.11.4/jquery-ui.min.js')}
|
${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.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.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'))}
|
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.js'))}
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
@ -139,6 +140,7 @@
|
||||||
${self.jquery_theme()}
|
${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.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.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/jquery.ui.tailbone.css'))}
|
||||||
${h.stylesheet_link(request.static_url('tailbone:static/css/base.css'))}
|
${h.stylesheet_link(request.static_url('tailbone:static/css/base.css'))}
|
||||||
${h.stylesheet_link(request.static_url('tailbone:static/css/layout.css'))}
|
${h.stylesheet_link(request.static_url('tailbone:static/css/layout.css'))}
|
||||||
|
|
Loading…
Reference in a new issue