mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-04-07 14:28:52 +00:00
(feat) new time picker component + enabled it in the calendar module
This commit is contained in:
@@ -96,15 +96,17 @@
|
||||
<var:string label:value="Send Appointment Notifications"/>
|
||||
</md-checkbox>
|
||||
<!-- start + end -->
|
||||
<div layout="row">
|
||||
<md-input-container>
|
||||
<label><var:string label:value="From"/></label>
|
||||
<input type="text" ng-model="editor.component.startDate" required="required"/>
|
||||
</md-input-container>
|
||||
<md-input-container>
|
||||
<label><var:string label:value="To"/></label>
|
||||
<input type="text" ng-model="editor.component.endDate" required="required"/>
|
||||
</md-input-container>
|
||||
<div layout="column">
|
||||
<div layout="row" layout-align="start center">
|
||||
<span><var:string label:value="From"/></span>
|
||||
<md-datepicker ng-model="editor.component.start"> <!-- date picker--></md-datepicker>
|
||||
<sg-timepicker ng-model="editor.component.start"><!-- time picker --></sg-timepicker>
|
||||
</div>
|
||||
<div layout="row" layout-align="start center">
|
||||
<span><var:string label:value="To"/></span>
|
||||
<md-datepicker ng-model="editor.component.end"> <!-- date picker--></md-datepicker>
|
||||
<sg-timepicker ng-model="editor.component.end"><!-- time picker --></sg-timepicker>
|
||||
</div>
|
||||
</div>
|
||||
<!-- attach urls -->
|
||||
<div class="attr" ng-repeat="attach in editor.component.attachUrls">
|
||||
|
||||
@@ -80,15 +80,35 @@
|
||||
<var:string label:value="All day Event"/>
|
||||
</md-checkbox>
|
||||
<!-- start + end -->
|
||||
<div layout="row">
|
||||
<md-input-container>
|
||||
<label><var:string label:value="Start"/></label>
|
||||
<input type="text" ng-model="editor.component.startDate"/>
|
||||
</md-input-container>
|
||||
<md-input-container>
|
||||
<label><var:string label:value="Due"/></label>
|
||||
<input type="text" ng-model="editor.component.dueDate"/>
|
||||
</md-input-container>
|
||||
<div class="section" layout="column">
|
||||
<div layout="row" layout-align="start center" ng-show="editor.component.start">
|
||||
<label><var:string label:value="From"/></label>
|
||||
<md-datepicker ng-model="editor.component.start"> <!-- date picker--></md-datepicker>
|
||||
<sg-timepicker ng-model="editor.component.start"><!-- time picker --></sg-timepicker>
|
||||
<md-button class="sg-icon-button" type="button" ng-click="editor.component.$deleteStartDate()">
|
||||
<md-icon>remove_circle</md-icon>
|
||||
</md-button>
|
||||
</div>
|
||||
<div class="md-layout-margin" layout="row" layout-align="start center" ng-hide="editor.component.start">
|
||||
<md-button class="sg-icon-button" type="button" ng-click="editor.component.$addStartDate()">
|
||||
<md-icon>add_circle</md-icon>
|
||||
</md-button>
|
||||
<label class="button-label"><var:string label:value="Add From"/></label>
|
||||
</div>
|
||||
</div>
|
||||
<div layout="row" layout-align="start center" ng-show="editor.component.due">
|
||||
<label><var:string label:value="Due"/></label>
|
||||
<md-datepicker ng-model="editor.component.due"> <!-- date picker--></md-datepicker>
|
||||
<sg-timepicker ng-model="editor.component.due"><!-- time picker --></sg-timepicker>
|
||||
<md-button class="sg-icon-button" type="button" ng-click="editor.component.$deleteDueDate()">
|
||||
<md-icon>remove_circle</md-icon>
|
||||
</md-button>
|
||||
</div>
|
||||
<div class="md-layout-margin" layout="row" layout-align="start center" ng-hide="editor.component.due">
|
||||
<md-button class="sg-icon-button" type="button" ng-click="editor.component.$addDueDate()">
|
||||
<md-icon>add_circle</md-icon>
|
||||
</md-button>
|
||||
<label class="button-label"><var:string label:value="Add Due"/></label>
|
||||
</div>
|
||||
<!-- status -->
|
||||
<div layout="row">
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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; }
|
||||
240
UI/WebServerResources/scss/components/timepicker/timepicker.scss
Normal file
240
UI/WebServerResources/scss/components/timepicker/timepicker.scss
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user