fields: add timetable visualisation to item fields (#50035)
This commit is contained in:
parent
facedb66c8
commit
78ebedfdcd
|
@ -68,7 +68,11 @@ class DataSourceSelectionWidget(CompositeWidget):
|
|||
options.append(OptGroup(_('Cards')))
|
||||
options.extend(cards_options)
|
||||
|
||||
nds_options = [(x.slug, x.name, x.slug, {'data-type': x.type}) for x in NamedDataSource.select()]
|
||||
nds_options = [(
|
||||
x.slug, x.name, x.slug, {
|
||||
'data-type': x.type,
|
||||
'data-maybe-datetimes': 'true' if x.maybe_datetimes() else 'false'})
|
||||
for x in NamedDataSource.select()]
|
||||
nds_options.sort(key=lambda x: misc.simplify(x[1]))
|
||||
if nds_options:
|
||||
options.append(OptGroup(_('Manually Configured Data Sources')))
|
||||
|
@ -76,7 +80,7 @@ class DataSourceSelectionWidget(CompositeWidget):
|
|||
|
||||
if len(options) > 1:
|
||||
options.append(OptGroup(_('Generic Data Sources')))
|
||||
options.append(('json', _('JSON URL'), 'json'))
|
||||
options.append(('json', _('JSON URL'), 'json', {'data-maybe-datetimes': 'true'}))
|
||||
if allow_jsonp:
|
||||
options.append(('jsonp', _('JSONP URL'), 'jsonp'))
|
||||
if allow_geojson:
|
||||
|
@ -391,6 +395,9 @@ class NamedDataSource(XmlStorableObject):
|
|||
return True
|
||||
return False
|
||||
|
||||
def maybe_datetimes(self):
|
||||
return self.type == 'json' and 'datetimes' in (self.data_source.get('value') or '')
|
||||
|
||||
def migrate(self):
|
||||
changed = False
|
||||
|
||||
|
|
|
@ -1572,6 +1572,9 @@ class ItemField(WidgetField, MapOptionsMixin):
|
|||
kwargs['select2'] = True
|
||||
elif display_mode == 'map':
|
||||
self.widget_class = MapMarkerSelectionWidget
|
||||
elif display_mode == 'timetable':
|
||||
# SingleSelectHintWidget with custom template
|
||||
kwargs['template-name'] = 'qommon/forms/widgets/select-timetable.html'
|
||||
|
||||
def get_display_value(self, value):
|
||||
data_source = data_sources.get_object(self.data_source)
|
||||
|
@ -1694,6 +1697,7 @@ class ItemField(WidgetField, MapOptionsMixin):
|
|||
('radio', _('Radio buttons'), 'radio'),
|
||||
('autocomplete', _('Autocomplete'), 'autocomplete'),
|
||||
('map', _('Map (requires geographical data)'), 'map'),
|
||||
('timetable', _('Timetable'), 'timetable'),
|
||||
]
|
||||
form.add(RadiobuttonsWidget, 'display_mode',
|
||||
title=_('Display Mode'),
|
||||
|
|
|
@ -1858,6 +1858,8 @@ class SingleSelectHintWidget(SingleSelectWidget):
|
|||
def __init__(self, name, value=None, **kwargs):
|
||||
self.options_with_attributes = kwargs.pop('options_with_attributes', None)
|
||||
self.select2 = kwargs.pop('select2', None)
|
||||
if 'template-name' in kwargs:
|
||||
self.template_name = kwargs.pop('template-name')
|
||||
super(SingleSelectHintWidget, self).__init__(name, value=value, **kwargs)
|
||||
|
||||
def add_media(self):
|
||||
|
|
|
@ -256,14 +256,22 @@ $(function() {
|
|||
}
|
||||
|
||||
$('[type=radio][name=display_mode]').on('change', function() {
|
||||
// show everything
|
||||
$('select[name="data_source$type"] option').show();
|
||||
// then restrict
|
||||
if ($(this).val() == 'map') {
|
||||
$('input[name="data_mode"][value="data-source"]').click()
|
||||
$('select[name="data_source$type"] option:not([data-type="geojson"])').hide();
|
||||
if ($('select[name="data_source$type"] option:selected:visible').length == 0) {
|
||||
$('select[name="data_source$type"] option:visible').first().prop('selected', true);
|
||||
}
|
||||
} else {
|
||||
$('select[name="data_source$type"] option').show();
|
||||
}
|
||||
if ($(this).val() == 'timetable') {
|
||||
$('input[name="data_mode"][value="data-source"]').click()
|
||||
$('select[name="data_source$type"] option:not([data-maybe-datetimes="true"])').hide();
|
||||
if ($('select[name="data_source$type"] option:selected:visible').length == 0) {
|
||||
$('select[name="data_source$type"] option:visible').first().prop('selected', true);
|
||||
}
|
||||
}
|
||||
});
|
||||
$('[type=radio][name=display_mode]:checked').trigger('change');
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
{% extends "qommon/forms/widget.html" %}
|
||||
{% load qommon i18n %}
|
||||
{% block widget-control %}
|
||||
<select style="display: none" id="form_{{widget.name}}" name="{{widget.name}}"
|
||||
{% for attr in widget.attrs.items %}{{attr.0}}="{{attr.1}}"{% endfor %}>
|
||||
<option value="">---</option>
|
||||
{% for option in widget.get_options %}
|
||||
{% with datetime=option.options.datetime|parse_datetime %}
|
||||
<option{% for attr in option.attrs.items %} {{attr.0}}="{{attr.1}}"{% endfor %}
|
||||
data-weekday="{{ datetime|date:"l" }}"
|
||||
data-date="{{ datetime|date:"Y-m-d" }}"
|
||||
data-time="{{ datetime|date:"H:i" }}"
|
||||
>{{ option.description }}</option>
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
<div id="form_{{widget.name}}_table" class="timetable-widget meetings_table">
|
||||
</div>
|
||||
<script>
|
||||
$(function() {
|
||||
var WEEKDAYS = ["{% trans "Sunday" %}", "{% trans "Monday" %}",
|
||||
"{% trans "Tuesday" %}", "{% trans "Wednesday" %}",
|
||||
"{% trans "Thursday" %}", "{% trans "Friday" %}",
|
||||
"{% trans "Saturday" %}"];
|
||||
var $select = $('#form_{{widget.name}}');
|
||||
var $table = $('#form_{{widget.name}}_table');
|
||||
var column_count = 5;
|
||||
|
||||
function fill_with_items(items) {
|
||||
$select.empty();
|
||||
$('<option value=""></option>').appendTo($select);
|
||||
for (var i=0; i<items.length; i++) {
|
||||
var $option = $('<option></option>', {value: items[i].id, text: items[i].text});
|
||||
if (items[i].disabled) {
|
||||
$option.prop('disabled', true);
|
||||
}
|
||||
var date = new Date(items[i].datetime);
|
||||
$option.attr('data-weekday', WEEKDAYS[date.getDay()]);
|
||||
$option.attr('data-date', items[i].datetime.slice(0, 10));
|
||||
$option.attr('data-time', items[i].datetime.slice(11, 16));
|
||||
$option.appendTo($select);
|
||||
}
|
||||
}
|
||||
|
||||
$select.on('wcs:options-change', function(ev, data) {
|
||||
if (data !== undefined) {
|
||||
fill_with_items(data.items);
|
||||
}
|
||||
$table.empty();
|
||||
var options = $select.find('option');
|
||||
var current_date = null;
|
||||
var current_day_div = null;
|
||||
var current_offset = 0;
|
||||
var nb_days = 0;
|
||||
|
||||
for (var i=0; i<options.length; i++) {
|
||||
var $option = $(options[i]);
|
||||
if ($option.attr('value') == '') continue;
|
||||
var option_date = $option.data('date');
|
||||
if (option_date !== current_date) {
|
||||
var day_label = $option.text().split(' ', 3).join(' ');
|
||||
if (Intl && Intl.DateTimeFormat) {
|
||||
// create day label from actual date, this works for event agendas.
|
||||
var date = new Date(option_date);
|
||||
var month = new Intl.DateTimeFormat('fr', {month: 'long'}).format(date);
|
||||
day_label = date.getDate() + ' ' + month + ' ' + date.getFullYear();
|
||||
}
|
||||
var weekday = $option.data('weekday');
|
||||
nb_days += 1;
|
||||
current_day_div = $('<div><div class="head">' + weekday + '<br>' + day_label + '</div></div>');
|
||||
current_day_div.appendTo($('#form_{{widget.name}}_table'));
|
||||
current_date = option_date;
|
||||
}
|
||||
var day_time = $option.data('time');
|
||||
var option_span = $('<span class="selectable" data-idx="' + i + '">' + day_time + '</span>').appendTo(current_day_div);
|
||||
if ($option.attr('disabled')) {
|
||||
$(option_span).addClass('disabled').removeClass('selectable');
|
||||
}
|
||||
if ($option.attr('selected')) {
|
||||
current_offset = nb_days - 1;
|
||||
$(option_span).addClass('on');
|
||||
}
|
||||
}
|
||||
var go_prev = $('<button class="prev">←</button>');
|
||||
var go_next = $('<button class="next">→</button>');
|
||||
go_prev.prependTo($('#form_{{widget.name}}_table'));
|
||||
go_next.appendTo($('#form_{{widget.name}}_table'));
|
||||
go_prev.on('click', function() {
|
||||
current_offset = Math.max(0, current_offset - 1);
|
||||
display(current_offset);
|
||||
return false;
|
||||
});
|
||||
go_next.on('click', function() {
|
||||
current_offset = Math.min(current_offset + 1, Math.max(0, nb_days-column_count));
|
||||
display(current_offset);
|
||||
return false;
|
||||
});
|
||||
|
||||
function display(offset) {
|
||||
$('#form_{{widget.name}}_table > div').each(function(idx, elem) {
|
||||
if (idx >= offset && idx < offset+column_count) {
|
||||
$(elem).show();
|
||||
} else {
|
||||
$(elem).hide();
|
||||
}
|
||||
});
|
||||
if (go_prev.next().is(':visible')) {
|
||||
go_prev.prop('disabled', true);
|
||||
} else {
|
||||
go_prev.prop('disabled', null);
|
||||
}
|
||||
if (go_next.prev().is(':visible')) {
|
||||
go_next.prop('disabled', true);
|
||||
} else {
|
||||
go_next.prop('disabled', null);
|
||||
}
|
||||
current_offset = offset;
|
||||
}
|
||||
|
||||
function set_layout() {
|
||||
if ($select.parents('form').width() > 600) {
|
||||
// desktop layout
|
||||
column_count = 5;
|
||||
$table.removeClass('mobile');
|
||||
} else {
|
||||
// mobile layout
|
||||
column_count = 1;
|
||||
$table.addClass('mobile');
|
||||
}
|
||||
display(current_offset);
|
||||
t1 = new Date();
|
||||
}
|
||||
set_layout();
|
||||
var layout_change_timeout_id = null;
|
||||
$(window).on('resize', function() {
|
||||
clearTimeout(layout_change_timeout_id);
|
||||
layout_change_timeout_id = setTimeout(set_layout, 200);
|
||||
});
|
||||
|
||||
$('#form_{{widget.name}}_table span.selectable').on('click', function() {
|
||||
$('#form_{{widget.name}}_table span').removeClass('on');
|
||||
$(this).addClass('on');
|
||||
$('#form_{{widget.name}}').val($(options[$(this).data('idx')]).attr('value'));
|
||||
});
|
||||
});
|
||||
$select.trigger('wcs:options-change');
|
||||
});
|
||||
</script>
|
||||
{% if request.quixote_request.is_in_backoffice %}
|
||||
<style>
|
||||
div.meetings_table {
|
||||
margin-top: 1ex;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.meetings_table > div {
|
||||
flex: 0 1 auto;
|
||||
width: 20%;
|
||||
text-align: center;
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.meetings_table div span {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
padding: 0.3ex 0;
|
||||
}
|
||||
|
||||
div.meetings_table div span.disabled {
|
||||
opacity: 0.3;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
div.meetings_table div span.selectable.on {
|
||||
background: #215D9C;
|
||||
color: white;
|
||||
}
|
||||
|
||||
div.meetings_table div.head {
|
||||
padding-bottom: 1ex;
|
||||
}
|
||||
</style>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block widget-hint %}
|
||||
{% if widget.hint %}<div class="hint">{{widget.hint}}</div>{% endif %}
|
||||
{% endblock %}
|
Loading…
Reference in New Issue