changeset ebf7626f0631 in sao:default
details: https://hg.tryton.org/sao?cmd=changeset;node=ebf7626f0631
description:
        Use tempusdominus and Popper for date time picker

        issue9455
        review327751002
diffstat:

 CHANGELOG        |   1 +
 Gruntfile.js     |   2 +-
 bower.json       |   5 +-
 index.html       |   3 +-
 src/sao.js       |  33 ++++++++++++++++++++
 src/sao.less     |   6 +++-
 src/screen.js    |  35 +++++++++------------
 src/view/form.js |  89 ++++++++++++++++++++++---------------------------------
 src/view/tree.js |   5 +++
 9 files changed, 101 insertions(+), 78 deletions(-)

diffs (426 lines):

diff -r 0e62c2943fee -r ebf7626f0631 CHANGELOG
--- a/CHANGELOG Sat Sep 12 01:25:35 2020 +0200
+++ b/CHANGELOG Sat Sep 12 09:58:20 2020 +0200
@@ -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
diff -r 0e62c2943fee -r ebf7626f0631 Gruntfile.js
--- a/Gruntfile.js      Sat Sep 12 01:25:35 2020 +0200
+++ b/Gruntfile.js      Sat Sep 12 09:58:20 2020 +0200
@@ -106,7 +106,7 @@
                     '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: {
diff -r 0e62c2943fee -r ebf7626f0631 bower.json
--- a/bower.json        Sat Sep 12 01:25:35 2020 +0200
+++ b/bower.json        Sat Sep 12 09:58:20 2020 +0200
@@ -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"
diff -r 0e62c2943fee -r ebf7626f0631 index.html
--- a/index.html        Sat Sep 12 01:25:35 2020 +0200
+++ b/index.html        Sat Sep 12 09:58:20 2020 +0200
@@ -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>
diff -r 0e62c2943fee -r ebf7626f0631 src/sao.js
--- a/src/sao.js        Sat Sep 12 01:25:35 2020 +0200
+++ b/src/sao.js        Sat Sep 12 09:58:20 2020 +0200
@@ -1146,4 +1146,37 @@
         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();
+    };
 }());
diff -r 0e62c2943fee -r ebf7626f0631 src/sao.less
--- a/src/sao.less      Sat Sep 12 01:25:35 2020 +0200
+++ b/src/sao.less      Sat Sep 12 09:58:20 2020 +0200
@@ -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 @@
     }
 }
 
+.bootstrap-datetimepicker-widget.dropdown-menu {
+    z-index: 2000;
+}
+
 @media screen and (max-width: @screen-xs-max) {
     .treeview {
         height: calc(100vh - 370px);
diff -r 0e62c2943fee -r ebf7626f0631 src/screen.js
--- a/src/screen.js     Sat Sep 12 01:25:35 2020 +0200
+++ b/src/screen.js     Sat Sep 12 09:58:20 2020 +0200
@@ -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;
diff -r 0e62c2943fee -r ebf7626f0631 src/view/form.js
--- a/src/view/form.js  Sat Sep 12 01:25:35 2020 +0200
+++ b/src/view/form.js  Sat Sep 12 09:58:20 2020 +0200
@@ -1566,16 +1566,21 @@
             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 @@
                 '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 @@
                         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 @@
             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 @@
             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 @@
             } 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 @@
             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 @@
             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 @@
             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 @@
         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;
             }
diff -r 0e62c2943fee -r ebf7626f0631 src/view/tree.js
--- a/src/view/tree.js  Sat Sep 12 01:25:35 2020 +0200
+++ b/src/view/tree.js  Sat Sep 12 09:58:20 2020 +0200
@@ -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);
         }
     });

Reply via email to