Use tempusdominus and Popper for date time picker
issue9455 review327751002
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
* Use tempusdominus and Popper for date time picker
|
||||
* Support PYSON comparison of date and datetime
|
||||
* Customize bootstrap default style
|
||||
* Set field's name as input's name attribute
|
||||
|
||||
+1
-1
@@ -106,7 +106,7 @@ module.exports = function(grunt) {
|
||||
'bower_components/bootstrap',
|
||||
'bower_components/bootstrap/less',
|
||||
'bower_components/bootstrap-rtl-ondemand/less',
|
||||
'bower_components/eonasdan-bootstrap-datetimepicker/src/less',
|
||||
'bower_components/tempusdominus-bootstrap-3/src/less/',
|
||||
]
|
||||
},
|
||||
files: {
|
||||
|
||||
+3
-2
@@ -18,14 +18,15 @@
|
||||
"jquery": "^3",
|
||||
"bootstrap": "^3.3.7",
|
||||
"moment": "^2.10",
|
||||
"eonasdan-bootstrap-datetimepicker": "^4.17",
|
||||
"gettext.js": "^0.7",
|
||||
"c3": "^0.7",
|
||||
"papaparse": "^5.0",
|
||||
"fullcalendar": "^3.10.2",
|
||||
"mousetrap": "^1.6",
|
||||
"bootstrap-rtl-ondemand": "^3.3.4-ondemand",
|
||||
"Sortable": "sortablejs#^1.8.4"
|
||||
"Sortable": "sortablejs#^1.8.4",
|
||||
"tempusdominus-bootstrap-3": "bootstrap-datetimepicker-v5-alpha#^5.0.0-alpha8",
|
||||
"popper.js": "https://unpkg.com/popper.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"qunit": "^1.18"
|
||||
|
||||
+2
-1
@@ -13,7 +13,8 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<script type="text/javascript" src="bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="bower_components/moment/min/moment.min.js"></script>
|
||||
<script type="text/javascript" src="bower_components/moment/min/locales.min.js"></script>
|
||||
<script type="text/javascript" src="bower_components/eonasdan-bootstrap-datetimepicker/build/js/bootstrap-datetimepicker.min.js"></script>
|
||||
<script type="text/javascript" src="bower_components/popper.js/index.js"></script>
|
||||
<script type="text/javascript" src="bower_components/tempusdominus-bootstrap-3/build/js/tempusdominus-bootstrap-3.js"></script>
|
||||
<script type="text/javascript" src="bower_components/gettext.js/dist/gettext.min.js"></script>
|
||||
<script type="text/javascript" src="bower_components/d3/d3.min.js"></script>
|
||||
<script type="text/javascript" src="bower_components/c3/c3.min.js"></script>
|
||||
|
||||
+33
@@ -1146,4 +1146,37 @@ var Sao = {};
|
||||
jQuery('.modal.in:visible:last').focus()
|
||||
.next('.modal-backdrop.in').removeClass('hidden');
|
||||
}
|
||||
|
||||
// XXX: use Popper to place popup
|
||||
// cherry picked from tempusdominus-core.js
|
||||
$.fn.datetimepicker.prototype.constructor.Constructor.prototype._place = function _place(e) {
|
||||
var self = (e && e.data && e.data.picker) || this;
|
||||
if (self._options.sideBySide) {
|
||||
self._element.append(self.widget);
|
||||
return;
|
||||
}
|
||||
if (self._options.widgetParent) {
|
||||
self._options.widgetParent.append(self.widget);
|
||||
} else if (self._element.is('input')) {
|
||||
self._element.after(self.widget).parent();
|
||||
} else {
|
||||
self._element.children().first().after(self.widget);
|
||||
}
|
||||
|
||||
var reference = self.component[0];
|
||||
|
||||
if (!reference) {
|
||||
reference = self._element;
|
||||
}
|
||||
|
||||
// ReSharper disable once ConstructorCallNotUsed
|
||||
new Popper(reference, self.widget[0], {
|
||||
placement: 'bottom-start'
|
||||
});
|
||||
};
|
||||
|
||||
// XXX: Ensure to always return a date
|
||||
$.fn.datetimepicker.prototype.constructor.Constructor.prototype._getLastPickedDate = function _getLastPickedDate() {
|
||||
return this._dates[this._getLastPickedDateIndex()] || this.getMoment();
|
||||
};
|
||||
}());
|
||||
|
||||
+5
-1
@@ -2,7 +2,7 @@
|
||||
this repository contains the full copyright notices and license terms. */
|
||||
@import "bootstrap";
|
||||
@import "bootstrap-rtl";
|
||||
@import "bootstrap-datetimepicker-build";
|
||||
@import "_tempusdominus-bootstrap-3";
|
||||
@import "sao-variables";
|
||||
html[theme="default"] {
|
||||
@import "theme";
|
||||
@@ -681,6 +681,10 @@ input.column-boolean {
|
||||
}
|
||||
}
|
||||
|
||||
.bootstrap-datetimepicker-widget.dropdown-menu {
|
||||
z-index: 2000;
|
||||
}
|
||||
|
||||
@media screen and (max-width: @screen-xs-max) {
|
||||
.treeview {
|
||||
height: calc(100vh - 370px);
|
||||
|
||||
+15
-20
@@ -636,56 +636,51 @@
|
||||
Sao.ScreenContainer.BetweenDates, {
|
||||
build_entry: function(placeholder, el) {
|
||||
var entry = jQuery('<div/>', {
|
||||
'class': 'input-group input-group-sm'
|
||||
'class': 'input-group input-group-sm',
|
||||
'data-target-input': 'nearest',
|
||||
}).appendTo(el);
|
||||
entry.uniqueId();
|
||||
jQuery('<span/>', {
|
||||
'class': 'input-group-btn'
|
||||
}).append(jQuery('<button/>', {
|
||||
'class': 'datepickerbutton btn btn-default',
|
||||
'class': 'btn btn-default',
|
||||
type: 'button',
|
||||
'tabindex': -1,
|
||||
'aria-label': Sao.i18n.gettext("Open the calendar"),
|
||||
'title': Sao.i18n.gettext("Open the calendar"),
|
||||
'data-target': '#' + entry.attr('id'),
|
||||
'data-toggle': 'datetimepicker',
|
||||
}).append(Sao.common.ICONFACTORY.get_icon_img('tryton-date')
|
||||
)).appendTo(entry);
|
||||
jQuery('<input/>', {
|
||||
'class': 'form-control input-sm',
|
||||
'class': ('form-control input-sm datetimepicker-input ' +
|
||||
'mousetrap'),
|
||||
type: 'text',
|
||||
placeholder: placeholder,
|
||||
'data-target': '#' + entry.attr('id'),
|
||||
}).appendTo(entry);
|
||||
entry.datetimepicker({
|
||||
'locale': moment.locale(),
|
||||
'locale': Sao.common.moment_format(this.format),
|
||||
'keyBinds': null,
|
||||
});
|
||||
entry.data('DateTimePicker').format(this.format);
|
||||
// We must set the overflow of the modal-body
|
||||
// containing the input to visible to prevent vertical scrollbar
|
||||
// inherited from the auto overflow-x
|
||||
// (see http://www.w3.org/TR/css-overflow-3/#overflow-properties)
|
||||
entry.on('dp.hide', function() {
|
||||
entry.closest('.modal-body').css('overflow', '');
|
||||
});
|
||||
entry.on('dp.show', function() {
|
||||
entry.closest('.modal-body').css('overflow', 'visible');
|
||||
'useCurrent': false,
|
||||
});
|
||||
|
||||
var mousetrap = new Mousetrap(el[0]);
|
||||
|
||||
mousetrap.bind('enter', function(e, combo) {
|
||||
entry.data('DateTimePicker').date();
|
||||
entry.datetimepicker('date');
|
||||
});
|
||||
mousetrap.bind('=', function(e, combo) {
|
||||
e.preventDefault();
|
||||
entry.data('DateTimePicker').date(moment());
|
||||
entry.datetimepicker('date', moment());
|
||||
});
|
||||
|
||||
Sao.common.DATE_OPERATORS.forEach(function(operator) {
|
||||
mousetrap.bind(operator[0], function(e, combo) {
|
||||
e.preventDefault();
|
||||
var dp = entry.data('DateTimePicker');
|
||||
var date = dp.date();
|
||||
var date = entry.datetimepicker('date');
|
||||
date.add(operator[1]);
|
||||
dp.date(date);
|
||||
entry.datetimepicker('date', date);
|
||||
});
|
||||
});
|
||||
return entry;
|
||||
|
||||
+36
-53
@@ -1566,16 +1566,21 @@ function eval_pyson(value){
|
||||
this.date = this.labelled = jQuery('<div/>', {
|
||||
'class': ('input-group input-group-sm ' +
|
||||
'input-icon input-icon-primary'),
|
||||
'data-target-input': 'nearest',
|
||||
}).appendTo(this.el);
|
||||
this.date.uniqueId();
|
||||
Sao.common.ICONFACTORY.get_icon_img('tryton-date')
|
||||
.appendTo(jQuery('<div/>', {
|
||||
'class': 'datepickerbutton icon-input icon-primary',
|
||||
'class': 'icon-input icon-primary',
|
||||
'aria-label': Sao.i18n.gettext("Open the calendar"),
|
||||
'title': Sao.i18n.gettext("Open the calendar"),
|
||||
'data-target': '#' + this.date.attr('id'),
|
||||
'data-toggle': 'datetimepicker',
|
||||
}).appendTo(this.date));
|
||||
this.input = jQuery('<input/>', {
|
||||
'type': 'text',
|
||||
'class': 'form-control input-sm mousetrap',
|
||||
'class': 'form-control input-sm datetimepicker-input mousetrap',
|
||||
'data-target': '#' + this.date.attr('id'),
|
||||
'name': attributes.name,
|
||||
}).appendTo(this.date);
|
||||
this.date.datetimepicker({
|
||||
@@ -1584,32 +1589,18 @@ function eval_pyson(value){
|
||||
'useCurrent': false,
|
||||
});
|
||||
this.date.css('max-width', this._width);
|
||||
this.date.on('dp.change', this.focus_out.bind(this));
|
||||
// We must set the overflow of the treeview and modal-body
|
||||
// containing the input to visible to prevent vertical scrollbar
|
||||
// inherited from the auto overflow-x
|
||||
// (see http://www.w3.org/TR/css-overflow-3/#overflow-properties)
|
||||
this.date.on('dp.hide', function() {
|
||||
this.date.closest('.treeview').css('overflow', '');
|
||||
this.date.closest('.modal-body').css('overflow', '');
|
||||
this.date.closest('.form-group_').css('overflow', 'auto');
|
||||
}.bind(this));
|
||||
this.date.on('dp.show', function() {
|
||||
this.date.closest('.treeview').css('overflow', 'visible');
|
||||
this.date.closest('.modal-body').css('overflow', 'visible');
|
||||
this.date.closest('.form-group_').css('overflow', 'visible');
|
||||
}.bind(this));
|
||||
this.date.on('change.datetimepicker', this.focus_out.bind(this));
|
||||
var mousetrap = new Mousetrap(this.el[0]);
|
||||
|
||||
mousetrap.bind('enter', function(e, combo) {
|
||||
if (!this.date.find('input').prop('readonly')) {
|
||||
this.date.data('DateTimePicker').date();
|
||||
this.date.datetimepicker('date');
|
||||
}
|
||||
}.bind(this));
|
||||
mousetrap.bind('=', function(e, combo) {
|
||||
if (!this.date.find('input').prop('readonly')) {
|
||||
e.preventDefault();
|
||||
this.date.data('DateTimePicker').date(moment());
|
||||
this.date.datetimepicker('date', moment());
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
@@ -1619,10 +1610,9 @@ function eval_pyson(value){
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
var dp = this.date.data('DateTimePicker');
|
||||
var date = dp.date();
|
||||
var date = this.date.datetimepicker('date');
|
||||
date.add(operator[1]);
|
||||
dp.date(date);
|
||||
this.date.datetimepicker('date', date);
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
},
|
||||
@@ -1630,7 +1620,7 @@ function eval_pyson(value){
|
||||
return this.field.date_format(this.record);
|
||||
},
|
||||
get_value: function() {
|
||||
var value = this.date.data('DateTimePicker').date();
|
||||
var value = this.date.datetimepicker('date');
|
||||
if (value) {
|
||||
value.isDate = true;
|
||||
}
|
||||
@@ -1640,8 +1630,8 @@ function eval_pyson(value){
|
||||
var record = this.record;
|
||||
var field = this.field;
|
||||
if (record && field) {
|
||||
this.date.data('DateTimePicker').format(
|
||||
Sao.common.moment_format(this.get_format()));
|
||||
this.date.datetimepicker(
|
||||
'format', Sao.common.moment_format(this.get_format()));
|
||||
}
|
||||
Sao.View.Form.Date._super.display.call(this);
|
||||
var value;
|
||||
@@ -1650,11 +1640,11 @@ function eval_pyson(value){
|
||||
} else {
|
||||
value = null;
|
||||
}
|
||||
this.date.off('dp.change');
|
||||
this.date.off('change.datetimepicker');
|
||||
try {
|
||||
this.date.data('DateTimePicker').date(value);
|
||||
this.date.datetimepicker('date', value);
|
||||
} finally {
|
||||
this.date.on('dp.change', this.focus_out.bind(this));
|
||||
this.date.on('change.datetimepicker', this.focus_out.bind(this));
|
||||
}
|
||||
},
|
||||
focus: function() {
|
||||
@@ -1686,7 +1676,7 @@ function eval_pyson(value){
|
||||
return field.date_format(record) + ' ' + field.time_format(record);
|
||||
},
|
||||
get_value: function() {
|
||||
var value = this.date.data('DateTimePicker').date();
|
||||
var value = this.date.datetimepicker('date');
|
||||
if (value) {
|
||||
value.isDateTime = true;
|
||||
}
|
||||
@@ -1701,7 +1691,7 @@ function eval_pyson(value){
|
||||
return this.field.time_format(this.record);
|
||||
},
|
||||
get_value: function() {
|
||||
var value = this.date.data('DateTimePicker').date();
|
||||
var value = this.date.datetimepicker('date');
|
||||
if (value) {
|
||||
value.isTime = true;
|
||||
}
|
||||
@@ -4788,64 +4778,57 @@ function eval_pyson(value){
|
||||
Sao.View.Form.Dict.Date._super.create_widget.call(this);
|
||||
this.date = this.input.parent();
|
||||
this.date.addClass('input-icon input-icon-primary');
|
||||
this.date.attr('data-target-input', 'nearest');
|
||||
this.date.uniqueId();
|
||||
Sao.common.ICONFACTORY.get_icon_img('tryton-date')
|
||||
.appendTo(jQuery('<div/>', {
|
||||
'class': 'datepickerbutton icon-input icon-primary',
|
||||
'class': 'icon-input icon-primary',
|
||||
'aria-label': Sao.i18n.gettext("Open the calendar"),
|
||||
'title': Sao.i18n.gettext("Open the calendar"),
|
||||
'data-target': '#' + this.date.attr('id'),
|
||||
'data-toggle': 'datetimepicker',
|
||||
}).prependTo(this.date));
|
||||
this.input.addClass('datetimepicker-input');
|
||||
this.input.data('target', '#' + this.date.attr('id'));
|
||||
this.date.datetimepicker({
|
||||
'format': Sao.common.moment_format(this.format),
|
||||
'locale': moment.locale(),
|
||||
'keyBinds': null,
|
||||
'useCurrent': false,
|
||||
});
|
||||
this.date.on('dp.change',
|
||||
this.date.on('change.datetimepicker',
|
||||
this.parent_widget.focus_out.bind(this.parent_widget));
|
||||
// We must set the overflow of the treeview and modal-body
|
||||
// containing the input to visible to prevent vertical scrollbar
|
||||
// inherited from the auto overflow-x
|
||||
// (see http://www.w3.org/TR/css-overflow-3/#overflow-properties)
|
||||
this.date.on('dp.hide', function() {
|
||||
this.date.closest('.treeview').css('overflow', '');
|
||||
this.date.closest('.modal-body').css('overflow', '');
|
||||
}.bind(this));
|
||||
this.date.on('dp.show', function() {
|
||||
this.date.closest('.treeview').css('overflow', 'visible');
|
||||
this.date.closest('.modal-body').css('overflow', 'visible');
|
||||
}.bind(this));
|
||||
var mousetrap = new Mousetrap(this.el[0]);
|
||||
|
||||
mousetrap.bind(['enter', '='], function(e, combo) {
|
||||
if (e.which != Sao.common.RETURN_KEYCODE) {
|
||||
e.preventDefault();
|
||||
}
|
||||
this.date.data('DateTimePicker').date(moment());
|
||||
this.date.datetimepicker('date', moment());
|
||||
}.bind(this));
|
||||
|
||||
Sao.common.DATE_OPERATORS.forEach(function(operator) {
|
||||
mousetrap.bind(operator[0], function(e, combo) {
|
||||
e.preventDefault();
|
||||
var dp = this.date.data('DateTimePicker');
|
||||
var date = dp.date();
|
||||
var date = this.date.datetimepicker('date');
|
||||
date.add(operator[1]);
|
||||
dp.date(date);
|
||||
this.date.datetimepicker('date', date);
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
},
|
||||
get_value: function() {
|
||||
var value = this.date.data('DateTimePicker').date();
|
||||
var value = this.date.datetimepicker('date');
|
||||
if (value) {
|
||||
value.isDate = true;
|
||||
}
|
||||
return value;
|
||||
},
|
||||
set_value: function(value) {
|
||||
this.date.off('dp.change');
|
||||
this.date.off('change.datetimepicker');
|
||||
try {
|
||||
this.date.data('DateTimePicker').date(value);
|
||||
this.date.datetimepicker('date', value);
|
||||
} finally {
|
||||
this.date.on('dp.change',
|
||||
this.date.on('change.datetimepicker',
|
||||
this.parent_widget.focus_out.bind(this.parent_widget));
|
||||
}
|
||||
}
|
||||
@@ -4855,7 +4838,7 @@ function eval_pyson(value){
|
||||
class_: 'dict-datetime',
|
||||
format: '%x %X',
|
||||
get_value: function() {
|
||||
var value = this.date.data('DateTimePicker').date();
|
||||
var value = this.date.datetimepicker('date');
|
||||
if (value) {
|
||||
value.isDateTime = true;
|
||||
}
|
||||
|
||||
@@ -2559,6 +2559,11 @@
|
||||
init: function(view, attributes) {
|
||||
Sao.View.EditableTree.Date._super.init.call(
|
||||
this, view, attributes);
|
||||
// XXX: click event does not work in editable
|
||||
// so we manage it ourselves to toggle the picker.
|
||||
this.date.find('.icon-input').removeAttr('data-toggle').click(function() {
|
||||
this.date.datetimepicker('toggle');
|
||||
}.bind(this));
|
||||
Sao.View.EditableTree.editable_mixin(this);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user