Skip to content
This repository has been archived by the owner on Dec 30, 2022. It is now read-only.

Commit

Permalink
Use tempusdominus and Popper for date time picker
Browse files Browse the repository at this point in the history
issue9455
review327751002
  • Loading branch information
cedk committed Sep 12, 2020
1 parent 5365329 commit 8b111b8
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 78 deletions.
1 change: 1 addition & 0 deletions CHANGELOG
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion Gruntfile.js
Expand Up @@ -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: {
Expand Down
5 changes: 3 additions & 2 deletions bower.json
Expand Up @@ -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"
Expand Down
3 changes: 2 additions & 1 deletion index.html
Expand Up @@ -13,7 +13,8 @@
<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>
Expand Down
33 changes: 33 additions & 0 deletions src/sao.js
Expand Up @@ -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();
};
}());
6 changes: 5 additions & 1 deletion src/sao.less
Expand Up @@ -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";
Expand Down Expand Up @@ -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);
Expand Down
35 changes: 15 additions & 20 deletions src/screen.js
Expand Up @@ -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;
Expand Down
89 changes: 36 additions & 53 deletions src/view/form.js
Expand Up @@ -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({
Expand All @@ -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));

Expand All @@ -1619,18 +1610,17 @@ 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));
},
get_format: function() {
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;
}
Expand All @@ -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;
Expand All @@ -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() {
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -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));
}
}
Expand All @@ -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;
}
Expand Down
5 changes: 5 additions & 0 deletions src/view/tree.js
Expand Up @@ -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);
}
});
Expand Down

0 comments on commit 8b111b8

Please sign in to comment.