diff --git a/UI/WebServerResources/js/Common/sgTimepicker.directive.js b/UI/WebServerResources/js/Common/sgTimepicker.directive.js
index 3a42fcc4f..9170938f3 100644
--- a/UI/WebServerResources/js/Common/sgTimepicker.directive.js
+++ b/UI/WebServerResources/js/Common/sgTimepicker.directive.js
@@ -5,307 +5,319 @@
.module('SOGo.Common')
.directive('sgTimePane', timePaneDirective);
- function timePaneDirective() {
- return {
- template:
- '
' +
- '
' +
- '
' +
- '' +
- '
' +
+ template: [
+ // Buttons are not in the tab order because users can open the hours pane via keyboard
+ // interaction on the text input, and multiple tab stops for one component (picker)
+ // may be confusing.
+ '
',
+ ' access_time',
+ '',
+ '
',
+ // This pane will be detached from here and re-attached to the document body.
+ '
',
+ '
',
+ '
',
+ ' ',
+ '
',
+ '
'
+ ].join(''),
require: ['ngModel', 'sgTimepicker'],
scope: {
placeholder: '@mdPlaceholder'
@@ -398,7 +411,7 @@
* ngInject @constructor
*/
function TimePickerCtrl($scope, $element, $attrs, $compile, $timeout, $mdConstant, $mdTheming,
- $mdUtil, $mdDateLocale, $$mdDateUtil, $$rAF) {
+ $mdUtil, $mdDateLocale, $$mdDateUtil, $$rAF) {
/** @final */
this.$compile = $compile;
@@ -494,8 +507,8 @@
});
}
- TimePickerCtrl.$inject = ["$scope", "$element", "$attrs", "$compile", "$timeout", "$mdConstant", "$mdTheming",
- "$mdUtil", "$mdDateLocale", "$$mdDateUtil", "$$rAF"];
+ TimePickerCtrl.$inject = ["$scope", "$element", "$attrs", "$compile", "$timeout", "$mdConstant", "$mdTheming",
+ "$mdUtil", "$mdDateLocale", "$$mdDateUtil", "$$rAF"];
/**
* Sets up the controller's reference to ngModelController.
@@ -505,21 +518,21 @@
this.ngModelCtrl = ngModelCtrl;
var self = this;
ngModelCtrl.$render = function() {
- self.time = self.ngModelCtrl.$viewValue;
- self.inputElement.value = self.formatTime(self.time);
+ self.time = self.ngModelCtrl.$viewValue;
+ self.inputElement.value = self.formatTime(self.time);
self.resizeInputElement();
};
};
- TimePickerCtrl.prototype.formatTime = function(time) {
- var t = new Date(time);
- if(t) {
- var h= t.getHours();
- var m= t.getMinutes();
- return (h<10?('0'+ h) : h) + ':' + (m<10?'0'+ m : m);
- }
- else return '';
- };
+ TimePickerCtrl.prototype.formatTime = function(time) {
+ var t = new Date(time);
+ if(t) {
+ var h= t.getHours();
+ var m= t.getMinutes();
+ return (h<10?('0'+ h) : h) + ':' + (m<10?'0'+ m : m);
+ }
+ else return '';
+ };
/**
* Attach event listeners for both the text input and the md-time.
* Events are used instead of ng-model so that updates don't infinitely update the other
@@ -529,21 +542,21 @@
var self = this;
self.$scope.$on('sg-time-pane-change', function(event, data) {
- var time = new Date(data.date);
- self.ngModelCtrl.$setViewValue(time);
- self.time = time;
- self.inputElement.value = self.formatTime(self.time);
- if(data.changed == 'minutes') {
- self.closeTimePane();
- }
- self.resizeInputElement();
- self.inputContainer.classList.remove(INVALID_CLASS);
+ var time = new Date(data.date);
+ self.ngModelCtrl.$setViewValue(time);
+ self.time = time;
+ self.inputElement.value = self.formatTime(self.time);
+ if(data.changed == 'minutes') {
+ self.closeTimePane();
+ }
+ self.resizeInputElement();
+ self.inputContainer.classList.remove(INVALID_CLASS);
});
var ngElement = angular.element(self.inputElement);
ngElement.on('input', angular.bind(self, self.resizeInputElement));
ngElement.on('input', self.$mdUtil.debounce(self.handleInputEvent,
- DEFAULT_DEBOUNCE_INTERVAL, self));
+ DEFAULT_DEBOUNCE_INTERVAL, self));
};
/** Attach event listeners for user interaction. */
@@ -609,19 +622,19 @@
*/
TimePickerCtrl.prototype.handleInputEvent = function(self) {
var inputString = this.inputElement.value;
- var arr = inputString.split(':');
- if(arr.length < 2) {return;}
- var h=Number(arr[0]);
- var m=Number(arr[1]);
- var newVal = new Date(this.time);
- if (h && h>=0 && h<=23 && m && m>=0 && m<= 59 && angular.isDate(newVal)) {
- newVal.setHours(h);
- newVal.setMinutes(m);
- this.ngModelCtrl.$setViewValue(newVal);
- this.time = newVal;
- this.inputContainer.classList.remove(INVALID_CLASS);
- }
- else {
+ var arr = inputString.split(':');
+ if(arr.length < 2) {return;}
+ var h=Number(arr[0]);
+ var m=Number(arr[1]);
+ var newVal = new Date(this.time);
+ if (h && h>=0 && h<=23 && m && m>=0 && m<= 59 && angular.isDate(newVal)) {
+ newVal.setHours(h);
+ newVal.setMinutes(m);
+ this.ngModelCtrl.$setViewValue(newVal);
+ this.time = newVal;
+ this.inputContainer.classList.remove(INVALID_CLASS);
+ }
+ else {
// If there's an input string, it's an invalid time.
this.inputContainer.classList.toggle(INVALID_CLASS, inputString);
}
@@ -631,6 +644,7 @@
TimePickerCtrl.prototype.attachTimePane = function() {
var timePane = this.timePane;
this.$element.addClass('sg-timepicker-open');
+ this.$element.find('button').addClass('md-primary');
var elementRect = this.inputContainer.getBoundingClientRect();
var bodyRect = document.body.getBoundingClientRect();
@@ -654,6 +668,7 @@
/** Detach the floating time pane from the document. */
TimePickerCtrl.prototype.detachTimePane = function() {
this.$element.removeClass('sg-timepicker-open');
+ this.$element.find('button').removeClass('md-primary');
this.timePane.classList.remove('md-pane-open');
if (this.timePane.parentNode) {
@@ -672,7 +687,7 @@
this.isTimeOpen = true;
this.timePaneOpenedFrom = event.target;
this.attachTimePane();
- this.focusTime();
+ //this.focusTime();
// Because the time pane is attached directly to the body, it is possible that the
// rest of the component (input, etc) is in a different scrolling container, such as
@@ -711,7 +726,7 @@
// Use a timeout in order to allow the time to be rendered, as it is gated behind an ng-if.
var self = this;
this.$mdUtil.nextTick(function() {
- var ctrl = self.getTimePaneCtrl();
+ var ctrl = self.getTimePaneCtrl();
self.getTimePaneCtrl().focus(null, ctrl);
}, false);
};
diff --git a/UI/WebServerResources/scss/components/datepicker/datePicker.scss b/UI/WebServerResources/scss/components/datepicker/datePicker.scss
index dcf140f13..b28ff445d 100644
--- a/UI/WebServerResources/scss/components/datepicker/datePicker.scss
+++ b/UI/WebServerResources/scss/components/datepicker/datePicker.scss
@@ -1,6 +1,14 @@
/// datePicker.scss -*- Mode: scss; indent-tabs-mode: nil; basic-offset: 2 -*-
@import 'extends-datePicker';
-md-datepicker {
- background: transparent;
+.md-calendar-scroll-container,
+.md-datepicker-input-mask {
+ // Let the content set the container width instead of using a fixed width
+ width: auto;
}
+
+.md-datepicker-input-container {
+ // Gain some space
+ margin-left: 0;
+}
+
diff --git a/UI/WebServerResources/scss/components/timepicker/timepicker.scss b/UI/WebServerResources/scss/components/timepicker/timepicker.scss
index 06b1fc045..f1dc754b1 100644
--- a/UI/WebServerResources/scss/components/timepicker/timepicker.scss
+++ b/UI/WebServerResources/scss/components/timepicker/timepicker.scss
@@ -1,18 +1,6 @@
/** Styles for sgTimePane. */
$sg-time-pane-cell-size: 40px;
-
-$md-calendar-cell-size: 44px !default;
-$md-calendar-header-height: 40px;
-$md-calendar-cell-emphasis-size: 40px !default;
-$md-calendar-side-padding: 16px !default;
-$md-calendar-weeks-to-show: 7 !default;
-
-$md-calendar-month-label-padding: 8px !default;
-$md-calendar-month-label-font-size: 13px !default;
-
-$md-calendar-width: (7 * $md-calendar-cell-size) + (2 * $md-calendar-side-padding);
-$md-calendar-height:
-($md-calendar-weeks-to-show * $md-calendar-cell-size) + $md-calendar-header-height;
+$sg-time-width: (12 * $sg-time-pane-cell-size) + (2 * $md-calendar-side-padding);
sg-time-pane {
font-size: 13px;
@@ -20,13 +8,21 @@ sg-time-pane {
}
.hours-pane {
+ // TODO: should use background-200
border-bottom: solid 1px rgb(224,224,224);
}
.toggle-pane {
+ // TODO: should use background-200
border-top: solid 1px rgb(224,224,224);
}
+.hours-pane,
+.min1,
+.min5 {
+ padding: 0 $md-calendar-side-padding;
+}
+
.md-button.md-fab.hourBtn,
.md-button.md-fab.minuteBtn,
.md-button.md-fab.toggleBtn,
@@ -35,45 +31,30 @@ sg-time-pane {
.md-button.md-fab.toggleBtn.md-focused,
.md-button.md-fab.hourBtn.md-focus,
.md-button.md-fab.minuteBtn.md-focus,
-.md-button.md-fab.toggleBtn.md-focus{
+.md-button.md-fab.toggleBtn.md-focus {
min-width: 10px;
min-height: 10px;
- background-color: transparent;
border-color: transparent;
- font-family: Roboto, 'Helvetica Neue', sans-serif;
- font-size: 16px;
font-weight:normal;
color: rgba(0,0,0,0.5);
- height:$sg-time-pane-cell-size;
- width:$sg-time-pane-cell-size;
+ height: $sg-time-pane-cell-size;
+ width: $sg-time-pane-cell-size;
line-height: $sg-time-pane-cell-size;
box-shadow: none;
- margin: 2px;
+ margin: 0;
+ &:not(.md-bg):not(.toggleBtn) {
+ background-color: transparent;
+ &:hover {
+ background-color: lightgrey;
+ color: #666666;
+ }
+ }
}
-.md-button.md-fab.toggleBtn{
- background-color: rgb(63, 81, 181);
+.md-button.md-fab.toggleBtn {
color: white;
margin: 5px;
}
-.md-button.md-fab.hourBtn:hover, .md-button.md-fab.minuteBtn:hover {
- background-color: lightgrey;
- color: #666666;
-}
-
-.md-button.md-fab.hourBtn.md-primary, .md-button.md-fab.minuteBtn.md-primary,
-.md-button.md-fab.hourBtn.md-primary:hover, .md-button.md-fab.minuteBtn.md-primary:hover,
-.md-button.md-fab.hourBtn.md-primary.md-focus, .md-button.md-fab.minuteBtn.md-primary.md-focus,
-.md-button.md-fab.hourBtn.md-primary.md-focused, .md-button.md-fab.minuteBtn.md-primary.md-focused{
- background-color: lightgrey;
- color: rgb(63, 81, 181);;
-}
-
-/** Styles for sgTimepicker. */
-$md-datepicker-button-gap: 12px; // Space between the text input and the calendar-icon button.
-$md-datepicker-border-bottom-gap: 5px; // Space between input and the grey underline.
-$md-datepicker-open-animation-duration: 0.2s;
-
sg-timepicker {
// Don't let linebreaks happen between the open icon-button and the input.
white-space: nowrap;
@@ -87,60 +68,40 @@ sg-timepicker {
background: none;
}
-// The input into which the user can type the date.
+// The input into which the user can type the time.
.sg-timepicker-input {
- //@include md-flat-input();
- min-width: 120px;
- max-width: $md-calendar-width - $md-datepicker-button-gap;
- background: inherit;
- border: none;
+ @extend .md-datepicker-input;
}
-// Container for the datepicker input.
+// Container for the timepicker input.
.sg-timepicker-input-container {
- // Position relative in order to absolutely position the down-triangle button within.
- position: relative;
-
- padding-bottom: $md-datepicker-border-bottom-gap;
- border-bottom-width: 1px;
- border-bottom-style: solid;
- border-bottom-color: rgb(224,224,224);
-
- display: inline-block;
- width: auto;
- margin-left: $md-datepicker-button-gap;
+ @extend .md-datepicker-input-container;
&.sg-timepicker-focused {
border-bottom-width: 2px;
}
+
+ // From datePicker-theme.scss
+ // TODO: should use background-300
+ border-bottom-color: rgb(224,224,224);
}
// Floating pane that contains the time at the bottom of the input.
.sg-timepicker-time-pane {
- position: absolute;
- top: 0;
- left: 0;
- z-index: $z-index-menu;
+ @extend .md-datepicker-calendar-pane;
- border-width: 1px;
- border-style: solid;
- background: inherit;
+ // Because blocks of 1-minute cells are allowed to wrap on multiple rows,
+ // we limit the maximum size of the time pane
+ max-width: $sg-time-width;
+
+ // From datePicker-theme.css
border-color: rgb(224,224,224);
- box-shadow: rgba(0, 0, 0, 0.137255) 0 3px 1px -2px, rgba(0, 0, 0, 0.0980392) 0 2px 2px 0, rgba(0, 0, 0, 0.0823529) 0 1px 5px 0;
- transform: scale(0);
- transform-origin: 0 0;
- //transition: transform $md-datepicker-open-animation-duration $swift-ease-out-timing-function;
-
- &.md-pane-open {
- transform: scale(1);
- }
}
// Portion of the floating panel that sits, invisibly, on top of the input.
.sg-timepicker-input-mask {
height: 40px;
- width: $md-calendar-width;
position: relative;
background: transparent;
@@ -149,12 +110,7 @@ sg-timepicker {
}
.sg-timepicker-input-mask-opaque {
- position: absolute;
- right: 0;
- left: 120px;
- background: white;
-
- height: 100%;
+ @extend .md-datepicker-input-mask-opaque;
}
// The time portion of the floating pane (vs. the input mask).
@@ -177,38 +133,23 @@ sg-timepicker {
// Down triangle/arrow indicating that the datepicker can be opened.
// We can do this entirely with CSS without needing to load an icon.
// See https://css-tricks.com/snippets/css/css-triangle/
-$md-date-arrow-size: 5px;
+//$md-date-arrow-size: 5px;
.sg-timepicker-expand-triangle {
- // Center the triangle inside of the button so that the
- // ink ripple origin looks correct.
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
-
- width: 0;
- height: 0;
- border-left: $md-date-arrow-size solid transparent;
- border-right: $md-date-arrow-size solid transparent;
- border-top: $md-date-arrow-size solid rgba(black, 0.20);
+ @extend .md-datepicker-expand-triangle;
}
// Button containing the down "disclosure" triangle/arrow.
.sg-timepicker-triangle-button {
- position: absolute;
- right: 0;
- top: 0;
+ @extend .md-datepicker-triangle-button;
- // TODO(jelbourn): This position isn't great on all platforms.
- transform: translateY(-25%) translateX(45%);
+ // From datepicker-theme.scss
+ &:hover .sg-timepicker-expand-triangle {
+ border-top-color: rgba(0,0,0,0.54);
+ }
}
-// Need crazy specificity to override .md-button.md-icon-button.
-// Only apply this high specifiy to the property we need to override.
.sg-timepicker-triangle-button.md-button.md-icon-button {
- height: 100%;
- width: 36px;
- position: absolute;
+ @extend .md-datepicker-triangle-button.md-button.md-icon-button;
}
// Disabled state for all elements of the picker.