diff --git a/UI/Templates/SchedulerUI/UIxTaskEditorTemplate.wox b/UI/Templates/SchedulerUI/UIxTaskEditorTemplate.wox
index 87c2384f0..e79ef33c5 100644
--- a/UI/Templates/SchedulerUI/UIxTaskEditorTemplate.wox
+++ b/UI/Templates/SchedulerUI/UIxTaskEditorTemplate.wox
@@ -80,15 +80,35 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+ remove_circle
+
+
+
+
+ add_circle
+
+
+
+
+
+
+
+
+
+ remove_circle
+
+
+
+
+ add_circle
+
+
diff --git a/UI/WebServerResources/js/Scheduler/Component.service.js b/UI/WebServerResources/js/Scheduler/Component.service.js
index b62be10b0..5d02b7792 100644
--- a/UI/WebServerResources/js/Scheduler/Component.service.js
+++ b/UI/WebServerResources/js/Scheduler/Component.service.js
@@ -345,8 +345,17 @@
if (this.startDate)
this.start = new Date(this.startDate.substring(0,10) + ' ' + this.startDate.substring(11,16));
+ else if (this.type == 'appointment') {
+ this.start = new Date();
+ }
+
if (this.endDate)
this.end = new Date(this.endDate.substring(0,10) + ' ' + this.endDate.substring(11,16));
+ else if (this.type == 'appointment') {
+ this.end = new Date();
+ this.end.addHours(1);
+ }
+
if (this.dueDate)
this.due = new Date(this.dueDate.substring(0,10) + ' ' + this.dueDate.substring(11,16));
@@ -719,6 +728,45 @@
}
};
+ /**
+ * @function $addDueDate
+ * @memberof Component.prototype
+ * @desc Add a due date
+ */
+ Component.prototype.$addDueDate = function() {
+ this.due = new Date();
+ this.dueDate = this.due.toISOString();
+ };
+
+ /**
+ * @function $deleteDueDate
+ * @memberof Component.prototype
+ * @desc Delete a due date
+ */
+ Component.prototype.$deleteDueDate = function() {
+ delete this.due;
+ delete this.dueDate;
+ };
+
+ /**
+ * @function $addStartDate
+ * @memberof Component.prototype
+ * @desc Add a start date
+ */
+ Component.prototype.$addStartDate = function() {
+ this.start = new Date();
+ };
+
+ /**
+ * @function $deleteStartDate
+ * @memberof Component.prototype
+ * @desc Delete a start date
+ */
+ Component.prototype.$deleteStartDate = function() {
+ delete this.start;
+ delete this.startDate;
+ };
+
/**
* @function $reset
* @memberof Component.prototype
@@ -821,9 +869,13 @@
}
});
- // Format times
- component.startTime = component.startDate ? formatTime(component.startDate) : '';
- component.endTime = component.endDate ? formatTime(component.endDate) : '';
+ // Format dates and times
+ component.startDate = component.start ? formatDate(component.start) : '';
+ component.startTime = component.start ? formatTime(component.start) : '';
+ component.endDate = component.end ? formatDate(component.end) : '';
+ component.endTime = component.end ? formatTime(component.end) : '';
+ component.dueDate = component.due ? formatDate(component.due) : '';
+ component.dueTime = component.due ? formatTime(component.due) : '';
// Update recurrence definition depending on selections
if (this.$hasCustomRepeat) {
@@ -867,18 +919,30 @@
component.alarm = {};
}
- function formatTime(dateString) {
- // YYYY-MM-DDTHH:MM-ZZ:00 => YYYY-MM-DD HH:MM
- var date = new Date(dateString.substring(0,10) + ' ' + dateString.substring(11,16)),
- hours = date.getHours(),
- minutes = date.getMinutes();
-
+ function formatTime(date) {
+ var hours = date.getHours();
if (hours < 10) hours = '0' + hours;
- if (minutes < 10) minutes = '0' + minutes;
+ var minutes = date.getMinutes();
+ if (minutes < 10) minutes = '0' + minutes;
return hours + ':' + minutes;
}
+ function formatDate(date) {
+ var year = date.getYear();
+ if (year < 1000) year += 1900;
+
+ var month = '' + (date.getMonth() + 1);
+ if (month.length == 1)
+ month = '0' + month;
+
+ var day = '' + date.getDate();
+ if (day.length == 1)
+ day = '0' + day;
+
+ return year + '-' + month + '-' + day;
+ }
+
return component;
};
diff --git a/UI/WebServerResources/scss/components/timepicker/timepicker-default-theme.css b/UI/WebServerResources/scss/components/timepicker/timepicker-default-theme.css
new file mode 100644
index 000000000..95030874c
--- /dev/null
+++ b/UI/WebServerResources/scss/components/timepicker/timepicker-default-theme.css
@@ -0,0 +1,19 @@
+
+/** Theme styles for sgTimepicker. */
+sg-timepicker.md-THEME_NAME-theme {
+ background: white; }
+
+.md-THEME_NAME-theme .sg-timepicker-input-container {
+ border-bottom-color: '{{background-300}}'; }
+ .md-THEME_NAME-theme .sg-timepicker-input-container.sg-timepicker-focused {
+ border-bottom-color: '{{primary-500}}'; }
+ .md-THEME_NAME-theme .sg-timepicker-input-container.sg-timepicker-invalid {
+ border-bottom-color: '{{warn-500}}'; }
+.md-THEME_NAME-theme .sg-timepicker-time-pane {
+ border-color: '{{background-300}}'; }
+.md-THEME_NAME-theme .sg-timepicker-triangle-button:hover .sg-timepicker-expand-triangle {
+ border-top-color: '{{foreground-2}}'; }
+.md-THEME_NAME-theme .sg-timepicker-open .sg-timepicker-time-icon {
+ fill: '{{primary-500}}'; }
+.md-THEME_NAME-theme .sg-timepicker-calendar {
+ background: white; }
diff --git a/UI/WebServerResources/scss/components/timepicker/timepicker.scss b/UI/WebServerResources/scss/components/timepicker/timepicker.scss
new file mode 100644
index 000000000..06b1fc045
--- /dev/null
+++ b/UI/WebServerResources/scss/components/timepicker/timepicker.scss
@@ -0,0 +1,240 @@
+/** 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-pane {
+ font-size: 13px;
+ user-select: none;
+}
+
+.hours-pane {
+ border-bottom: solid 1px rgb(224,224,224);
+}
+
+.toggle-pane {
+ border-top: solid 1px rgb(224,224,224);
+}
+
+.md-button.md-fab.hourBtn,
+.md-button.md-fab.minuteBtn,
+.md-button.md-fab.toggleBtn,
+.md-button.md-fab.hourBtn.md-focused,
+.md-button.md-fab.minuteBtn.md-focused,
+.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{
+ 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;
+ line-height: $sg-time-pane-cell-size;
+ box-shadow: none;
+ margin: 2px;
+}
+.md-button.md-fab.toggleBtn{
+ background-color: rgb(63, 81, 181);
+ 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;
+}
+
+// The calendar icon button used to open the calendar pane.
+// Need absurd specificty to override md-button.md-icon-button.
+.sg-timepicker-button {
+ display: inline-block;
+ box-sizing: border-box;
+ background: none;
+}
+
+// The input into which the user can type the date.
+.sg-timepicker-input {
+ //@include md-flat-input();
+ min-width: 120px;
+ max-width: $md-calendar-width - $md-datepicker-button-gap;
+ background: inherit;
+ border: none;
+}
+
+// Container for the datepicker 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;
+
+ &.sg-timepicker-focused {
+ border-bottom-width: 2px;
+ }
+}
+
+
+// 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;
+
+ border-width: 1px;
+ border-style: solid;
+ background: inherit;
+ 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;
+ pointer-events: none;
+ cursor: text;
+}
+
+.sg-timepicker-input-mask-opaque {
+ position: absolute;
+ right: 0;
+ left: 120px;
+ background: white;
+
+ height: 100%;
+}
+
+// The time portion of the floating pane (vs. the input mask).
+.sg-timepicker-time {
+ opacity: 0;
+ // Use a modified timing function (from swift-ease-out) so that the opacity part of the
+ // animation doesn't come in as quickly so that the floating pane doesn't ever seem to
+ // cover up the trigger input.
+ transition: opacity $md-datepicker-open-animation-duration cubic-bezier(0.5, 0, 0.25, 1);
+
+ .md-pane-open & {
+ opacity: 1;
+ }
+
+ sg-time:focus {
+ outline: none;
+ }
+}
+
+// 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;
+.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);
+}
+
+// Button containing the down "disclosure" triangle/arrow.
+.sg-timepicker-triangle-button {
+ position: absolute;
+ right: 0;
+ top: 0;
+
+ // TODO(jelbourn): This position isn't great on all platforms.
+ transform: translateY(-25%) translateX(45%);
+}
+
+// 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;
+}
+
+// Disabled state for all elements of the picker.
+sg-timepicker[disabled] {
+ .sg-timepicker-input-container {
+ border-bottom-color: transparent;
+ }
+
+ .sg-timepicker-triangle-button {
+ display: none;
+ }
+}
+
+// Open state for all of the elements of the picker.
+.sg-timepicker-open {
+ .sg-timepicker-input-container {
+ margin-left: -$md-datepicker-button-gap;
+ border: none;
+ }
+
+ .sg-timepicker-input {
+ margin-left: 24px;
+ height: 40px;
+ }
+
+ .sg-timepicker-triangle-button {
+ display: none;
+ }
+}
\ No newline at end of file
diff --git a/UI/WebServerResources/scss/styles.scss b/UI/WebServerResources/scss/styles.scss
index ef945b7af..6df40aeff 100755
--- a/UI/WebServerResources/scss/styles.scss
+++ b/UI/WebServerResources/scss/styles.scss
@@ -63,6 +63,9 @@
@import 'components/virtualRepeat/virtual-repeat';
@import 'components/whiteframe/whiteframe';
+// Inverse components
+@import 'components/timepicker/timepicker';
+
// Theme
//@import '../angular-material/src/components/bottomSheet/bottomSheet-theme';
//@import '../angular-material/src/components/button/button-theme';