From 78a4398dde80d8ce086bc272b62b26ed0b8fec6f Mon Sep 17 00:00:00 2001 From: Farid Inawan Date: Sun, 15 Jul 2018 04:04:14 +0700 Subject: [PATCH] Update Timepicker (#1354) With the new one, the widget is not hidden in scrolling container --- plugins/timepicker/bootstrap-timepicker.css | 37 +- plugins/timepicker/bootstrap-timepicker.js | 722 ++++++++++++------ .../timepicker/bootstrap-timepicker.min.css | 2 +- .../timepicker/bootstrap-timepicker.min.js | 6 +- 4 files changed, 533 insertions(+), 234 deletions(-) diff --git a/plugins/timepicker/bootstrap-timepicker.css b/plugins/timepicker/bootstrap-timepicker.css index 873e0c723..64f781b26 100644 --- a/plugins/timepicker/bootstrap-timepicker.css +++ b/plugins/timepicker/bootstrap-timepicker.css @@ -23,16 +23,16 @@ left: auto; right: 13px; } -.bootstrap-timepicker .add-on { +.bootstrap-timepicker .input-group-addon { cursor: pointer; } -.bootstrap-timepicker .add-on i { +.bootstrap-timepicker .input-group-addon i { display: inline-block; width: 16px; height: 16px; } .bootstrap-timepicker-widget.dropdown-menu { - padding: 2px 3px 2px 2px; + padding: 4px; } .bootstrap-timepicker-widget.dropdown-menu.open { display: inline-block; @@ -43,9 +43,7 @@ border-right: 7px solid transparent; content: ""; display: inline-block; - left: 9px; position: absolute; - top: -7px; } .bootstrap-timepicker-widget.dropdown-menu:after { border-bottom: 6px solid #FFFFFF; @@ -53,10 +51,36 @@ border-right: 6px solid transparent; content: ""; display: inline-block; - left: 10px; position: absolute; +} +.bootstrap-timepicker-widget.timepicker-orient-left:before { + left: 6px; +} +.bootstrap-timepicker-widget.timepicker-orient-left:after { + left: 7px; +} +.bootstrap-timepicker-widget.timepicker-orient-right:before { + right: 6px; +} +.bootstrap-timepicker-widget.timepicker-orient-right:after { + right: 7px; +} +.bootstrap-timepicker-widget.timepicker-orient-top:before { + top: -7px; +} +.bootstrap-timepicker-widget.timepicker-orient-top:after { top: -6px; } +.bootstrap-timepicker-widget.timepicker-orient-bottom:before { + bottom: -7px; + border-bottom: 0; + border-top: 7px solid #999; +} +.bootstrap-timepicker-widget.timepicker-orient-bottom:after { + bottom: -6px; + border-bottom: 0; + border-top: 6px solid #ffffff; +} .bootstrap-timepicker-widget a.btn, .bootstrap-timepicker-widget input { border-radius: 4px; @@ -96,6 +120,7 @@ } .bootstrap-timepicker-widget table td a i { margin-top: 2px; + font-size: 18px; } .bootstrap-timepicker-widget table td input { width: 25px; diff --git a/plugins/timepicker/bootstrap-timepicker.js b/plugins/timepicker/bootstrap-timepicker.js index 3068c940c..bbb923c5e 100644 --- a/plugins/timepicker/bootstrap-timepicker.js +++ b/plugins/timepicker/bootstrap-timepicker.js @@ -1,16 +1,14 @@ -//TODO: move arrow styles and button click code into configurable items, with defaults matching the existing code - /*! -* Timepicker Component for Twitter Bootstrap -* -* Copyright 2013 Joris de Wit -* -* Contributors https://github.com/jdewit/bootstrap-timepicker/graphs/contributors -* -* For the full copyright and license information, please view the LICENSE -* file that was distributed with this source code. -*/ -(function($, window, document, undefined) { + * Timepicker Component for Twitter Bootstrap + * + * Copyright 2013 Joris de Wit and bootstrap-timepicker contributors + * + * Contributors https://github.com/jdewit/bootstrap-timepicker/graphs/contributors + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +(function($, window, document) { 'use strict'; // TIMEPICKER PUBLIC CLASS DEFINITION @@ -19,18 +17,33 @@ this.$element = $(element); this.defaultTime = options.defaultTime; this.disableFocus = options.disableFocus; + this.disableMousewheel = options.disableMousewheel; this.isOpen = options.isOpen; this.minuteStep = options.minuteStep; this.modalBackdrop = options.modalBackdrop; + this.orientation = options.orientation; this.secondStep = options.secondStep; + this.snapToStep = options.snapToStep; this.showInputs = options.showInputs; this.showMeridian = options.showMeridian; this.showSeconds = options.showSeconds; this.template = options.template; this.appendWidgetTo = options.appendWidgetTo; - this.upArrowStyle = options.upArrowStyle; - this.downArrowStyle = options.downArrowStyle; - this.containerClass = options.containerClass; + this.showWidgetOnAddonClick = options.showWidgetOnAddonClick; + this.icons = options.icons; + this.maxHours = options.maxHours; + this.explicitMode = options.explicitMode; // If true 123 = 1:23, 12345 = 1:23:45, else invalid. + + this.handleDocumentClick = function (e) { + var self = e.data.scope; + // This condition was inspired by bootstrap-datepicker. + // The element the timepicker is invoked on is the input but it has a sibling for addon/button. + if (!(self.$element.parent().find(e.target).length || + self.$widget.is(e.target) || + self.$widget.find(e.target).length)) { + self.hideWidget(); + } + }; this._init(); }; @@ -38,46 +51,41 @@ Timepicker.prototype = { constructor: Timepicker, - _init: function() { var self = this; - if (this.$element.parent().hasClass('input-append') || this.$element.parent().hasClass('input-prepend')) { - if (this.$element.parent('.input-append, .input-prepend').find('.add-on').length) { - this.$element.parent('.input-append, .input-prepend').find('.add-on').on({ - 'click.timepicker': $.proxy(this.showWidget, this) - }); - } else { - this.$element.closest(this.containerClass).find('.add-on').on({ - 'click.timepicker': $.proxy(this.showWidget, this) - }); - } - + if (this.showWidgetOnAddonClick && (this.$element.parent().hasClass('input-group') && this.$element.parent().hasClass('bootstrap-timepicker'))) { + this.$element.parent('.input-group.bootstrap-timepicker').find('.input-group-addon').on({ + 'click.timepicker': $.proxy(this.showWidget, this) + }); this.$element.on({ 'focus.timepicker': $.proxy(this.highlightUnit, this), 'click.timepicker': $.proxy(this.highlightUnit, this), 'keydown.timepicker': $.proxy(this.elementKeydown, this), - 'blur.timepicker': $.proxy(this.blurElement, this) + 'blur.timepicker': $.proxy(this.blurElement, this), + 'mousewheel.timepicker DOMMouseScroll.timepicker': $.proxy(this.mousewheel, this) }); } else { if (this.template) { this.$element.on({ 'focus.timepicker': $.proxy(this.showWidget, this), 'click.timepicker': $.proxy(this.showWidget, this), - 'blur.timepicker': $.proxy(this.blurElement, this) + 'blur.timepicker': $.proxy(this.blurElement, this), + 'mousewheel.timepicker DOMMouseScroll.timepicker': $.proxy(this.mousewheel, this) }); } else { this.$element.on({ 'focus.timepicker': $.proxy(this.highlightUnit, this), 'click.timepicker': $.proxy(this.highlightUnit, this), 'keydown.timepicker': $.proxy(this.elementKeydown, this), - 'blur.timepicker': $.proxy(this.blurElement, this) + 'blur.timepicker': $.proxy(this.blurElement, this), + 'mousewheel.timepicker DOMMouseScroll.timepicker': $.proxy(this.mousewheel, this) }); } } if (this.template !== false) { - this.$widget = $(this.getTemplate()).prependTo(this.$element.parents(this.appendWidgetTo)).on('click', $.proxy(this.widgetClick, this)); + this.$widget = $(this.getTemplate()).on('click', $.proxy(this.widgetClick, this)); } else { this.$widget = false; } @@ -86,7 +94,8 @@ this.$widget.find('input').each(function() { $(this).on({ 'click.timepicker': function() { $(this).select(); }, - 'keydown.timepicker': $.proxy(self.widgetKeydown, self) + 'keydown.timepicker': $.proxy(self.widgetKeydown, self), + 'keyup.timepicker': $.proxy(self.widgetKeyup, self) }); }); } @@ -95,10 +104,19 @@ }, blurElement: function() { - this.highlightedUnit = undefined; + this.highlightedUnit = null; this.updateFromElementVal(); }, + clear: function() { + this.hour = ''; + this.minute = ''; + this.second = ''; + this.meridian = ''; + + this.$element.val(''); + }, + decrementHour: function() { if (this.showMeridian) { if (this.hour === 1) { @@ -115,13 +133,12 @@ this.hour--; } } else { - if (this.hour === 0) { - this.hour = 23; + if (this.hour <= 0) { + this.hour = this.maxHours - 1; } else { this.hour--; } } - this.update(); }, decrementMinute: function(step) { @@ -139,7 +156,6 @@ } else { this.minute = newVal; } - this.update(); }, decrementSecond: function() { @@ -151,32 +167,25 @@ } else { this.second = newVal; } - this.update(); }, elementKeydown: function(e) { - switch (e.keyCode) { + switch (e.which) { case 9: //tab - this.updateFromElementVal(); - - switch (this.highlightedUnit) { - case 'hour': - e.preventDefault(); + if (e.shiftKey) { + if (this.highlightedUnit === 'hour') { + this.hideWidget(); + break; + } + this.highlightPrevUnit(); + } else if ((this.showMeridian && this.highlightedUnit === 'meridian') || (this.showSeconds && this.highlightedUnit === 'second') || (!this.showMeridian && !this.showSeconds && this.highlightedUnit ==='minute')) { + this.hideWidget(); + break; + } else { this.highlightNextUnit(); - break; - case 'minute': - if (this.showMeridian || this.showSeconds) { - e.preventDefault(); - this.highlightNextUnit(); - } - break; - case 'second': - if (this.showMeridian) { - e.preventDefault(); - this.highlightNextUnit(); - } - break; } + e.preventDefault(); + this.updateFromElementVal(); break; case 27: // escape this.updateFromElementVal(); @@ -206,11 +215,12 @@ this.highlightMeridian(); break; } + this.update(); break; case 39: // right arrow e.preventDefault(); - this.updateFromElementVal(); this.highlightNextUnit(); + this.updateFromElementVal(); break; case 40: // down arrow e.preventDefault(); @@ -232,18 +242,12 @@ this.highlightMeridian(); break; } + + this.update(); break; } }, - formatTime: function(hour, minute, second, meridian) { - hour = hour < 10 ? '0' + hour : hour; - minute = minute < 10 ? '0' + minute : minute; - second = second < 10 ? '0' + second : second; - - return hour + ':' + minute + (this.showSeconds ? ':' + second : '') + (this.showMeridian ? ' ' + meridian : ''); - }, - getCursorPosition: function() { var input = this.$element.get(0); @@ -270,10 +274,10 @@ templateContent; if (this.showInputs) { - hourTemplate = ''; - minuteTemplate = ''; - secondTemplate = ''; - meridianTemplate = ''; + hourTemplate = ''; + minuteTemplate = ''; + secondTemplate = ''; + meridianTemplate = ''; } else { hourTemplate = ''; minuteTemplate = ''; @@ -283,16 +287,16 @@ templateContent = ''+ ''+ - ''+ + ''+ ''+ - ''+ + ''+ (this.showSeconds ? ''+ - '' + '' : '') + (this.showMeridian ? ''+ - '' + '' : '') + ''+ ''+ @@ -309,16 +313,16 @@ : '') + ''+ ''+ - ''+ + ''+ ''+ - ''+ + ''+ (this.showSeconds ? ''+ - '' + '' : '') + (this.showMeridian ? ''+ - '' + '' : '') + ''+ '
   
  
'; @@ -327,7 +331,7 @@ case 'modal': template = '