diff options
author | YuanHu <yuan.hu1@zte.com.cn> | 2018-03-27 17:33:22 +0800 |
---|---|---|
committer | YuanHu <yuan.hu1@zte.com.cn> | 2018-03-27 17:33:22 +0800 |
commit | 8261a4ea8091c27b61ac581a852e2e18283b3cdd (patch) | |
tree | a2ca109f7600e9e0cbe73eb9139ffe6284be1159 /sdc-workflow-designer-ui/src/app/paletx/plx-datepicker | |
parent | 573f32b362f4639928485d66feb1c0721109716b (diff) |
Include paletx components
Include paletx components to WF Designer UI.
Issue-ID: SDC-1130,SDC-1131
Change-Id: Iad06b2dde8fc98d03a0e3633e829b686d75cafd0
Signed-off-by: YuanHu <yuan.hu1@zte.com.cn>
Diffstat (limited to 'sdc-workflow-designer-ui/src/app/paletx/plx-datepicker')
17 files changed, 3801 insertions, 0 deletions
diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/numberedFixLen.pipe.ts b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/numberedFixLen.pipe.ts new file mode 100644 index 00000000..9d26b16f --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/numberedFixLen.pipe.ts @@ -0,0 +1,27 @@ +/** + * numberFixedLen.pipe + */ + +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'numberFixedLen' +}) +export class NumberFixedLenPipe implements PipeTransform { + transform(num: number, len: number): any { + let numberInt = Math.floor(num); + let length = Math.floor(len); + + if (num === null || isNaN(numberInt) || isNaN(length)) { + return num; + } + + let numString = numberInt.toString(); + + while (numString.length < length) { + numString = '0' + numString; + } + + return numString; + } +} diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/picker.component.html b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/picker.component.html new file mode 100644 index 00000000..8e4102c2 --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/picker.component.html @@ -0,0 +1,134 @@ +<div + class="owl-dateTime owl-widget" + [ngClass]="{'owl-dateTime-inline': inline}" + [ngStyle]="style" + #container> + <div *ngIf="!inline && customTemp.children.length == 0" class="owl-dateTime-inputWrapper" (mouseout)="Mouseout($event)" (mouseover)="Mouseover($event)"> + <input *ngIf="!supportKeyboardInput" type="text" [class]="inputStyleClass" + [ngClass]="{ + 'owl-dateTime-input owl-inputtext owl-state-default': true + }" + [ngStyle]="inputStyle" + [attr.placeholder]="placeHolder" + [attr.tabindex]="tabIndex" [attr.id]="inputId" + [attr.required]="required" + [disabled]="disabled" + [value]="formattedValue" + (focus)="onInputFocus($event)" (blur)="onInputBlur($event)" (click)="onInputClick($event)" readonly> + <input *ngIf="supportKeyboardInput" type="text" [class]="inputStyleClass" + [ngClass]="{ + 'owl-dateTime-input owl-inputtext owl-state-default': true + }" + [ngStyle]="inputStyle" + [attr.placeholder]="placeHolder" + [attr.tabindex]="tabIndex" [attr.id]="inputId" + (change)="onInputChange($event)" + [attr.required]="required" + [value]="formattedValue" + (focus)="onInputFocus($event)" (blur)="onInputBlur($event)" (click)="onInputClick($event)"> + <i class="ict ict-close owl-icon" style="margin-right: 5px;" [hidden]="(!value)||(disabled)||(!mouseIn)||(!canClear)" (click)="clearValue($event)"></i> + <i class="fa fa-calendar owl-icon" style="margin-right: 5px;" [hidden]="value&&(!disabled)" (click)="onInputClick($event)" ></i> + </div> + <!-- Workaround of ng-content default content (angular issue #12530) --> + <div [ngClass]="{'owl-dateTime-customTemp': customTemp.children.length !== 0}" #customTemp (click)="onInputClick($event)"> + <ng-content></ng-content> + </div> + <div class="owl-dateTime-dialog owl-state-default owl-corner-all" + [ngStyle]="{'display': inline ? 'inline-block' : null}" + [@fadeInOut]="dialogVisible? 'visible' : (!inline? 'hidden': null)" + (click)="onDialogClick($event)" #dialog> + <div *ngIf="showHeader" class="owl-dateTime-dialogHeader owl-corner-top"> + <span *ngIf="value; else elseBlock">{{formattedValue}}</span> + <ng-template #elseBlock><span>{{placeHolder}}</span></ng-template> + </div> + <div *ngIf="type ==='both' || type === 'calendar'" class="owl-calendar-wrapper owl-corner-all owl-padding"> + <div class="owl-calendar-control owl-cal-header"> + <div class="owl-calendar-controlNav"> + <span class="fa fa-angle-left" style="padding: 8px;margin-left:12px; font-size: 20px;" + (click)="prevNav($event)"></span> + </div> + <div class="owl-calendar-controlContent"> + <span class="month-control" (click)="changeDialogType(2)">{{pickerMonth}}</span> + <span class="year-control" (click)="changeDialogType(3)">{{pickerYear}}</span> + </div> + <div class="owl-calendar-controlNav"> + <span class="fa fa-angle-right" style="padding: 8px; font-size: 20px;" (click)="nextNav($event)"></span> + </div> + </div> + <div class="owl-calendar" [hidden]="dialogType !== 1"> + <table class="owl-calendar-day"> + <thead class="owl-cal-header"> + <tr class="owl-weekdays" style="height:40px"> + <th *ngFor="let weekDay of calendarWeekdays" class="owl-weekday" scope="col"> + <span>{{weekDay}}</span> + </th> + </tr> + </thead> + <tbody> + <tr class="owl-days" *ngFor="let week of calendarDays"> + <td *ngFor="let d of week" class="owl-day" style="padding: 5px;" + [ngClass]="{ + 'owl-calendar-invalid': !isValidDay(d.date), + 'owl-calendar-outFocus': d.otherMonth, + 'owl-calendar-hidden': d.hide, + 'owl-day-today': d.today + }" (click)="selectDate($event, d.date)"> + <div style="height:32px;" class="owl-day day" [ngClass]=" {'owl-calendar-selected': isSelectedDay(d.date), 'owl-calendar-invalid': !isValidDay(d.date)}"> + <a style="line-height:32px;">{{d.num}}</a> + </div> + </td> + </tr> + </tbody> + </table> + </div> + <div class="owl-calendar" [hidden]="dialogType !== 2"> + <table class="owl-calendar-month"> + <tbody> + <tr class="owl-months" *ngFor="let months of calendarMonths; let i = index"> + <td *ngFor="let month of months; let j = index" class="owl-month" + (click)="selectMonth(i*3 + j)"> + <div class="owl-month" [ngClass]="{'owl-calendar-div-selected': isCurrentMonth(i*3 + j),'owl-calendar-month-part':true,'owl-calendar-month-selected': isCurrentMonth(i*3 + j)}"> + <a>{{month}}</a> + </div> + </td> + </tr> + </tbody> + </table> + </div> + <div class="owl-calendar" [hidden]="dialogType !== 3"> + <table class="owl-calendar-year"> + <tbody> + <tr class="owl-years" *ngFor="let years of calendarYears"> + <td class="owl-year" *ngFor="let year of years" + (click)="selectYear(+year)"> + <div [ngClass]="{'owl-calendar-year-part':true,'owl-calendar-year-selected': isCurrentYear(year)}"> + <a>{{year}}</a> + </div> + </td> + </tr> + </tbody> + </table> + <!-- + <div class="owl-calendar-yearArrow left" style="left: 12px; + font-size: 20px; + margin-top: -116px;" (click)="generateYearList('prev')"> + <i style="padding:8px" class="fa fa-angle-left"></i> + </div> + <div class="owl-calendar-yearArrow right" style="left: 261px; + font-size: 20px; + margin-top: -116px;" (click)="generateYearList('next')"> + <i style="padding:8px" class="fa fa-angle-right"></i> + </div> + --> + </div> + </div> + <div *ngIf="type ==='both' || type === 'timer'" [hidden]="dialogType !== 1" > + <div style="height: 35px; padding: 8px;margin-bottom: 20px;"> + <oes-timepickerr #timepicker [max]="_max" [min]="_min" class="time-picker" (TimerChange)="TimerChange($event)" [showSecondsTimer]="showSeconds" [(ngModel)]="mtime" [size]="'small'" [seconds]="seconds"></oes-timepickerr> + <div class="confirm-btn-div" style=" float: right; margin-top: -35px;"> + <button class="plx-btn plx-btn-primary plx-btn-xs" (click)="confirm()" type="button"> {{locale.confirm}} </button> + </div> + </div> + </div> + </div> +</div>
\ No newline at end of file diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/picker.component.less b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/picker.component.less new file mode 100644 index 00000000..8e50660b --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/picker.component.less @@ -0,0 +1,434 @@ +@import "../../assets/components/themes/default/theme.less"; +@import "../../assets/components/themes/common/plx-input.less"; +@import "../../assets/components/themes/common/plx-button.less"; + +.owl-dateTime { + display: inline-block; + position: relative; + width: 100%; + font-family: @font-family; + font-size: @font-size; + background: @component-bg; + color: @text-color; +} + +.owl-dateTime input { + .plx-input; +} + +.owl-dateTime input:-ms-input-placeholder { + color: @unselected-text-color !important; +} +.owl-dateTime input::-webkit-input-placeholder { + color: @unselected-text-color !important; +} + +.owl-dateTime-input { + width: 100%; + padding-right: 1.5em; } + +.owl-dateTime-cancel { + position: absolute; + top: 50%; + right: .1em; + border-radius: 50%; + transform: translateY(-50%); + cursor: pointer; + color: inherit; } + +.owl-dateTime-inputWrapper { + position: relative; } + +.owl-dateTime-customTemp { + display: inline-block; + position: relative; } + +.owl-dateTime-dialog { + padding: 0px; + position: absolute; } + +.owl-dateTime-dialogHeader { + display: flex; + justify-content: center; + align-items: center; + width: 100%; } + +.owl-calendar-wrapper, +.owl-timer-wrapper { + position: relative; + width: 100%; + padding: .2em .5em; } + +.owl-calendar-control { + display: flex; + justify-content: space-around; + width: 100%; + height: 2em; } + .owl-calendar-control .owl-calendar-controlNav { + position: relative; + cursor: pointer; + width: 12.5%; } + .owl-calendar-control .owl-calendar-controlContent { + display: flex; + justify-content: center; + align-items: center; + width: 75%; + height: 100%; } + +.owl-calendar { + position: relative; + min-height: 13.7em; } + .owl-calendar table { + width: 100%; + border-collapse: collapse; } + .owl-calendar tbody td { + position: relative; + text-align: center; } + .owl-calendar tbody td a { + display: block; + width: 100%; + height: 100%; + text-decoration: none; + color: inherit; + font-size:12px; + } + .owl-calendar .owl-calendar-yearArrow { + position: absolute; + top: 50%; + width: 1.5em; + height: 1.5em; + transform: translateY(-50%); + cursor: pointer; } + .owl-calendar .owl-calendar-yearArrow.left { + left: -.5em; } + .owl-calendar .owl-calendar-yearArrow.right { + right: -.5em; } + +.owl-timer-wrapper { + position: relative; + display: flex; + justify-content: center; } + .owl-timer-wrapper .owl-timer { + position: relative; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 25%; + height: 100%; } + .owl-timer-wrapper .owl-timer-control { + display: flex; + justify-content: center; + align-items: center; + height: 30%; + width: 100%; + cursor: pointer; } + .owl-timer-wrapper .owl-timer-control .icon:before { + margin: 0; } + .owl-timer-wrapper .owl-timer-input { + width: 60%; + height: 100%; } + +/*# sourceMappingURL=picker.component.css.map */ +.font-face { + font-weight: normal; + font-style: normal; } + +[class^="paletx-datepicker-icon-"]:before, [class*="paletx-datepicker-icon-"]:before { + font-family: "fontello"; + font-style: normal; + font-weight: normal; + speak: none; + display: inline-block; + text-decoration: inherit; + width: 1em; + margin-right: .2em; + text-align: center; + /* opacity: .8; */ + /* For safety - reset parent styles, that can break glyph codes*/ + font-variant: normal; + text-transform: none; + /* fix buttons height, for twitter bootstrap */ + line-height: 1em; + /* Animation center compensation - margins should be symmetric */ + /* remove if not needed */ + margin-left: .2em; + /* you can be more comfortable with increased icons size */ + /* font-size: 120%; */ + /* Font smoothing. That was taken from TWBS */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + /* Uncomment for 3D effect */ + /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ } + +.paletx-datepicker-icon-cancel:before { + content: '\e802'; } + +/* '' */ +.paletx-datepicker-icon-up-open:before { + content: '\e805'; } + +/* '' */ +.paletx-datepicker-icon-down-open:before { + content: '\e80b'; } + +/* '' */ +.paletx-datepicker-icon-left-open:before { + content: '\e817'; } + +/* '' */ +.paletx-datepicker-icon-right-open:before { + content: '\e818'; } + +/* '' */ +.owl-widget, +.owl-widget * { + box-sizing: border-box; } + +.owl-widget { + font-size: 1em; } +.owl-padding{ + padding: 0px; +} +.owl-corner-all { + border-radius: 3px; } + +.owl-corner-top { + border-top-left-radius: 3px; + border-top-right-radius: 3px; } + +.owl-state-default { + border: 1px solid @border-color-base; + background: @component-bg; + color: @text-color; } + +.owl-inputtext { + margin: 0; + outline: medium none; + transition: .2s; } + + + .owl-dateTime.owl-dateTime-inline { + width: auto; } + .owl-dateTime.owl-dateTime-inline .owl-dateTime-dialog { + position: relative; + z-index: auto; } + +.owl-dateTime-dialog { + width: 300px; + user-select: none; + z-index: 99999; } + +.owl-dateTime-dialogHeader { + height: 2.5em; + padding: .25em; + background-color: @component-bg; + overflow-y: auto; } + +.owl-calendar-control .owl-calendar-controlNav .nav-prev, +.owl-calendar-control .owl-calendar-controlNav .nav-next { + position: absolute; + top: 50%; + right: auto; + bottom: auto; + left: 50%; + transform: translate(-50%, -50%); +} + +.owl-cal-header{ + background: @selected-bg-color; + //color: @form-label; + height: 35px; + //width: 105%; + //margin-left: -7px; +} + .owl-calendar-control .owl-calendar-controlNav .nav-prev:before, + .owl-calendar-control .owl-calendar-controlNav .nav-next:before { + //content: ""; + border-top: .5em solid transparent; + border-bottom: .5em solid transparent; + border-right: 0.75em solid #000000; + width: 0; + height: 0; + display: block; + margin: 0 auto; } +.owl-calendar-control .owl-calendar-controlNav .nav-next:before { + border-right: 0; + border-left: 0.75em solid #000000; } +.owl-calendar-control .owl-calendar-controlContent .month-control, +.owl-calendar-control .owl-calendar-controlContent .year-control { + color: @unselected-text-color; + display: inline-block; + cursor: pointer; + transition: transform 200ms ease; } + .owl-calendar-control .owl-calendar-controlContent .month-control:hover, + .owl-calendar-control .owl-calendar-controlContent .year-control:hover { + // transform: scale(1.2); } + color: @guide-color; } +.owl-calendar-control .owl-calendar-controlContent .month-control { + font-size: @font-size-title-group; + margin-right: .8rem; +} +.owl-calendar-control .owl-calendar-controlContent .year-control { + font-size: @font-size-title-group; +} + +.owl-calendar tbody td.owl-calendar-selected { + background-color: @guide-color; + color: @component-bg } +.owl-calendar tbody td.owl-calendar-invalid { + color: @disabled-text-color } +.owl-calendar tbody td.owl-calendar-outFocus { + color: @unselected-text-color; } +.owl-calendar tbody td.owl-calendar-hidden { + visibility: hidden; } + /** +.owl-calendar tbody td:not(.owl-calendar-selected):not(.owl-calendar-invalid):hover { + background-color: @hover-bg-color; + color: @shadow-color } +**/ +.owl-years td.owl-year, +.owl-years td.owl-month, +.owl-months td.owl-year, +.owl-months td.owl-month { + font-size: 1.2em; + height: 2.5em; + width: 33.33%; + line-height: 2.5em; + border-radius: 60px; + } + +.owl-weekdays th.owl-weekday { + height: 1em; + line-height: 2em; + text-align: center; + font-weight: normal; + font-size: @font-size; + /**color: @unselected-text-color; **/ + } + +.owl-days td.owl-day { + border-radius: 30px; + height: 2em; + width: calc(100% / 7); + line-height: 2em; } + .owl-days td.owl-day.owl-day-today:before { + content: ''; + display: block; + position: absolute; + right: 2px; + top: 2px; + color: @primary-color; + border-top: 0.5em solid @primary-color-hover; + border-left: .5em solid transparent; + } + +.owl-timer-wrapper { + height: 5.4em; + background-color: @shadow-color; } + .owl-timer-wrapper .owl-timer-text { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 40%; + font-size: 1.5em; } + .owl-timer-wrapper .owl-meridian-btn { + font-size: .8em; + color: @guide-color; + background-image: none; + background-color: transparent; + border-color: @guide-color; } + .owl-timer-wrapper .owl-meridian-btn:hover { + color: @scene-textcolor; + background-color: @guide-color; + border-color: @guide-color; } + +.owl-timer-divider { + display: inline-block; + align-self: flex-end; + position: absolute; + width: .6em; + height: 100%; + left: -.3em; } + .owl-timer-divider .owl-timer-dot { + display: block; + width: .3em; + height: .3em; + position: absolute; + left: 50%; + border-radius: 50%; + transform: translateX(-50%); } + .owl-timer-divider .owl-timer-dot.dot-top { + top: 38%; } + .owl-timer-divider .owl-timer-dot.dot-bottom { + bottom: 38%; } +.owl-icon{ + position: absolute; + top: 50%; + right: .1em; + border-radius: 50%; + -webkit-transform: translateY(-50%); + transform: translateY(-50%); + cursor: pointer; + color: @fonticon-color; +} + +.oes-time-control{ + color: @text-color !important; +} +.owl-calendar-selected { + background-color: @guide-color; + color: #fff; + border-radius: 50%; +} +.owl-calendar tbody td div.day:not(.owl-calendar-selected):not(.owl-calendar-invalid):hover { + background-color: @hover-bg-color; + color:#000; + border-radius: 50%; } +.oes-time-control{ + font-size: @font-size; +} +.owl-calendar-year-part{ + width: 42px; + margin-left: 30px; + text-align: center; +} +.owl-calendar-year-part:hover{ + background-color: @hover-bg-color; + color:#000; + border-radius: 50%; +} +.owl-calendar-year-selected{ + background-color: @guide-color; + color: #fff; + border-radius: 50%; +} +.owl-calendar-year-selected:hover{ + background-color: @guide-color; + color: #fff; + border-radius: 50%; +} +.owl-calendar-month-part{ + width: 42px; + margin-left: 30px; + text-align: center; +} +.owl-calendar-month-part:hover{ + background-color: @hover-bg-color; + color:#000; + border-radius: 50%; +} +.owl-calendar-month-selected{ + background-color: @guide-color; + color: #fff; + border-radius: 50%; +} +.owl-calendar-month-selected:hover{ + background-color: @guide-color; + color: #fff; + border-radius: 50%; +} + +/*# sourceMappingURL=picker.css.map */ + diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/picker.component.ts b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/picker.component.ts new file mode 100644 index 00000000..493e0cb2 --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/picker.component.ts @@ -0,0 +1,1712 @@ +/** + * picker.component + */ + +import { + AfterViewInit, + Component, ElementRef, EventEmitter, forwardRef, Input, OnDestroy, OnInit, Output, Renderer2, + ViewChild +} from '@angular/core'; +import { animate, state, style, transition, trigger } from '@angular/animations'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { + parse, + isValid, + startOfMonth, + getDate, + getDay, + addDays, + addMonths, + isSameDay, + isSameMonth, + isToday, + getMonth, + setMonth, + getYear, + addYears, + differenceInCalendarDays, + setYear, + getHours, + setHours, + getMinutes, + setMinutes, + getSeconds, + setSeconds, + isBefore, + isAfter, + compareAsc, + startOfDay, + format, + endOfDay, +} from 'date-fns'; +import { NumberFixedLenPipe } from './numberedFixLen.pipe'; + +export interface LocaleSettings { + firstDayOfWeek?: number; + dayNames: string[]; + dayNamesShort: string[]; + monthNames: string[]; + monthNamesShort: string[]; + dateFns: any; + confirm: string; +} + +export enum DialogType { + Time, + Date, + Month, + Year, +} + +export const DATETIMEPICKER_VALUE_ACCESSOR: any = { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => DateTimePickerComponent), + multi: true +}; + +@Component({ + selector: 'plx-datepicker', + templateUrl: './picker.component.html', + styleUrls: ['./picker.component.less'], + providers: [NumberFixedLenPipe, DATETIMEPICKER_VALUE_ACCESSOR], + animations: [ + trigger('fadeInOut', [ + state('hidden', style({ + opacity: 0, + display: 'none' + })), + state('visible', style({ + opacity: 1, + display: 'block' + })), + transition('visible => hidden', animate('200ms ease-in')), + transition('hidden => visible', animate('400ms ease-out')) + ]) + ], +}) + +export class DateTimePickerComponent implements OnInit, AfterViewInit, OnDestroy, ControlValueAccessor { + + @ViewChild('timepicker') public timepicker; + + /** + * Type of the value to write back to ngModel + * @default 'date' -- Javascript Date type + * @type {'string' | 'date'} + * */ + @Input() dataType: 'string' | 'date' = 'date'; + + /** + * Format of the date + * @default 'y/MM/dd' + * @type {String} + * */ + @Input() dateFormat: string = 'YYYY-MM-DD HH:mm'; + /** + * When present, it specifies that the component should be disabled + * @default false + * @type {Boolean} + * */ + @Input() disabled: boolean; + /** + * @default false + * @type {Boolean} + * */ + @Input() supportKeyboardInput: boolean = false; + /** + * Array with dates that should be disabled (not selectable) + * @default null + * @type {Date[]} + * */ + @Input() disabledDates: Date[] = []; + + /** + * Array with weekday numbers that should be disabled (not selectable) + * @default null + * @type {number[]} + * */ + @Input() disabledDays: number[]; + + /** + * When enabled, displays the calendar as inline + * @default false -- popup mode + * @type {boolean} + * */ + @Input() inline: boolean; + + /** + * Identifier of the focus input to match a label defined for the component + * @default null + * @type {String} + * */ + @Input() inputId: string; + + /** + * Inline style of the picker text input + * @default null + * @type {any} + * */ + @Input() inputStyle: any; + + /** + * Style class of the picker text input + * @default null + * @type {String} + * */ + @Input() inputStyleClass: string; + + /** + * Maximum number of selectable dates in multiple mode + * @default null + * @type {number} + * */ + @Input() maxDateCount: number; + + /** + * The minimum selectable date time + * @default null + * @type {Date | string} + * */ + private _max: Date; + @Input() + get max() { + return this._max; + } + + set max(val: Date | string) { + this._max = this.parseToDate(val); + } + + @Input() + get maxDate() { + return this._max; + } + + set maxDate(val: Date | string) { + this._max = this.parseToDate(val); + } + + /** + * The maximum selectable date time + * @default null + * @type {Date | string } + * */ + private _min: Date; + @Input() + get min() { + return this._min; + } + + set min(val: Date | string) { + this._min = this.parseToDate(val); + } + @Input() + get minDate() { + return this._min; + } + + set minDate(val: Date | string) { + this._min = this.parseToDate(val); + } + + @Input() + get dateValue() { + return this.value; + } + + set dateValue(val: Date | string) { + let newvalue = this.parseToDate(val); + if(newvalue!==undefined) + { + this.updateModel(newvalue); + this.updateCalendar(newvalue); + this.updateTimer(newvalue); + this.updateFormattedValue(); + } + } + + + /** + * Picker input placeholder value + * @default + * @type {String} + * */ + @Input() placeHolder: string = 'yyyy-mm-dd hh:mm'; + + /** + * When present, it specifies that an input field must be filled out before submitting the form + * @default false + * @type {Boolean} + * */ + @Input() required: boolean; + + /** + * Defines the quantity of the selection + * 'single' -- allow only a date value to be selected + * 'multiple' -- allow multiple date value to be selected + * 'range' -- allow to select an range ot date values + * @default 'single' + * @type {string} + * */ + @Input() selectionMode: 'single' | 'multiple' | 'range' = 'single'; + + /** + * Whether to show the picker dialog header + * @default false + * @type {Boolean} + * */ + @Input() showHeader: boolean; + + @Input() canClear: boolean = true; + + /** + * Whether to show the second's timer + * @default false + * @type {Boolean} + * */ + @Input() showSeconds: boolean; + + /** + * Inline style of the element + * @default null + * @type {any} + * */ + @Input() style: any; + + /** + * Style class of the element + * @default null + * @type {String} + * */ + @Input() styleClass: string; + + /** + * Index of the element in tabbing order + * @default null + * @type {Number} + * */ + @Input() tabIndex: number; + + /** + * Set the type of the dateTime picker + * 'both' -- show both calendar and timer + * 'calendar' -- show only calendar + * 'timer' -- show only timer + * @default 'both' + * @type {'both' | 'calendar' | 'timer'} + * */ + @Input() type: 'both' | 'calendar' | 'timer' = 'calendar'; + + //附加方法 + @Input() + set timeOnly(value: boolean) { + if (value) { + this.type = 'timer'; + } + else { + this.type = "both"; + } + } + + @Input() + set showTime(value: boolean) { + if (value) { + this.type = 'both'; + } + else { + this.type = "calendar"; + } + } + + /** + * An object having regional configuration properties for the dateTimePicker + * */ + @Input() + get locale(): any { + return this._locale; + } + set locale(val: any) { + if (val !== undefined) { + this._locale = val; + this._userlocale = true; + } + } + + @Input() localePrefab: 'Zh' | 'En' = 'En'; + /** + * Determine the hour format (12 or 24) + * @default '24' + * @type {'24'| '12'} + * */ + @Input() hourFormat: '12' | '24' = '24'; + + + /** + * When it is set to false, only show current month's days in calendar + * @default true + * @type {boolean} + * */ + @Input() showOtherMonths: boolean = true; + + /** + * Callback to invoke when dropdown gets focus. + * */ + @Output() onFocus: any = new EventEmitter<any>(); + + /** + * Callback to invoke when dropdown gets focus. + * */ + @Output() onConfirm: any = new EventEmitter<any>(); + + /** + * Callback to invoke when dropdown loses focus. + * */ + @Output() onBlur: any = new EventEmitter<any>(); + + /** + * Callback to invoke when a invalid date is selected. + * */ + @Output() onInvalid: any = new EventEmitter<any>(); + + + + @ViewChild('container') containerElm: ElementRef; + @ViewChild('textInput') textInputElm: ElementRef; + @ViewChild('dialog') dialogElm: ElementRef; + + public calendarDays: Array<any[]>; + public calendarWeekdays: string[]; + public calendarMonths: Array<string[]>; + public calendarYears: Array<string[]> = []; + public dialogType: DialogType = DialogType.Date; + public dialogVisible: boolean; + public focus: boolean; + public formattedValue: string = ''; + public value: any; + public pickerMoment: Date; + public pickerMonth: string; + public pickerYear: string; + + public hourValue: number; + public minValue: number; + public secValue: number; + public meridianValue: string = 'AM'; + private _userlocale: boolean = false; + private _locale: LocaleSettings = { + firstDayOfWeek: 0, + dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], + dayNamesShort: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], + //dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], + monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + dateFns: null, + confirm: 'OK' + }; + private _localeEn: LocaleSettings = { + firstDayOfWeek: 0, + dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], + dayNamesShort: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], + monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], + monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + dateFns: null, + confirm: 'OK' + }; + private _localeZh: LocaleSettings = { + firstDayOfWeek: 0, + dayNames: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'], + dayNamesShort: ['日', '一', '二', '三', '四', '五', '六'], + monthNames: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], + monthNamesShort: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], + dateFns: null, + confirm: '确定' + }; + private dialogClick: boolean; + private documentClickListener: Function; + private valueIndex: number = 0; + private onModelChange: Function = () => { + // + } + private onModelTouched: Function = () => { + // + } + + constructor(private renderer: Renderer2, + private numFixedLenPipe: NumberFixedLenPipe) { + } + private updateDate(newvalue : Date) + { + if(newvalue!==undefined&&newvalue!==null) + { + if(this.min) + { + newvalue = this._min.getTime()<=newvalue.getTime()?newvalue:new Date(this._min); + } + if(this.max) + { + newvalue = this._max.getTime()>=newvalue.getTime()?newvalue:new Date(this._max); + } + this.updateModel(newvalue); + this.updateCalendar(newvalue); + this.updateTimer(newvalue); + this.updateFormattedValue(); + return; + } + } + public onInputChange(event:any): void { + let newvalue = this.parseToDate(event.target.value); + if(newvalue!==undefined&&newvalue!==null) + { + if(this.min) + { + newvalue = this._min.getTime()<=newvalue.getTime()?newvalue:new Date(this._min); + } + if(this.max) + { + newvalue = this._max.getTime()>=newvalue.getTime()?newvalue:new Date(this._max); + } + this.updateModel(newvalue); + this.updateCalendar(newvalue); + this.updateTimer(newvalue); + this.updateFormattedValue(); + return; + } + this.updateModel(null); + this.updateCalendar(null); + this.updateTimer(null); + this.updateFormattedValue(); + } + public ngOnInit(): void { + + if ((!this._userlocale) || this.locale === null && this.locale === undefined) { + switch (this.localePrefab) { + + case 'Zh': { + this._locale = this._localeZh; + break; + } + case 'En': { + this._locale = this._localeEn; + break; + } + default: + { + this._locale = this._localeEn; + break; + } + } + } + this.pickerMoment = new Date(); + + if (this.type === 'both' || this.type === 'calendar') { + this.generateWeekDays(); + this.generateMonthList(); + } + this.updateTimer(this.value); + } + + public ngAfterViewInit(): void { + this.updateCalendar(this.value); + this.updateTimer(this.value); + } + + public ngOnDestroy(): void { + this.unbindDocumentClickListener(); + } + + public writeValue(obj: any): void { + + if (obj instanceof Array) { + this.value = []; + for (let o of obj) { + let v = this.parseToDate(o); + this.value.push(v); + } + this.updateCalendar(this.value[0]); + this.updateTimer(this.value[0]); + } else { + this.value = this.parseToDate(obj); + this.updateCalendar(this.value); + this.updateTimer(this.value); + } + this.updateFormattedValue(); + } + + public registerOnChange(fn: any): void { + this.onModelChange = fn; + } + + public registerOnTouched(fn: any): void { + this.onModelTouched = fn; + } + + public setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + } + + + private initflag = true; + /** + * Handle click event on the text input + * @param {any} event + * @return {void} + * */ + public onInputClick(event: any): void { + + if (this.timepicker !== undefined && this.initflag) { + this.initflag = false; + if (this.value !== undefined && this.value !== null) { + this.timepicker.updateHour(this.value.getHours()); + this.timepicker.updateMinute(this.value.getMinutes()); + this.timepicker.updateSecond(this.value.getSeconds()); + } + else { + this.timepicker.updateHour(0); + this.timepicker.updateMinute(0); + this.timepicker.updateSecond(0); + this.updateModel(null); + this.updateFormattedValue(); + } + } + if (this.disabled) { + event.preventDefault(); + return; + } + + this.dialogClick = true; + if (!this.dialogVisible) { + this.show(); + } + event.preventDefault(); + return; + } + + /** + * Set the element on focus + * @param {any} event + * @return {void} + * */ + public onInputFocus(event: any): void { + this.focus = true; + this.onFocus.emit(event); + event.preventDefault(); + return; + } + + /** + * Set the element on blur + * @param {any} event + * @return {void} + * */ + public onInputBlur(event: any): void { + this.focus = false; + this.onModelTouched(); + this.onBlur.emit(event); + event.preventDefault(); + return; + } + + /** + * Handle click event on the dialog + * @param {any} event + * @return {void} + * */ + public onDialogClick(event: any): void { + this.dialogClick = true; + } + + /** + * Go to previous month + * @param {any} event + * @return {void} + * */ + public prevMonth(event: any): void { + + if (this.disabled) { + event.preventDefault(); + return; + } + + this.pickerMoment = addMonths(this.pickerMoment, -1); + this.generateCalendar(); + if(this.value!==undefined&&this.value!==null) + { + let nowvalue = new Date(this.value); + nowvalue.setMonth(this.pickerMoment.getMonth()); + this.updateDate(nowvalue); + } + event.preventDefault(); + return; + } + + /** + * Go to next month + * @param {any} event + * @return {void} + * */ + public nextMonth(event: any): void { + + if (this.disabled) { + event.preventDefault(); + return; + } + + this.pickerMoment = addMonths(this.pickerMoment, 1); + this.generateCalendar(); + if(this.value!==undefined&&this.value!==null) + { + let nowvalue = new Date(this.value); + nowvalue.setMonth(this.pickerMoment.getMonth()); + this.updateDate(nowvalue); + } + event.preventDefault(); + return; + } + + /** + * Select a date + * @param {any} event + * @param {Date} date + * @return {void} + * */ + public selectDate(event: any, date: Date): void { + + if (this.disabled || !date) { + event.preventDefault(); + return; + } + + let temp: Date; + // check if the selected date is valid + if (this.isValidValue(date)) { + temp = date; + } else { + if (isSameDay(date, this._min)) { + temp = new Date(this._min); + } else if (isSameDay(date, this._max)) { + temp = new Date(this._max); + } else { + this.onInvalid.emit({ originalEvent: event, value: date }); + return; + } + } + if (this.minValue !== undefined) { + temp.setMinutes(this.minValue); + } + if (this.secValue !== undefined) { + temp.setSeconds(this.secValue); + } + if (this.hourValue !== undefined) { + temp.setHours(this.hourValue); + } + let selected; + if (this.isSingleSelection()) { + if (!isSameDay(this.value, temp)) { + selected = temp; + } + } else if (this.isRangeSelection()) { + if (this.value && this.value.length) { + let startDate = this.value[0]; + let endDate = this.value[1]; + + if (!endDate && temp.getTime() > startDate.getTime()) { + endDate = temp; + this.valueIndex = 1; + } else { + startDate = temp; + endDate = null; + this.valueIndex = 0; + } + selected = [startDate, endDate]; + } else { + selected = [temp, null]; + this.valueIndex = 0; + } + } else if (this.isMultiSelection()) { + + // check if it exceeds the maxDateCount limit + if (this.maxDateCount && this.value && + this.value.length && this.value.length >= this.maxDateCount) { + this.onInvalid.emit({ originalEvent: event, value: 'Exceed max date count.' }); + return; + } + + if (this.isSelectedDay(temp)) { + selected = this.value.filter((d: Date) => { + return !isSameDay(d, temp); + }); + } else { + selected = this.value ? [...this.value, temp] : [temp]; + this.valueIndex = selected.length - 1; + } + } + if (selected) { + this.updateModel(selected); + if (this.value instanceof Array) { + this.updateCalendar(this.value[this.valueIndex]); + this.updateTimer(this.value[this.valueIndex]); + } else { + this.updateCalendar(this.value); + this.updateTimer(this.value); + } + this.updateFormattedValue(); + } + } + + /** + * Set a pickerMoment's month + * @param {Number} monthNum + * @return {void} + * */ + public selectMonth(monthNum: number): void { + this.pickerMoment = setMonth(this.pickerMoment, monthNum); + this.generateCalendar(); + if(this.value!==undefined&&this.value!==null) + { + let nowvalue = new Date(this.value); + nowvalue.setMonth(monthNum); + this.updateDate(nowvalue); + } + this.changeDialogType(DialogType.Month); + } + + /** + * Set a pickerMoment's year + * @param {Number} yearNum + * @return {void} + * */ + public selectYear(yearNum: number): void { + this.pickerMoment = setYear(this.pickerMoment, yearNum); + this.generateCalendar(); + if(this.value!==undefined&&this.value!==null) + { + let nowvalue = new Date(this.value); + nowvalue.setFullYear(yearNum); + this.updateDate(nowvalue); + } + this.changeDialogType(DialogType.Year); + } + + /** + * Set the selected moment's meridian + * @param {any} event + * @return {void} + * */ + public toggleMeridian(event: any): void { + + let value = this.value ? (this.value.length ? this.value[this.valueIndex] : this.value) : null; + + if (this.disabled) { + event.preventDefault(); + return; + } + + if (!value) { + this.meridianValue = this.meridianValue === 'AM' ? 'PM' : 'AM'; + return; + } + + let hours = getHours(value); + if (this.meridianValue === 'AM') { + hours += 12; + } else if (this.meridianValue === 'PM') { + hours -= 12; + } + + let selectedTime = setHours(value, hours); + this.setSelectedTime(selectedTime); + event.preventDefault(); + return; + } + + /** + * Set the selected moment's hour + * @param {any} event + * @param {'increase' | 'decrease' | number} val + * 'increase' -- increase hour value by 1 + * 'decrease' -- decrease hour value by 1 + * number -- set hour value to val + * @param {HTMLInputElement} input -- optional + * @return {boolean} + * */ + public setHours(event: any, val: 'increase' | 'decrease' | number, input?: HTMLInputElement): boolean { + + let value; + if (this.value) { + if (this.value.length) { + value = this.value[this.valueIndex]; + } else { + value = this.value; + } + } else { + if (this.type === 'timer') { + value = new Date(); + } else { + value = null; + } + } + + if (this.disabled || !value) { + event.preventDefault(); + return false; + } + + let hours = getHours(value); + if (val === 'increase') { + hours += 1; + } else if (val === 'decrease') { + hours -= 1; + } else { + hours = val; + } + + if (hours > 23) { + hours = 0; + } else if (hours < 0) { + hours = 23; + } + + let selectedTime = setHours(value, hours); + let done = this.setSelectedTime(selectedTime); + + // Focus the input and select its value when model updated + if (input) { + setTimeout(() => { + input.focus(); + }, 0); + } + + event.preventDefault(); + return done; + } + + /** + * Set the selected moment's minute + * @param {any} event + * @param {'increase' | 'decrease' | number} val + * 'increase' -- increase minute value by 1 + * 'decrease' -- decrease minute value by 1 + * number -- set minute value to val + * @param {HTMLInputElement} input -- optional + * @return {boolean} + * */ + public setMinutes(event: any, val: 'increase' | 'decrease' | number, input?: HTMLInputElement): boolean { + + let value; + if (this.value) { + if (this.value.length) { + value = this.value[this.valueIndex]; + } else { + value = this.value; + } + } else { + if (this.type === 'timer') { + value = new Date(); + } else { + value = null; + } + } + + if (this.disabled || !value) { + event.preventDefault(); + return false; + } + + let minutes = getMinutes(value); + if (val === 'increase') { + minutes += 1; + } else if (val === 'decrease') { + minutes -= 1; + } else { + minutes = val; + } + + if (minutes > 59) { + minutes = 0; + } else if (minutes < 0) { + minutes = 59; + } + + let selectedTime = setMinutes(value, minutes); + let done = this.setSelectedTime(selectedTime); + + // Focus the input and select its value when model updated + if (input) { + setTimeout(() => { + input.focus(); + }, 0); + } + + event.preventDefault(); + return done; + } + + /** + * Set the selected moment's second + * @param {any} event + * @param {'increase' | 'decrease' | number} val + * 'increase' -- increase second value by 1 + * 'decrease' -- decrease second value by 1 + * number -- set second value to val + * @param {HTMLInputElement} input -- optional + * @return {boolean} + * */ + public setSeconds(event: any, val: 'increase' | 'decrease' | number, input?: HTMLInputElement): boolean { + + let value; + if (this.value) { + if (this.value.length) { + value = this.value[this.valueIndex]; + } else { + value = this.value; + } + } else { + if (this.type === 'timer') { + value = new Date(); + } else { + value = null; + } + } + + if (this.disabled || !value) { + event.preventDefault(); + return false; + } + + let seconds = getSeconds(value); + if (val === 'increase') { + seconds = this.secValue + 1; + } else if (val === 'decrease') { + seconds = this.secValue - 1; + } else { + seconds = val; + } + + if (seconds > 59) { + seconds = 0; + } else if (seconds < 0) { + seconds = 59; + } + + let selectedTime = setSeconds(value, seconds); + let done = this.setSelectedTime(selectedTime); + + // Focus the input and select its value when model updated + if (input) { + setTimeout(() => { + input.focus(); + }, 0); + } + + event.preventDefault(); + return done; + } + + /** + * Check if the date is selected + * @param {Date} date + * @return {Boolean} + * */ + public isSelectedDay(date: Date): boolean { + if (this.isSingleSelection()) { + return this.value && isSameDay(this.value, date); + } else if (this.isRangeSelection() && this.value && this.value.length) { + if (this.value[1]) { + return (isSameDay(this.value[0], date) || isSameDay(this.value[1], date) || + this.isDayBetween(this.value[0], this.value[1], date)) && this.isValidDay(date); + } else { + return isSameDay(this.value[0], date); + } + } else if (this.isMultiSelection() && this.value && this.value.length) { + let selected; + for (let d of this.value) { + selected = isSameDay(d, date); + if (selected) { + break; + } + } + return selected; + } + return false; + } + + /** + * Check if a day is between two specific days + * @param {Date} start + * @param {Date} end + * @param {Date} day + * @return {boolean} + * */ + public isDayBetween(start: Date, end: Date, day: Date): boolean { + if (start && end) { + return isAfter(day, start) && isBefore(day, end); + } else { + return false; + } + } + + /** + * Check if the calendar day is a valid day + * @param {Date} date + * @return {Boolean} + * */ + public isValidDay(date: Date): boolean { + let isValid = true; + if (this.disabledDates && this.disabledDates.length) { + for (let disabledDate of this.disabledDates) { + if (isSameDay(disabledDate, date)) { + return false; + } + } + } + + if (isValid && this.disabledDays && this.disabledDays.length) { + let weekdayNum = getDay(date); + isValid = this.disabledDays.indexOf(weekdayNum) === -1; + } + + if (isValid && this.min) { + isValid = isValid && !isBefore(date, startOfDay(this.min)); + } + + if (isValid && this.max) { + isValid = isValid && !isAfter(date, endOfDay(this.max)); + } + return isValid; + } + + /** + * Check if the month is current pickerMoment's month + * @param {Number} monthNum + * @return {Boolean} + * */ + public isCurrentMonth(monthNum: number): boolean { + return getMonth(this.pickerMoment) === monthNum; + } + + /** + * Check if the year is current pickerMoment's year + * @param {Number} yearNum + * @return {Boolean} + * */ + public isCurrentYear(yearNum: any): boolean { + return getYear(this.pickerMoment) === yearNum||(getYear(this.pickerMoment)+"") === yearNum; + } + + /** + * Change the dialog type + * @param {DialogType} type + * @return {void} + * */ + public changeDialogType(type: DialogType): void { + if (this.dialogType === type) { + this.dialogType = DialogType.Date; + return; + } else { + this.dialogType = type; + } + + if (this.dialogType === DialogType.Year) { + this.generateYearList(); + } + } + + /** + * Handle blur event on timer input + * @param {any} event + * @param {HTMLInputElement} input + * @param {string} type + * @param {number} modelValue + * @return {void} + * */ + public onTimerInputBlur(event: any, input: HTMLInputElement, type: string, modelValue: number): void { + let val = +input.value; + + if (this.disabled || val === modelValue) { + event.preventDefault(); + return; + } + + let done; + if (!isNaN(val)) { + switch (type) { + case 'hours': + if (this.hourFormat === '24' && + val >= 0 && val <= 23) { + done = this.setHours(event, val); + } else if (this.hourFormat === '12' + && val >= 1 && val <= 12) { + if (this.meridianValue === 'AM' && val === 12) { + val = 0; + } else if (this.meridianValue === 'PM' && val < 12) { + val = val + 12; + } + done = this.setHours(event, val); + } + break; + case 'minutes': + if (val >= 0 && val <= 59) { + done = this.setMinutes(event, val); + } + break; + case 'seconds': + if (val >= 0 && val <= 59) { + done = this.setSeconds(event, val); + } + break; + } + } + + if (!done) { + input.value = this.numFixedLenPipe.transform(modelValue, 2); + input.focus(); + return; + } + event.preventDefault(); + return; + } + + /** + * Set value to null + * @param {any} event + * @return {void} + * */ + public clearValue(event: any): void { + this.dialogClick = true; + this.updateModel(null); + this.updateTimer(this.value); + if (this.timepicker!==undefined) { + this.timepicker.settime(undefined); + } + this.updateFormattedValue(); + if(this.value!==null) + { + event.date=new Date(this.value); + } + this.onConfirm.emit(event); + event.preventDefault(); + } + + /** + * Show the dialog + * @return {void} + * */ + private show(): void { + this.alignDialog(); + this.dialogVisible = true; + this.dialogType = DialogType.Date; + this.bindDocumentClickListener(); + return; + } + private nextNav(event : any):void { + if( this.dialogType===DialogType.Date|| this.dialogType===DialogType.Month) + { + this.nextMonth(event); + } + else if(this.dialogType===DialogType.Year){ + this.generateYearList('next'); + } + } + private prevNav(event : any):void { + if( this.dialogType===DialogType.Date|| this.dialogType===DialogType.Month) + { + this.prevMonth(event); + } + else if(this.dialogType===DialogType.Year){ + this.generateYearList('prev'); + } + } + /** + * Hide the dialog + * @return {void} + * */ + private hide(): void { + this.dialogVisible = false; + this.timepicker ? this.timepicker.closeProp() : 0; + this.unbindDocumentClickListener(); + if(this.value!==null) + { + event["date"]=new Date(this.value); + } + this.onConfirm.emit(event); + return; + } + + /** + * Set the dialog position + * @return {void} + * */ + private alignDialog(): void { + let element = this.dialogElm.nativeElement; + let target = this.containerElm.nativeElement; + let elementDimensions = element.offsetParent ? { + width: element.offsetWidth, + height: element.offsetHeight + } : this.getHiddenElementDimensions(element); + let targetHeight = target.offsetHeight; + let targetWidth = target.offsetWidth; + let targetOffset = target.getBoundingClientRect(); + let viewport = this.getViewport(); + let top, left; + + if ((targetOffset.top + targetHeight + elementDimensions.height) > viewport.height) { + top = -1 * (elementDimensions.height); + if (targetOffset.top + top < 0) { + top = 0; + } + } else { + top = targetHeight; + } + + + if ((targetOffset.left + elementDimensions.width) > viewport.width) { + left = targetWidth - elementDimensions.width; + } else { + left = 0; + } + + element.style.top = top + 'px'; + element.style.left = left + 'px'; + } + + /** + * Bind click event on document + * @return {void} + * */ + private bindDocumentClickListener(): void { + let firstClick = true; + if (!this.documentClickListener) { + this.documentClickListener = this.renderer.listen('document', 'click', () => { + if (!firstClick && !this.dialogClick) { + this.hide(); + } + + firstClick = false; + this.dialogClick = false; + }); + } + return; + } + + /** + * Unbind click event on document + * @return {void} + * */ + private unbindDocumentClickListener(): void { + if (this.documentClickListener) { + this.documentClickListener(); + this.documentClickListener = null; + } + return; + } + + /** + * Parse a object to Date object + * @param {any} val + * @return {Date} + * */ + private parseToDate(val: any): Date { + if (!val) { + return; + } + + let parsedVal; + if (typeof val === 'string') { + parsedVal = parse(val); + } else { + parsedVal = val; + } + + return isValid(parsedVal) ? parsedVal : null; + } + + /** + * Generate the calendar days array + * @return {void} + * */ + private generateCalendar(): void { + + if (!this.pickerMoment) { + return; + } + + this.calendarDays = []; + let startDateOfMonth = startOfMonth(this.pickerMoment); + let startWeekdayOfMonth = getDay(startDateOfMonth); + + let dayDiff = 0 - (startWeekdayOfMonth + (7 - this.locale.firstDayOfWeek)) % 7; + + for (let i = 1; i < 7; i++) { + let week = []; + for (let j = 0; j < 7; j++) { + let date = addDays(startDateOfMonth, dayDiff); + let inOtherMonth = !isSameMonth(date, this.pickerMoment); + week.push({ + date, + num: getDate(date), + today: isToday(date), + otherMonth: inOtherMonth, + hide: !this.showOtherMonths && inOtherMonth, + }); + dayDiff += 1; + } + this.calendarDays.push(week); + } + + this.pickerMonth = this.locale.monthNames[getMonth(this.pickerMoment)]; + this.pickerYear = getYear(this.pickerMoment).toString(); + } + + /** + * Generate the calendar weekdays array + * */ + private generateWeekDays(): void { + this.calendarWeekdays = []; + let dayIndex = this.locale.firstDayOfWeek; + for (let i = 0; i < 7; i++) { + this.calendarWeekdays.push(this.locale.dayNamesShort[dayIndex]); + dayIndex = (dayIndex === 6) ? 0 : ++dayIndex; + } + } + + /** + * Generate the calendar month array + * @return {void} + * */ + private generateMonthList(): void { + this.calendarMonths = []; + let monthIndex = 0; + for (let i = 0; i < 4; i++) { + let months = []; + for (let j = 0; j < 3; j++) { + months.push(this.locale.monthNamesShort[monthIndex]); + monthIndex += 1; + } + this.calendarMonths.push(months); + } + } + + /** + * Generate the calendar year array + * @return {void} + * */ + public generateYearList(dir?: string): void { + + if (!this.pickerMoment) { + return; + } + let start; + + if (dir === 'prev') { + start = +this.calendarYears[0][0] - 12; + if(start<0) + { + start=0; + } + } else if (dir === 'next') { + start = +this.calendarYears[3][2] + 1; + } else { + start = getYear(addYears(this.pickerMoment, -4)); + } + + for (let i = 0; i < 4; i++) { + let years = []; + for (let j = 0; j < 3; j++) { + let year = (start + i * 3 + j).toString(); + years.push(year); + } + this.calendarYears[i] = years; + } + return; + } + + /** + * Update the calendar + * @param {Date} value + * @return {void} + * */ + private updateCalendar(value: Date): void { + + // if the dateTime picker is only the timer, + // no need to update the update Calendar. + if (this.type === 'timer') { + return; + } + + if (value && (!this.calendarDays || !isSameMonth(value, this.pickerMoment))) { + this.pickerMoment = setMonth(this.pickerMoment, getMonth(value)); + this.pickerMoment = setYear(this.pickerMoment, getYear(value)); + this.generateCalendar(); + } else if (!value && !this.calendarDays) { + this.generateCalendar(); + } + return; + } + + /** + * Update the timer + * @param {Date} value + * @return {boolean} + * */ + private updateTimer(value: Date): boolean { + + // if the dateTime picker is only the calendar, + // no need to update the timer + if (this.type === 'calendar') { + return false; + } + + if (!value) { + this.hourValue = null; + this.minValue = null; + this.secValue = null; + this.mtime.hour = 0; + this.mtime.minute = 0; + this.mtime.second = 0; + return true; + } + this.mtime.hour = value.getHours(); + this.mtime.minute = value.getMinutes(); + this.mtime.second = value.getSeconds();; + + let time = value; + let hours = getHours(time); + if (this.hourFormat === '12') { + if (hours < 12 && hours > 0) { + this.hourValue = hours; + this.meridianValue = 'AM'; + } else if (hours > 12) { + this.hourValue = hours - 12; + this.meridianValue = 'PM'; + } else if (hours === 12) { + this.hourValue = 12; + this.meridianValue = 'PM'; + } else if (hours === 0) { + this.hourValue = 12; + this.meridianValue = 'AM'; + } + } else if (this.hourFormat === '24') { + this.hourValue = hours; + } + + this.minValue = getMinutes(time); + this.secValue = getSeconds(time); + if(this.value!==undefined&&this.timepicker!==undefined) + { + this.timepicker.settime(new Date(this.value)); + } + return true; + } + + /** + * Update ngModel + * @param {Date} value + * @return {Boolean} + * */ + private updateModel(value: Date | Date[]): boolean { + this.value = value; + if (this.dataType === 'date') { + this.onModelChange(this.value); + } else if (this.dataType === 'string') { + if (this.value && this.value.length) { + let formatted = []; + for (let v of this.value) { + if (v) { + formatted.push(format(v, this.dateFormat, { locale: this.locale.dateFns })); + } else { + formatted.push(null); + } + } + this.onModelChange(formatted); + } else { + this.onModelChange(format(this.value, this.dateFormat, { locale: this.locale.dateFns })); + } + } + return true; + } + + /** + * Update variable formattedValue + * @return {void} + * */ + private updateFormattedValue(): void { + let formattedValue = ''; + + if (this.value) { + if (this.isSingleSelection()) { + formattedValue = format(this.value, this.dateFormat, { locale: this.locale.dateFns }); + } else if (this.isRangeSelection()) { + let startDate = this.value[0]; + let endDate = this.value[1]; + + formattedValue = format(startDate, this.dateFormat, { locale: this.locale.dateFns }); + + if (endDate) { + formattedValue += ' - ' + format(endDate, this.dateFormat, { locale: this.locale.dateFns }); + } else { + formattedValue += ' - ' + this.dateFormat; + } + } else if (this.isMultiSelection()) { + for (let i = 0; i < this.value.length; i++) { + let dateAsString = format(this.value[i], this.dateFormat, { locale: this.locale.dateFns }); + formattedValue += dateAsString; + if (i !== (this.value.length - 1)) { + formattedValue += ', '; + } + } + } + } + + this.formattedValue = formattedValue; + + return; + } + + /** + * Set the time + * @param {Date} val + * @return {boolean} + * */ + public setSelectedTime(val: Date): boolean { + let done; + if (this.isValidValue(val)) { + if (this.value instanceof Array) { + this.value[this.valueIndex] = val; + done = this.updateModel(this.value); + done = done && this.updateTimer(this.value[this.valueIndex]); + } else { + done = this.updateModel(val); + done = done && this.updateTimer(this.value); + } + this.updateFormattedValue(); + } else { + this.onInvalid.emit({ originalEvent: event, value: val }); + done = false; + } + return done; + } + + private isValidValue(value: Date): boolean { + let isValid = true; + + if (this.disabledDates && this.disabledDates.length) { + for (let disabledDate of this.disabledDates) { + if (isSameDay(disabledDate, value)) { + return false; + } + } + } + + if (isValid && this.disabledDays && this.disabledDays.length) { + let weekdayNum = getDay(value); + isValid = this.disabledDays.indexOf(weekdayNum) === -1; + } + + if (isValid && this.min) { + isValid = isValid && !isBefore(value, this.min); + } + + if (isValid && this.max) { + isValid = isValid && !isAfter(value, this.max); + } + + return isValid; + } + + /** + * Check if the selection mode is 'single' + * @return {boolean} + * */ + private isSingleSelection(): boolean { + return this.selectionMode === 'single'; + } + + /** + * Check if the selection mode is 'range' + * @return {boolean} + * */ + private isRangeSelection(): boolean { + return this.selectionMode === 'range'; + } + + /** + * Check if the selection mode is 'multiple' + * @return {boolean} + * */ + private isMultiSelection(): boolean { + return this.selectionMode === 'multiple'; + } + + private getHiddenElementDimensions(element: any): any { + let dimensions: any = {}; + element.style.visibility = 'hidden'; + element.style.display = 'block'; + dimensions.width = element.offsetWidth; + dimensions.height = element.offsetHeight; + element.style.display = 'none'; + element.style.visibility = 'visible'; + + return dimensions; + } + + private getViewport(): any { + let win = window, + d = document, + e = d.documentElement, + g = d.getElementsByTagName('body')[0], + w = win.innerWidth || e.clientWidth || g.clientWidth, + h = win.innerHeight || e.clientHeight || g.clientHeight; + + return { width: w, height: h }; + } + public confirm() { + this.hide(); + } + public seconds = false; + public mtime: any = { hour: 0, minute: 0, second: 0 }; + public TimerChange(time: any) { + let value; + if (this.value) { + if (this.value.length) { + value = this.value[this.valueIndex]; + } else { + value = this.value; + } + } else { + if (this.type === 'timer') { + value = new Date(); + } else { + value = new Date(); + } + } + + if (this.disabled || !value) { + event.preventDefault(); + return false; + } + + let minute = time.minute; + let hour = time.hour; + let second = time.second; + this.minValue = minute; + this.hourValue = hour; + this.secValue = second; + let selectedTime = setMinutes(value, minute); + selectedTime = setHours(selectedTime, hour); + selectedTime = setSeconds(selectedTime, second); + let done = this.setSelectedTime(selectedTime); + + // Focus the input and select its value when model updated + + event.preventDefault(); + return done; + } + private mouseIn :boolean = false; + private Mouseout(event:any) + { + this.mouseIn = false; + } + private Mouseover(event:any) + { + this.mouseIn = true; + } +} diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/picker.module.ts b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/picker.module.ts new file mode 100644 index 00000000..0511ad71 --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/picker.module.ts @@ -0,0 +1,27 @@ +/** + * picker.module + */ + +import { NgModule } from '@angular/core'; + +import { DateTimePickerComponent } from './picker.component'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { NumberFixedLenPipe } from './numberedFixLen.pipe'; +import { NgbTimepickerr } from './timepicker'; +import { OesDaterangePopover, OesDaterangePopoverWindow } from './popover'; +import { OesDaterangePopoverConfig } from './popover-config'; +import { NgbTimepickerConfig } from './timepicker-config'; +import { PlxDateRangePickerComponent } from './pickerrange.component' +export {DateTimePickerComponent} from './picker.component'; + +@NgModule({ + imports: [CommonModule, FormsModule], + exports: [DateTimePickerComponent, NgbTimepickerr, OesDaterangePopover,PlxDateRangePickerComponent], + declarations: [DateTimePickerComponent, NumberFixedLenPipe, NgbTimepickerr, OesDaterangePopoverWindow, OesDaterangePopover,PlxDateRangePickerComponent], + providers: [OesDaterangePopoverConfig, NgbTimepickerConfig, OesDaterangePopoverConfig], + entryComponents: [DateTimePickerComponent, OesDaterangePopoverWindow] +}) +export class PlxDatePickerModule { +} + diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/pickerrange.component.html b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/pickerrange.component.html new file mode 100644 index 00000000..2b1986fe --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/pickerrange.component.html @@ -0,0 +1,14 @@ +<div style="width:100%;"> +<div class="datepickboxleft" > + <plx-datepicker [canClear]="canClear" [supportKeyboardInput]="supportKeyboardInput" [disabled]="disabled" [(ngModel)]="startDate" [showTime]="showTime" [showSeconds]="showSeconds" [timeOnly]="timeOnly" [dateFormat]="dateFormat" [locale]="locale" [minDate]="startMinDate" [maxDate]="_startMaxDate" (onConfirm)="EvonStartDateClosed($event)" + placeHolder="{{placeHolderStartDate}}"></plx-datepicker> +</div> +<div class="datepickboxto" > +{{locale.to}} +</div> +<div class="datepickboxright" > + <plx-datepicker [canClear]="canClear" [supportKeyboardInput]="supportKeyboardInput" [disabled]="disabled" [(ngModel)]="endDate" [showTime]="showTime" [showSeconds]="showSeconds" [timeOnly]="timeOnly" [dateFormat]="dateFormat" [locale]="locale" [minDate]="_endMinDate" [maxDate]="endMaxDate" (onConfirm)="EvonEndDateClosed($event)" + placeHolder="{{placeHolderEndDate}}"></plx-datepicker> +</div> +<br/> +</div>
\ No newline at end of file diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/pickerrange.component.ts b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/pickerrange.component.ts new file mode 100644 index 00000000..a84e0987 --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/pickerrange.component.ts @@ -0,0 +1,162 @@ +/** + * picker.component + */ + +import { + AfterViewInit, + Component, ElementRef, EventEmitter, forwardRef, Input, OnDestroy, OnInit, Output, Renderer2, + ViewChild +} from '@angular/core'; +import {animate, state, style, transition, trigger} from '@angular/animations'; +import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'; + +export interface LocaleSettings { + firstDayOfWeek?: number; + dayNames: string[]; + dayNamesShort: string[]; + monthNames: string[]; + monthNamesShort: string[]; + dateFns: any; +} + +export enum DialogType { + Time, + Date, + Month, + Year, +} + +@Component({ + selector: 'plx-daterange-picker', + templateUrl: './pickerrange.component.html', + styleUrls: ['./pickerrange.component.css'], + providers: [], +}) + +export class PlxDateRangePickerComponent { + /* +disabled boolean false 设置为true时input框不能输入 +minDate Date null 最小可选日期 +maxDate Date null 最大可选日期 +showTime boolean false 设置为true时显示时间选择器 +showSeconds boolean false 时间选择器显示秒 +timeOnly boolean false 设置为true时只显示时间选择器 +dateFormat string YYYY-MM-DD HH:mm 设置时间选择模式 +locale Object null 设置国际化对象,请参考国际化例子。 +改变组件时间*/ + + @Input() disabled : boolean = false; + @Input() showTime : boolean = false; + @Input() showSeconds : boolean = false; + @Input() timeOnly : boolean = false; + @Input() dateFormat : string = "YYYY-MM-DD HH:mm"; + @Input() placeHolderStartDate : string = ""; + @Input() placeHolderEndDate : string = ""; + @Input() locale : any ={ + firstDayOfWeek: 0, + dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], + dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], + monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + dateFns: null, + confirm:'OK', + to:"to" + }; + @Input() startDate : Date; + @Input() endDate : Date; + @Input() canClear: boolean = true; + @Input() startMinDate:Date; + @Input() endMaxDate:Date; + /** + * @default false + * @type {Boolean} + * */ + @Input() supportKeyboardInput: boolean = false; + _startSetMaxDate:Date; + _startMaxDate:Date; + @Input() + set startMaxDate( date:Date) + { + this._startSetMaxDate=date; + this.BuildstartMaxDate(); + } + _endSetMinDate:Date; + _endMinDate:Date; + @Input() + set endMinDate( date:Date) + { + this._endSetMinDate=date; + this.BuildendMinDate(); + } + BuildstartMaxDate() + { + if(this._startSetMaxDate===undefined) + { + this._startMaxDate=this.endDate + return; + } + if(this.endDate!==undefined) + { + this._startMaxDate= this.endDate<this._startSetMaxDate?this.endDate:this._startSetMaxDate; + return; + } + this._startMaxDate=this._startSetMaxDate; + } + BuildendMinDate() + { + if(this._endSetMinDate===undefined) + { + this._endMinDate=this.startDate + return; + } + if(this.startDate!==undefined) + { + this._endMinDate= this.startDate>this._endSetMinDate?this.startDate:this._endSetMinDate; + return; + } + this._endMinDate=this._endSetMinDate; + } + + @Output() + onStartDateClosed: EventEmitter<any> = new EventEmitter<any>(); + @Output() + onEndDateClosed: EventEmitter<any> = new EventEmitter<any>(); + + EvonStartDateClosed(event : any) + { + this.BuildendMinDate(); + if(this.startDate!==null) + { + event.date=new Date(this.startDate); + } + this.onStartDateClosed.emit(event); + event.preventDefault(); + let dd= this; + return; + } + + + EvonEndDateClosed (event : any) + { + + this.BuildstartMaxDate() + if(this.endDate!==null) + { + event.date=new Date(this.endDate); + } + this.onEndDateClosed.emit(event); + event.preventDefault(); + let dd= this; + return; + } + + + public navigateTo (startDate: Date, endDate: Date) + { + this.startDate=startDate; + this.endDate = endDate; + } + + + +} diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/popover-config.ts b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/popover-config.ts new file mode 100644 index 00000000..5ac773c5 --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/popover-config.ts @@ -0,0 +1,13 @@ +import { Injectable } from '@angular/core'; + +/** + * Configuration service for the OesDaterangePopover directive. + * You can inject this service, typically in your root component, and customize the values of its properties in + * order to provide default values for all the popovers used in the application. + */ +@Injectable() +export class OesDaterangePopoverConfig { + public placement: 'top' | 'bottom' | 'left' | 'right' = 'top'; + public triggers = 'click'; + public container: string; +} diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/popover.ts b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/popover.ts new file mode 100644 index 00000000..3d054120 --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/popover.ts @@ -0,0 +1,175 @@ +import { + Component, + Directive, + Input, + Output, + EventEmitter, + ChangeDetectionStrategy, + OnInit, + OnDestroy, + Injector, + Renderer, + ComponentRef, + ElementRef, + TemplateRef, + ViewContainerRef, + ComponentFactoryResolver, + NgZone +} from '@angular/core'; + +import { listenToTriggers } from './util/triggers'; +import { positionElements } from './util/positioning'; +import { PopupService } from './util/popup'; +import { OesDaterangePopoverConfig } from './popover-config'; + +let nextId = 0; + +@Component({ + selector: 'ngb-popover-window', + changeDetection: ChangeDetectionStrategy.OnPush, + host: { '[class]': '"popover show popover-" + placement', 'role': 'tooltip', '[id]': 'id' }, + styles: [` + + .popover-title,.popover-content{ + background-color: #fff; + } + .popover-custom{ + padding:9px 5px !important; + } + + + `], + template: ` + <h3 class="popover-title">{{title}}</h3><div class="popover-content popover-custom"><ng-content></ng-content></div> + ` +}) +export class OesDaterangePopoverWindow { + @Input() public placement: 'top' | 'bottom' | 'left' | 'right' = 'top'; + @Input() public title: string; + @Input() public id: string; +} + +/** + * A lightweight, extensible directive for fancy oes-popover creation. + */ +@Directive({ selector: '[oesDaterangePopover]', exportAs: 'oesDaterangePopover' }) +export class OesDaterangePopover implements OnInit, OnDestroy { + /** + * Content to be displayed as oes-popover. + */ + @Input() public oesDaterangePopover: string | TemplateRef<any>; + /** + * Title of a oes-popover. + */ + @Input() public popoverTitle: string; + /** + * Placement of a oes-popover. Accepts: "top", "bottom", "left", "right" + */ + @Input() public placement: 'top' | 'bottom' | 'left' | 'right'; + /** + * Specifies events that should trigger. Supports a space separated list of event names. + */ + @Input() public triggers: string; + /** + * A selector specifying the element the oes-popover should be appended to. + * Currently only supports "body". + */ + @Input() public container: string; + /** + * Emits an event when the oes-popover is shown + */ + @Output() public shown = new EventEmitter(); + /** + * Emits an event when the oes-popover is hidden + */ + @Output() public hidden = new EventEmitter(); + + private _OesDaterangePopoverWindowId = `ngb-popover-${nextId++}`; + private _popupService: PopupService<OesDaterangePopoverWindow>; + private _windowRef: ComponentRef<OesDaterangePopoverWindow>; + private _unregisterListenersFn; + private _zoneSubscription: any; + + constructor( + private _elementRef: ElementRef, private _renderer: Renderer, injector: Injector, + componentFactoryResolver: ComponentFactoryResolver, viewContainerRef: ViewContainerRef, config: OesDaterangePopoverConfig, + ngZone: NgZone) { + this.placement = config.placement; + this.triggers = config.triggers; + this.container = config.container; + this._popupService = new PopupService<OesDaterangePopoverWindow>( + OesDaterangePopoverWindow, injector, viewContainerRef, _renderer, componentFactoryResolver); + + this._zoneSubscription = ngZone.onStable.subscribe(() => { + if (this._windowRef) { + positionElements( + this._elementRef.nativeElement, this._windowRef.location.nativeElement, this.placement, + this.container === 'body'); + } + }); + } + + /** + * Opens an element’s oes-popover. This is considered a “manual” triggering of the oes-popover. + * The context is an optional value to be injected into the oes-popover template when it is created. + */ + public open(context?: any) { + if (!this._windowRef) { + this._windowRef = this._popupService.open(this.oesDaterangePopover, context); + this._windowRef.instance.placement = this.placement; + this._windowRef.instance.title = this.popoverTitle; + this._windowRef.instance.id = this._OesDaterangePopoverWindowId; + + this._renderer.setElementAttribute(this._elementRef.nativeElement, 'aria-describedby', this._OesDaterangePopoverWindowId); + + if (this.container === 'body') { + window.document.querySelector(this.container).appendChild(this._windowRef.location.nativeElement); + } + + // we need to manually invoke change detection since events registered via + // Renderer::listen() are not picked up by change detection with the OnPush strategy + this._windowRef.changeDetectorRef.markForCheck(); + this.shown.emit(); + } + } + + /** + * Closes an element’s oes-popover. This is considered a “manual” triggering of the oes-popover. + */ + public close(): void { + if (this._windowRef) { + this._renderer.setElementAttribute(this._elementRef.nativeElement, 'aria-describedby', null); + this._popupService.close(); + this._windowRef = null; + this.hidden.emit(); + } + } + + /** + * Toggles an element’s oes-popover. This is considered a “manual” triggering of the oes-popover. + */ + public toggle(): void { + if (this._windowRef) { + this.close(); + } else { + this.open(); + } + } + + /** + * Returns whether or not the oes-popover is currently being shown + */ + public isOpen(): boolean { return this._windowRef !== null; } + + public ngOnInit() { + this._unregisterListenersFn = listenToTriggers( + this._renderer, this._elementRef.nativeElement, this.triggers, this.open.bind(this), this.close.bind(this), + this.toggle.bind(this)); + } + + public ngOnDestroy() { + this.close(); + this._unregisterListenersFn(); + this._zoneSubscription.unsubscribe(); + } +} diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/time.ts b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/time.ts new file mode 100644 index 00000000..ab31a498 --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/time.ts @@ -0,0 +1,51 @@ +import { isNumber, toInteger } from './util/util'; + +export class NgbTime { + public hour: number; + public minute: number; + public second: number; + + constructor(hour?: number, minute?: number, second?: number) { + this.hour = toInteger(hour); + this.minute = toInteger(minute); + this.second = toInteger(second); + } + + public changeHour(step = 1) { this.updateHour((isNaN(this.hour) ? 0 : this.hour) + step); } + + public updateHour(hour: number) { + if (isNumber(hour)) { + this.hour = (hour < 0 ? 24 + hour : hour) % 24; + } else { + this.hour = NaN; + } + } + + public changeMinute(step = 1) { this.updateMinute((isNaN(this.minute) ? 0 : this.minute) + step); } + + public updateMinute(minute: number) { + if (isNumber(minute)) { + this.minute = minute % 60 < 0 ? 60 + minute % 60 : minute % 60; + this.changeHour(Math.floor(minute / 60)); + } else { + this.minute = NaN; + } + } + + public changeSecond(step = 1) { this.updateSecond((isNaN(this.second) ? 0 : this.second) + step); } + + public updateSecond(second: number) { + if (isNumber(second)) { + this.second = second < 0 ? 60 + second % 60 : second % 60; + this.changeMinute(Math.floor(second / 60)); + } else { + this.second = NaN; + } + } + + public isValid(checkSecs = true) { + return isNumber(this.hour) && isNumber(this.minute) && (checkSecs ? isNumber(this.second) : true); + } + + public toString() { return `${this.hour || 0}:${this.minute || 0}:${this.second || 0}`; } +} diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/timepicker-config.ts b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/timepicker-config.ts new file mode 100644 index 00000000..8b752866 --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/timepicker-config.ts @@ -0,0 +1,19 @@ +import { Injectable } from '@angular/core'; + +/** + * Configuration service for the NgbTimepicker component. + * You can inject this service, typically in your root component, and customize the values of its properties in + * order to provide default values for all the timepickers used in the application. + */ +@Injectable() +export class NgbTimepickerConfig { + public meridian = false; + public spinners = true; + public seconds = false; + public hourStep = 1; + public minuteStep = 1; + public secondStep = 1; + public disabled = false; + public readonlyInputs = false; + public size: 'small' | 'medium' | 'large' = 'medium'; +} diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/timepicker.less b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/timepicker.less new file mode 100644 index 00000000..60acfa6b --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/timepicker.less @@ -0,0 +1,163 @@ +@import "../../assets/components/themes/default/theme.less"; +@import "../../assets/components/themes/common/plx-input.less"; +@import "../../assets/components/themes/common/plx-button.less"; +.oes-time-table .chevron::before { + border-style: solid; + border-width: 0.29em 0.29em 0 0; + content: ''; + display: inline-block; + height: 0.69em; + left: 0.05em; + position: relative; + top: 0.15em; + transform: rotate(-45deg); + -webkit-transform: rotate(-45deg); + -ms-transform: rotate(-45deg); + vertical-align: middle; + width: 0.71em; +} + +.oes-time-table .chevron.bottom:before { + top: -.3em; + -webkit-transform: rotate(135deg); + -ms-transform: rotate(135deg); + transform: rotate(135deg); +} + +.oes-time-table .btn-link { + border: none!important; + cursor: pointer; + outline: 0; + display: block; +} + +.oes-time-table .btn-link.disabled { + cursor: not-allowed; + opacity: .65; +} + +.oes-time-control { + text-align: center; +} + +.datapicker-form-control { + width: auto !important; + display: inline-block; +} + +.oes-time-table .ict-stretch{ + + font-size: 8px; +} + +.oes-time-table .ict-shrink{ + font-size: 8px; +} +.time-pick-bk{ + background-color: #fff; +} + +.btn-link:focus, .btn-link:hover{ + text-decoration: none; +} +.oes-time-control{ + border: 0; + width: 30px !important; + padding: 3px 0; + margin: 0; + font-size: @font-size; +} + +.oes-time-control:hover{ + background-color: #e6e6e6; + color:#000; + cursor: pointer; +} + + +.oes-time-control-foucs-bk{ + background-color: #00abff !important; + color:#fff!important; + +} + +.oes-time-separator{ + margin: 0 -5px; +} +.oes-time-group,.oes-time-group:hover{ + + border-bottom: 1px solid #ccc; + border-left: 1px solid #ccc; + border-top: 1px solid #ccc; + border-radius: 0.2em; + } + .oes-time-btns,.oes-time-btns:hover{ + + border-bottom: 1px solid #ccc; + border-right: 1px solid #ccc; + border-top: 1px solid #ccc; + border-radius: 0.2em; + padding: 0 0 7px 0 !important; + + } + + .oes-time-btns-wrapper { + margin-top:-3px; + transform:scale(0.6,0.6); + } + + .i18nTimeDes,.i18nTimeDes:hover{ + + padding: 0 5px 0px 0; + + } + + .oes-time-btn{ + + height: 5px; + } + + + .oes-time-table{ + margin-bottom: 10px; + } + +.hour-table{ + + font-size:12px; +} + +.hour-table td{ + + padding: 5px; + padding-top: 3px; + padding-bottom: 3px; + cursor: pointer; +} +.oes-time-btn-shrink{ + position: relative; + top:-5px; + left:0px; + color:#CCC; +} + +.oes-time-btn-stretch{ + position: relative; + left:0px; + color:#CCC; +} +.owl-calendar-timer-invalid{ + color: #acacac; +} +.owl-calendar-timer-selected{ + background-color: #00abff; + color: #FFFFFF; + border-radius: 1.2em; +} +.hour-table td:not(.owl-calendar-timer-selected):not(.owl-calendar-timer-invalid):hover { + background-color: #ebf6fd; + color: #000000; + border-radius: 1.2em; +} + + diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/timepicker.ts b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/timepicker.ts new file mode 100644 index 00000000..45dd7a4a --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/timepicker.ts @@ -0,0 +1,558 @@ +import { Component, Input, Output, forwardRef, OnChanges, EventEmitter, SimpleChanges, ViewChild } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; + +import { isNumber, padNumber, toInteger, isDefined } from './util/util'; +import { NgbTime } from './time'; +import { NgbTimepickerConfig } from './timepicker-config'; + +const NGB_TIMEPICKER_VALUE_ACCESSOR = { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => NgbTimepickerr), + multi: true +}; + +/** + * A lightweight & configurable timepicker directive. + */ +@Component({ + selector: 'oes-timepickerr', + styleUrls: ['./timepicker.less'], + template: ` + <template #popContentHour> + + <table class="hour-table"> + <tbody> + <tr><td (click)="selectHour(hour,$event)" *ngFor="let hour of hours1 " [ngClass]=" {'owl-calendar-timer-selected': isSelectedHour(hour), 'owl-calendar-timer-invalid': !isValidHour(hour)}">{{hour}}</td></tr> + <tr><td (click)="selectHour(hour,$event)" *ngFor="let hour of hours2 " [ngClass]=" {'owl-calendar-timer-selected': isSelectedHour(hour), 'owl-calendar-timer-invalid': !isValidHour(hour)}">{{hour}}</td></tr> + <tr><td (click)="selectHour(hour,$event)" *ngFor="let hour of hours3 " [ngClass]=" {'owl-calendar-timer-selected': isSelectedHour(hour), 'owl-calendar-timer-invalid': !isValidHour(hour)}">{{hour}}</td></tr> + + </tbody> + </table> + + </template> + + <template #popContentMin> + + <table class="hour-table"> + <tbody> + <tr><td (click)="selectMin(minuter,$event)" *ngFor="let minuter of minute1 " [ngClass]=" {'owl-calendar-timer-selected': isSelectedMin(minuter), 'owl-calendar-timer-invalid': !isValidMin(minuter)}">{{minuter}}</td></tr> + <tr><td (click)="selectMin(minuter,$event)" *ngFor="let minuter of minute2 " [ngClass]=" {'owl-calendar-timer-selected': isSelectedMin(minuter), 'owl-calendar-timer-invalid': !isValidMin(minuter)}">{{minuter}}</td></tr> + <tr><td (click)="selectMin(minuter,$event)" *ngFor="let minuter of minute3 " [ngClass]=" {'owl-calendar-timer-selected': isSelectedMin(minuter), 'owl-calendar-timer-invalid': !isValidMin(minuter)}">{{minuter}}</td></tr> + <tr><td (click)="selectMin(minuter,$event)" *ngFor="let minuter of minute4 " [ngClass]=" {'owl-calendar-timer-selected': isSelectedMin(minuter), 'owl-calendar-timer-invalid': !isValidMin(minuter)}">{{minuter}}</td></tr> + <tr><td (click)="selectMin(minuter,$event)" *ngFor="let minuter of minute5 " [ngClass]=" {'owl-calendar-timer-selected': isSelectedMin(minuter), 'owl-calendar-timer-invalid': !isValidMin(minuter)}">{{minuter}}</td></tr> + <tr><td (click)="selectMin(minuter,$event)" *ngFor="let minuter of minute6 " [ngClass]=" {'owl-calendar-timer-selected': isSelectedMin(minuter), 'owl-calendar-timer-invalid': !isValidMin(minuter)}">{{minuter}}</td></tr> + + </tbody> + </table> + + </template> + + <template #popContentSecond> + <table class="hour-table"> + <tbody> + <tr><td (click)="selectSecond(minuter,$event)" *ngFor="let minuter of minute1 " [ngClass]=" {'owl-calendar-timer-selected': isSelectedSec(minuter), 'owl-calendar-timer-invalid': !isValidSec(minuter)}">{{minuter}}</td></tr> + <tr><td (click)="selectSecond(minuter,$event)" *ngFor="let minuter of minute2 " [ngClass]=" {'owl-calendar-timer-selected': isSelectedSec(minuter), 'owl-calendar-timer-invalid': !isValidSec(minuter)}">{{minuter}}</td></tr> + <tr><td (click)="selectSecond(minuter,$event)" *ngFor="let minuter of minute3 " [ngClass]=" {'owl-calendar-timer-selected': isSelectedSec(minuter), 'owl-calendar-timer-invalid': !isValidSec(minuter)}">{{minuter}}</td></tr> + <tr><td (click)="selectSecond(minuter,$event)" *ngFor="let minuter of minute4 " [ngClass]=" {'owl-calendar-timer-selected': isSelectedSec(minuter), 'owl-calendar-timer-invalid': !isValidSec(minuter)}">{{minuter}}</td></tr> + <tr><td (click)="selectSecond(minuter,$event)" *ngFor="let minuter of minute5 " [ngClass]=" {'owl-calendar-timer-selected': isSelectedSec(minuter), 'owl-calendar-timer-invalid': !isValidSec(minuter)}">{{minuter}}</td></tr> + <tr><td (click)="selectSecond(minuter,$event)" *ngFor="let minuter of minute6 " [ngClass]=" {'owl-calendar-timer-selected': isSelectedSec(minuter), 'owl-calendar-timer-invalid': !isValidSec(minuter)}">{{minuter}}</td></tr> + </tbody> + </table> + </template> + <table class="oes-time-table"> + <tr> + <td class="i18nTimeDes"> + {{i18nTimeDes}} + </td> + <td class="oes-time-group"> + <input placement="top" style="padding-left:1px;padding-right:1px;border: 0; width: 30px !important;padding: 3px 0; margin: 0; font-size: 12px;" + [oesDaterangePopover]="popContentHour" #propHour="oesDaterangePopover" + #hourItem type="text" (focus)="selectItem('hour')" + [ngClass]="{'oes-time-control-foucs-bk': currSelectedItem === 'hour'}" + class="form-control datapicker-form-control form-control-sm oes-time-control " maxlength="2" size="2" placeholder="HH" + [value]="formatHour(model?.hour)" (change)="updateHour($event.target.value)" + [readonly]="readonlyInputs" [disabled]="disabled"> + <span class="oes-time-separator"> : </span> + <input + [oesDaterangePopover]="popContentMin" #propMin="oesDaterangePopover" + #minuteItem type="text" + (focus)="selectItem('minute')" style="padding-left:1px;padding-right:1px;border: 0; width: 30px !important;padding: 3px 0; margin: 0; font-size: 12px;" + [ngClass]="{'oes-time-control-foucs-bk': currSelectedItem === 'minute'}" + class="form-control datapicker-form-control form-control-sm oes-time-control" maxlength="2" size="2" placeholder="MM" + [value]="formatMinSec(model?.minute)" (change)="updateMinute($event.target.value)" + [readonly]="readonlyInputs" [disabled]="disabled"> + <span *ngIf="showSecondsTimer" class="oes-time-separator"> : </span> + <input *ngIf="showSecondsTimer" style="padding-left:1px;padding-right:1px;border: 0; width: 30px !important;padding: 3px 0; margin: 0; font-size: 12px;" + [oesDaterangePopover]="popContentSecond" #propSecond="oesDaterangePopover" + #secondItem type="text" + (focus)="selectItem('second')" + [ngClass]="{'oes-time-control-foucs-bk': currSelectedItem === 'second'}" + class="form-control datapicker-form-control form-control-sm oes-time-control" maxlength="2" size="2" placeholder="SS" + [value]="formatMinSec(model?.second)" (change)="updateSecond($event.target.value)" + [readonly]="readonlyInputs" [disabled]="disabled"> + </td> + + <td class="text-center oes-time-btns"> + <div class="oes-time-btns-wrapper"> + <button type="button" class="btn-link btn-sm oes-time-btn oes-time-btn-shrink " (click)="changeTime(hourStep)" + [disabled]="disabled" [class.disabled]="disabled"> + <span class="ict-shrink"></span> + </button> + <button type="button" class="btn-link btn-sm oes-time-btn oes-time-btn-stretch" (click)="changeTime(-hourStep)" + [disabled]="disabled" [class.disabled]="disabled"> + <span class="ict-stretch"></span> + </button> + </div> + </td> + </tr> + </table> + `, + providers: [NGB_TIMEPICKER_VALUE_ACCESSOR] +}) +export class NgbTimepickerr implements ControlValueAccessor, + OnChanges { + public disabled: boolean; + public model: NgbTime; + public datemodel: Date; + @Output() TimerChange = new EventEmitter<NgbTime>(); + /** + * Whether to display 12H or 24H mode. + */ + @Input() public meridian: boolean; + + /** + * Whether to display the spinners above and below the inputs. + */ + @Input() public spinners: boolean; + + /** + * Whether to display seconds input. + */ + @Input() public seconds: boolean; + + /** + * Number of hours to increase or decrease when using a button. + */ + @Input() public hourStep: number; + + /** + * Number of minutes to increase or decrease when using a button. + */ + @Input() public minuteStep: number; + + /** + * Number of seconds to increase or decrease when using a button. + */ + @Input() public secondStep: number; + + /** + * To make timepicker readonly + */ + @Input() public readonlyInputs: boolean; + + /** + * To set the size of the inputs and button + */ + @Input() public size: 'small' | 'medium' | 'large'; + + + + private _max: Date; + @Input() + get max() { + return this._max; + } + + set max(val: Date) { + this._max = val; + } + private _min: Date; + @Input() + get min() { + return this._min; + } + + set min(val: Date) { + this._min = val; + } + + /** + * Whether to show the second's timer + * @default false + * @type {Boolean} + * */ + @Input() showSecondsTimer: boolean; + /** + * datePicker的国际化描述 + */ + @Input() public i18nTimeDes: string; + + @ViewChild('hourItem') public hourItem; + + @ViewChild('minuteItem') public minuteItem; + @ViewChild('secondItem') public secondItem; + + @ViewChild('propHour') public propHour; + + @ViewChild('propMin') public propMin; + @ViewChild('propSecond') public propSecond; + + public currSelectedItem: 'hour' | 'minute' | 'second'; + + public hours1 = ['00', '01', '02', '03', '04', '05', '06', '07']; + + public hours2 = ['08', '09', '10', '11', '12', '13', '14', '15']; + + public hours3 = ['16', '17', '18', '19', '20', '21', '22', '23']; + + public minute1 = ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09']; + + public minute2 = ['10', '11', '12', '13', '14', '15', '16', '17', '18', '19']; + + public minute3 = ['20', '21', '22', '23', '24', '25', '26', '27', '28', '29']; + + public minute4 = ['30', '31', '32', '33', '34', '35', '36', '37', '38', '39']; + + public minute5 = ['40', '41', '42', '43', '44', '45', '46', '47', '48', '49']; + + public minute6 = ['50', '51', '52', '53', '54', '55', '56', '57', '58', '59']; + + constructor(config: NgbTimepickerConfig) { + this.meridian = config.meridian; + this.spinners = config.spinners; + this.seconds = config.seconds; + this.hourStep = config.hourStep; + this.minuteStep = config.minuteStep; + this.secondStep = config.secondStep; + this.disabled = config.disabled; + this.readonlyInputs = config.readonlyInputs; + this.size = config.size; + } + + public onChange = (_: any) => { + // TO DO + } + public onTouched = () => { + // TO DO + } + public settime(date : Date) + { + if(date!=null&&date!==undefined) + { + if(this._max!==undefined&&this._max.getTime()<date.getTime()) + { + date.setHours(this._max.getHours()); + date.setMinutes(this._max.getMinutes()); + date.setSeconds(this._max.getSeconds()); + this.TimerChange.emit(new NgbTime(date.getHours(),date.getMinutes(),date.getSeconds())); + } + if(this._min!==undefined&&this._min.getTime()>date.getTime()) + { + date.setHours(this._min.getHours()); + date.setMinutes(this._min.getMinutes()); + date.setSeconds(this._min.getSeconds()); + this.TimerChange.emit(new NgbTime(date.getHours(),date.getMinutes(),date.getSeconds())); + } + } + if(date!==null&&date!==undefined) + { + let temptime = new NgbTime(date.getHours(),date.getMinutes(),date.getSeconds()) + this.model = temptime; + this.datemodel = date; + } + else + { + let temptime = new NgbTime(0,0,0) + this.model = temptime; + this.datemodel = date; + } + + } + public selectHour(hour: string, event) { + if(!this.isValidHour(parseInt(hour))) + { + return; + } + this.model.hour = parseInt(hour); + this.propHour.close(); + this.propagateModelChange(); + event.stopPropagation(); + } + + public selectMin(minute: string, event) { + if(!this.isValidMin(parseInt(minute))) + { + return; + } + this.model.minute = parseInt(minute); + this.propMin.close(); + this.propagateModelChange(); + + event.stopPropagation(); + } + public selectSecond(second: string, event) { + if(!this.isValidSec(parseInt(second))) + { + return; + } + this.model.second = parseInt(second); + this.propSecond.close(); + this.propagateModelChange(); + + event.stopPropagation(); + } + + /** + * ###描述 + * 单击小时或者分钟选项时触发的事件 + * + * + * */ + + public selectItem(item: 'hour' | 'minute' | 'second') { + + // 切换选中项 + this.currSelectedItem = item; + + if (item === 'hour') { + + this.propMin?this.propMin.close():0; + this.propSecond?this.propSecond.close():0; + } else if (item === 'minute') { + this.propHour?this.propHour.close():0; + this.propSecond?this.propSecond.close():0; + } else if (item === 'second') { + this.propHour?this.propHour.close():0; + this.propMin?this.propMin.close():0; + } + + this.minuteItem.nativeElement.blur(); + this.hourItem.nativeElement.blur(); + + this.secondItem?this.secondItem.nativeElement.blur():0; + + // 弹出时间选择列表 + } + + public changeTime(stepTime) { + + if (this.currSelectedItem === 'hour') { // 如果当前选中的是小时 + + this.changeHour(stepTime); + + } else if (this.currSelectedItem === 'minute') { + + this.changeMinute(stepTime); + } else if (this.currSelectedItem === 'second') { + + this.changeSecond(stepTime); + } + + } + + + public writeValue(value) { + this.model = value ? new NgbTime(value.hour, value.minute, value.second) : new NgbTime(); + if (!this.seconds && (!value || !isNumber(value.second))) { + this.model.second = 0; + } + } + + public registerOnChange(fn: (value: any) => any): void { this.onChange = fn; } + + public registerOnTouched(fn: () => any): void { this.onTouched = fn; } + + public setDisabledState(isDisabled: boolean) { this.disabled = isDisabled; } + + public changeHour(step: number) { + let newDate = new Date(this.datemodel.getTime()); + newDate.setHours(newDate.getHours()+step); + if(!this.isValidDate(newDate)) + { + return; + } + this.model.changeHour(step); + this.propagateModelChange(); + } + + public changeMinute(step: number) { + let newDate = new Date(this.datemodel.getTime()); + newDate.setMinutes(newDate.getMinutes()+step); + if(!this.isValidDate(newDate)) + { + return; + } + this.model.changeMinute(step); + this.propagateModelChange(); + } + + public changeSecond(step: number) { + let newDate = new Date(this.datemodel.getTime()); + newDate.setSeconds(newDate.getSeconds()+step); + if(!this.isValidDate(newDate)) + { + return; + } + this.model.changeSecond(step); + this.propagateModelChange(); + } + + public updateHour(newVal: string) { + this.model.updateHour(toInteger(newVal)); + this.propagateModelChange(); + } + + public updateMinute(newVal: string) { + this.model.updateMinute(toInteger(newVal)); + this.propagateModelChange(); + } + + public updateSecond(newVal: string) { + this.model.updateSecond(toInteger(newVal)); + this.propagateModelChange(); + } + + public toggleMeridian() { + if (this.meridian) { + this.changeHour(12); + } + } + + public formatHour(value: number) { + if (isNumber(value)) { + if (this.meridian) { + return padNumber(value % 12 === 0 ? 12 : value % 12); + } else { + return padNumber(value % 24); + } + } else { + return padNumber(NaN); + } + } + + public formatMinSec(value: number) { return padNumber(value); } + + public setFormControlSize() { return { 'form-control-sm': this.size === 'small', 'form-control-lg': this.size === 'large' }; } + + public setButtonSize() { return { 'btn-sm': this.size === 'small', 'btn-lg': this.size === 'large' }; } + + + public ngOnChanges(changes: SimpleChanges): void { + if (changes['seconds'] && !this.seconds && this.model && !isNumber(this.model.second)) { + this.model.second = 0; + this.propagateModelChange(false); + } + } + + private propagateModelChange(touched = true) { + this.TimerChange.emit(this.model); + if (touched) { + this.onTouched(); + } + if (this.model.isValid(this.seconds)) { + this.onChange({ hour: this.model.hour, minute: this.model.minute, second: this.model.second }); + } else { + this.onChange(null); + } + } + public closeProp() + { + + if(this.propSecond!==undefined) + { + this.propSecond.close(); + } + if(this.propMin!==undefined) + { + this.propMin.close(); + } + if(this.propHour!==undefined) + { + this.propHour.close(); + } + } + private isValidDate(date: Date) + { + let isValid = true; + if (isValid && this._min!==undefined&&this._min!==null) { + isValid = date.getTime()>=this._min.getTime(); + } + if (isValid && this._max!==undefined&&this._max!==null) { + isValid = date.getTime()<=this._max.getTime(); + } + return isValid; + } + private isSelectedMin(strvalue:any): boolean { + let value = parseInt(strvalue); + if(this.model!==null&&this.model!==undefined) + { + return this.model.minute === value; + } + else + { + return false; + } +} + private isValidMin(strvalue:any): boolean { + let value = parseInt(strvalue); + let nowdate = new Date(); + if(this.datemodel===undefined||this.datemodel===null) + { + } + else + { + nowdate = new Date(this.datemodel); + } + nowdate.setMinutes(value); + return this.isValidDate(nowdate); +} +private isSelectedSec(strvalue:any): boolean { + let value = parseInt(strvalue); + if(this.model!==null&&this.model!==undefined) + { + return this.model.second === value; + } + else + { + return false; + } +} +private isValidSec(strvalue:any): boolean { + let value = parseInt(strvalue); + let nowdate = new Date(); + if(this.datemodel===undefined||this.datemodel===null) + { + } + else + { + nowdate = new Date(this.datemodel); + } + nowdate.setSeconds(value); + return this.isValidDate(nowdate); +} +private isSelectedHour(strvalue:any): boolean { + let value = parseInt(strvalue); + if(this.model!==null&&this.model!==undefined) + { + return this.model.hour === value; + } + else + { + return false; + } +} +private isValidHour(strvalue:any): boolean { + debugger; + let value = parseInt(strvalue); + let nowdate = new Date(); + if(this.datemodel===undefined||this.datemodel===null) + { + } + else + { + nowdate = new Date(this.datemodel); + } + nowdate.setHours(value); + return this.isValidDate(nowdate); +} +} diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/util/popup.ts b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/util/popup.ts new file mode 100644 index 00000000..56c26d62 --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/util/popup.ts @@ -0,0 +1,58 @@ +import { + Injector, + TemplateRef, + ViewRef, + ViewContainerRef, + Renderer, + ComponentRef, + ComponentFactory, + ComponentFactoryResolver +} from '@angular/core'; + +export class ContentRef { + constructor(public nodes: any[], public viewRef?: ViewRef, public componentRef?: ComponentRef<any>) {} +} + +export class PopupService<T> { + private _windowFactory: ComponentFactory<T>; + private _windowRef: ComponentRef<T>; + private _contentRef: ContentRef; + + constructor( + type: any, private _injector: Injector, private _viewContainerRef: ViewContainerRef, private _renderer: Renderer, + componentFactoryResolver: ComponentFactoryResolver) { + this._windowFactory = componentFactoryResolver.resolveComponentFactory<T>(type); + } + + public open(content?: string | TemplateRef<any>, context?: any): ComponentRef<T> { + if (!this._windowRef) { + this._contentRef = this._getContentRef(content, context); + this._windowRef = + this._viewContainerRef.createComponent(this._windowFactory, 0, this._injector, this._contentRef.nodes); + } + return this._windowRef; + } + + public close() { + if (this._windowRef) { + this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._windowRef.hostView)); + this._windowRef = null; + + if (this._contentRef.viewRef) { + this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._contentRef.viewRef)); + this._contentRef = null; + } + } + } + + private _getContentRef(content: string | TemplateRef<any>, context?: any): ContentRef { + if (!content) { + return new ContentRef([]); + } else if (content instanceof TemplateRef) { + const viewRef = this._viewContainerRef.createEmbeddedView(<TemplateRef<T>>content, context); + return new ContentRef([viewRef.rootNodes], viewRef); + } else { + return new ContentRef([[this._renderer.createText(null, `${content}`)]]); + } + } +} diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/util/positioning.ts b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/util/positioning.ts new file mode 100644 index 00000000..ed9005c1 --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/util/positioning.ts @@ -0,0 +1,153 @@ +// previous version: +// https://github.com/angular-ui/bootstrap/blob/07c31d0731f7cb068a1932b8e01d2312b796b4ec/src/position/position.js +export class Positioning { + private getStyle(element: HTMLElement, prop: string): string { return window.getComputedStyle(element)[prop]; } + + private isStaticPositioned(element: HTMLElement): boolean { + return (this.getStyle(element, 'position') || 'static') === 'static'; + } + + private offsetParent(element: HTMLElement): HTMLElement { + let offsetParentEl = <HTMLElement>element.offsetParent || document.documentElement; + + while (offsetParentEl && offsetParentEl !== document.documentElement && this.isStaticPositioned(offsetParentEl)) { + offsetParentEl = <HTMLElement>offsetParentEl.offsetParent; + } + + return offsetParentEl || document.documentElement; + } + + public position(element: HTMLElement, round = true): ClientRect { + let elPosition: ClientRect; + let parentOffset: ClientRect = {width: 0, height: 0, top: 0, bottom: 0, left: 0, right: 0}; + + if (this.getStyle(element, 'position') === 'fixed') { + elPosition = element.getBoundingClientRect(); + } else { + const offsetParentEl = this.offsetParent(element); + + elPosition = this.offset(element, false); + + if (offsetParentEl !== document.documentElement) { + parentOffset = this.offset(offsetParentEl, false); + } + + parentOffset.top += offsetParentEl.clientTop; + parentOffset.left += offsetParentEl.clientLeft; + } + + elPosition.top -= parentOffset.top; + elPosition.bottom -= parentOffset.top; + elPosition.left -= parentOffset.left; + elPosition.right -= parentOffset.left; + + if (round) { + elPosition.top = Math.round(elPosition.top); + elPosition.bottom = Math.round(elPosition.bottom); + elPosition.left = Math.round(elPosition.left); + elPosition.right = Math.round(elPosition.right); + } + + return elPosition; + } + + public offset(element: HTMLElement, round = true): ClientRect { + const elBcr = element.getBoundingClientRect(); + const viewportOffset = { + top: window.pageYOffset - document.documentElement.clientTop, + left: window.pageXOffset - document.documentElement.clientLeft + }; + + let elOffset = { + height: elBcr.height || element.offsetHeight, + width: elBcr.width || element.offsetWidth, + top: elBcr.top + viewportOffset.top, + bottom: elBcr.bottom + viewportOffset.top, + left: elBcr.left + viewportOffset.left, + right: elBcr.right + viewportOffset.left + }; + + if (round) { + elOffset.height = Math.round(elOffset.height); + elOffset.width = Math.round(elOffset.width); + elOffset.top = Math.round(elOffset.top); + elOffset.bottom = Math.round(elOffset.bottom); + elOffset.left = Math.round(elOffset.left); + elOffset.right = Math.round(elOffset.right); + } + + return elOffset; + } + + public positionElements(hostElement: HTMLElement, targetElement: HTMLElement, placement: string, appendToBody?: boolean): + ClientRect { + const hostElPosition = appendToBody ? this.offset(hostElement, false) : this.position(hostElement, false); + const shiftWidth: any = { + left: hostElPosition.left, + left2: (hostElPosition.left - 85), + center: hostElPosition.left + hostElPosition.width / 2 - targetElement.offsetWidth / 2, + right: hostElPosition.left + hostElPosition.width + }; + const shiftHeight: any = { + top: hostElPosition.top, + center: hostElPosition.top + hostElPosition.height / 2 - targetElement.offsetHeight / 2, + bottom: hostElPosition.top + hostElPosition.height + }; + const targetElBCR = targetElement.getBoundingClientRect(); + const placementPrimary = placement.split('-')[0] || 'top'; + const placementSecondary = placement.split('-')[1] || 'center'; + + let targetElPosition: ClientRect = { + height: targetElBCR.height || targetElement.offsetHeight, + width: targetElBCR.width || targetElement.offsetWidth, + top: 0, + bottom: targetElBCR.height || targetElement.offsetHeight, + left: 0, + right: targetElBCR.width || targetElement.offsetWidth + }; + + switch (placementPrimary) { + case 'top': + targetElPosition.top = hostElPosition.top - targetElement.offsetHeight; + targetElPosition.bottom += hostElPosition.top - targetElement.offsetHeight; + targetElPosition.left = shiftWidth[placementSecondary]; + targetElPosition.right += shiftWidth[placementSecondary]; + break; + case 'bottom': + targetElPosition.top = shiftHeight[placementPrimary]; + targetElPosition.bottom += shiftHeight[placementPrimary]; + targetElPosition.left = shiftWidth[placementSecondary]; + targetElPosition.right += shiftWidth[placementSecondary]; + break; + case 'left': + targetElPosition.top = shiftHeight[placementSecondary]; + targetElPosition.bottom += shiftHeight[placementSecondary]; + targetElPosition.left = hostElPosition.left - targetElement.offsetWidth; + targetElPosition.right += hostElPosition.left - targetElement.offsetWidth; + break; + case 'right': + targetElPosition.top = shiftHeight[placementSecondary]; + targetElPosition.bottom += shiftHeight[placementSecondary]; + targetElPosition.left = shiftWidth[placementPrimary]; + targetElPosition.right += shiftWidth[placementPrimary]; + break; + + } + + targetElPosition.top = Math.round(targetElPosition.top); + targetElPosition.bottom = Math.round(targetElPosition.bottom); + targetElPosition.left = Math.round(targetElPosition.left); + targetElPosition.right = Math.round(targetElPosition.right); + + return targetElPosition; + } +} + +const positionService = new Positioning(); +export function positionElements( + hostElement: HTMLElement, targetElement: HTMLElement, placement: string, appendToBody?: boolean): void { + const pos = positionService.positionElements(hostElement, targetElement, placement, appendToBody); + + targetElement.style.top = `${pos.top}px`; + targetElement.style.left = `${pos.left}px`; +} diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/util/triggers.ts b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/util/triggers.ts new file mode 100644 index 00000000..8197de5b --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/util/triggers.ts @@ -0,0 +1,62 @@ +export class Trigger { + constructor(public open: string, public close?: string) { + if (!close) { + this.close = open; + } + } + + public isManual() { return this.open === 'manual' || this.close === 'manual'; } +} + +const DEFAULT_ALIASES = { + hover: ['mouseenter', 'mouseleave'] +}; + +export function parseTriggers(triggers: string, aliases = DEFAULT_ALIASES): Trigger[] { + const trimmedTriggers = (triggers || '').trim(); + + if (trimmedTriggers.length === 0) { + return []; + } + + const parsedTriggers = trimmedTriggers.split(/\s+/).map(trigger => trigger.split(':')).map((triggerPair) => { + let alias = aliases[triggerPair[0]] || triggerPair; + return new Trigger(alias[0], alias[1]); + }); + + const manualTriggers = parsedTriggers.filter(triggerPair => triggerPair.isManual()); + + if (manualTriggers.length > 1) { + throw 'Triggers parse error: only one manual trigger is allowed'; + } + + if (manualTriggers.length === 1 && parsedTriggers.length > 1) { + throw 'Triggers parse error: manual trigger can\'t be mixed with other triggers'; + } + + return parsedTriggers; +} + +const noopFn = () => { + // TO DO +}; + +export function listenToTriggers(renderer: any, nativeElement: any, triggers: string, openFn, closeFn, toggleFn) { + const parsedTriggers = parseTriggers(triggers); + const listeners = []; + + if (parsedTriggers.length === 1 && parsedTriggers[0].isManual()) { + return noopFn; + } + + parsedTriggers.forEach((trigger: Trigger) => { + if (trigger.open === trigger.close) { + listeners.push(renderer.listen(nativeElement, trigger.open, toggleFn)); + } else { + listeners.push( + renderer.listen(nativeElement, trigger.open, openFn), renderer.listen(nativeElement, trigger.close, closeFn)); + } + }); + + return () => { listeners.forEach(unsubscribeFn => unsubscribeFn()); }; +} diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/util/util.ts b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/util/util.ts new file mode 100644 index 00000000..fcabe960 --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-datepicker/util/util.ts @@ -0,0 +1,39 @@ +export function toInteger(value: any): number { + return parseInt(`${value}`, 10); +} + +export function toString(value: any): string { + return (value !== undefined && value !== null) ? `${value}` : ''; +} + +export function getValueInRange(value: number, max: number, min = 0): number { + return Math.max(Math.min(value, max), min); +} + +export function isString(value: any): boolean { + return typeof value === 'string'; +} + +export function isNumber(value: any): boolean { + return !isNaN(toInteger(value)); +} + +export function isInteger(value: any): boolean { + return typeof value === 'number' && isFinite(value) && Math.floor(value) === value; +} + +export function isDefined(value: any): boolean { + return value !== undefined && value !== null; +} + +export function padNumber(value: number) { + if (isNumber(value)) { + return value > 9? `${value}`.slice(-2):'0' + `${value}`.slice(-2); + } else { + return ''; + } +} + +export function regExpEscape(text) { + return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); +} |