diff options
Diffstat (limited to 'sdc-workflow-designer-ui/src/app/paletx/plx-text-input')
12 files changed, 1819 insertions, 0 deletions
diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/index.ts b/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/index.ts new file mode 100644 index 00000000..c677a944 --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/index.ts @@ -0,0 +1,8 @@ +export * from './text-input.component'; +export * from './text-input.module'; +export * from './ipv4-validator.directive'; +export * from './ipv6-validator.directive'; +export * from './max-validator.directive'; +export * from './min-validator.directive'; +export * from './text-input-ip.component'; +export * from './text-input-ip-address.component'; diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/ipv4-validator.directive.ts b/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/ipv4-validator.directive.ts new file mode 100644 index 00000000..312ea5f3 --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/ipv4-validator.directive.ts @@ -0,0 +1,24 @@ +import {Directive, forwardRef} from '@angular/core'; +import {AbstractControl, NG_VALIDATORS, Validators} from '@angular/forms'; + +@Directive({ + selector: '[ipv4][ngModel],[ipv4][formControl],[ipv4][formControlName]', + providers: [{ + provide: NG_VALIDATORS, + useExisting: forwardRef(() => Ipv4ValidatorDirective), + multi: true + }], +}) + +export class Ipv4ValidatorDirective { + validate(c: AbstractControl) { + if (Validators.required(c) !== undefined && + Validators.required(c) !== null) { + return null; + } + const ipv4Reg = + /^((25[0-5]|2[0-4]\d|[0-1]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[0-1]?\d\d?)$/; + let regex = new RegExp(ipv4Reg); + return regex.test(c.value) ? null : {'ipv4': true}; + } +} diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/ipv6-validator.directive.ts b/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/ipv6-validator.directive.ts new file mode 100644 index 00000000..45182036 --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/ipv6-validator.directive.ts @@ -0,0 +1,24 @@ +import {Directive, forwardRef} from '@angular/core';
+import {AbstractControl, NG_VALIDATORS, Validators} from '@angular/forms';
+
+@Directive({
+ selector: '[ipv6][ngModel],[ipv6][formControl],[ipv6][formControlName]',
+ providers: [{
+ provide: NG_VALIDATORS,
+ useExisting: forwardRef(() => Ipv6ValidatorDirective),
+ multi: true
+ }],
+})
+
+export class Ipv6ValidatorDirective {
+ validate(c: AbstractControl) {
+ if (Validators.required(c) !== undefined &&
+ Validators.required(c) !== null) {
+ return null;
+ }
+ const ipv6Reg =
+ /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/;
+ let regex = new RegExp(ipv6Reg);
+ return regex.test(c.value) ? null : {'ipv6': true};
+ }4
+}
diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/max-validator.directive.ts b/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/max-validator.directive.ts new file mode 100644 index 00000000..143dccc6 --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/max-validator.directive.ts @@ -0,0 +1,49 @@ +import {AfterViewInit, Directive, ElementRef, forwardRef, Input} from '@angular/core'; +import {AbstractControl, NG_VALIDATORS, ValidatorFn, Validators} from '@angular/forms'; + +import {NumberWrapperParseFloat} from '../core/number-wrapper-parse'; + +@Directive({ + selector: '[max][ngModel],[max][formControl],[max][formControlName]', + providers: [{ + provide: NG_VALIDATORS, + useExisting: forwardRef(() => MaxValidatorDirective), + multi: true + }], +}) + +export class MaxValidatorDirective implements AfterViewInit { + private _validator: ValidatorFn; + private inputElement: any; + constructor(elementRef: ElementRef) { + this.inputElement = elementRef; + } + ngAfterViewInit() { + this.inputElement = this.inputElement.nativeElement.querySelector('input'); + if (this.inputElement && this.inputElement.querySelector('input')) { + this._validator = max(NumberWrapperParseFloat( + this.inputElement.querySelector('input').getAttribute('max'))); + } + } + @Input() + set max(maxValue: string) { + this._validator = max(NumberWrapperParseFloat(maxValue)); + } + + validate(c: AbstractControl): {[key: string]: any} { + return this._validator(c); + } +} + +function max(maxvalue: number): ValidatorFn { + return (control: AbstractControl): {[key: string]: any} => { + if (Validators.required(control) !== undefined && + Validators.required(control) !== null) { + return null; + } + let v: Number = Number(control.value); + return v > maxvalue ? + {'max': {'requiredValue': maxvalue, 'actualValue': v}} : + null; + }; +} diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/min-validator.directive.ts b/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/min-validator.directive.ts new file mode 100644 index 00000000..260a93ed --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/min-validator.directive.ts @@ -0,0 +1,49 @@ +import {AfterViewInit, Directive, ElementRef, forwardRef, Input} from '@angular/core'; +import {AbstractControl, NG_VALIDATORS, ValidatorFn, Validators} from '@angular/forms'; + +import {NumberWrapperParseFloat} from '../core/number-wrapper-parse'; + +@Directive({ + selector: '[min][ngModel],[min][formControl],[min][formControlName]', + providers: [{ + provide: NG_VALIDATORS, + useExisting: forwardRef(() => MinValidatorDirective), + multi: true + }], +}) + +export class MinValidatorDirective implements AfterViewInit { + private _validator: ValidatorFn; + private inputElement: any; + constructor(elementRef: ElementRef) { + this.inputElement = elementRef; + } + ngAfterViewInit() { + this.inputElement = this.inputElement.nativeElement.querySelector('input'); + if (this.inputElement && this.inputElement.querySelector('input')) { + this._validator = min(NumberWrapperParseFloat( + this.inputElement.querySelector('input').getAttribute('min'))); + } + } + @Input() + set min(minValue: string) { + this._validator = min(NumberWrapperParseFloat(minValue)); + } + + validate(c: AbstractControl): {[key: string]: any} { + return this._validator(c); + } +} + +function min(minvalue: number): ValidatorFn { + return (control: AbstractControl): {[key: string]: any} => { + if (Validators.required(control) !== undefined && + Validators.required(control) !== null) { + return null; + } + let v: Number = Number(control.value); + return v < minvalue ? + {'min': {'requiredValue': minvalue, 'actualValue': v}} : + null; + }; +} diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/text-input-ip-address.component.ts b/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/text-input-ip-address.component.ts new file mode 100644 index 00000000..501d2326 --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/text-input-ip-address.component.ts @@ -0,0 +1,170 @@ +import {Component, EventEmitter, forwardRef, Input, OnInit, Output} from '@angular/core';
+import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
+import {BooleanFieldValue} from '../core/boolean-field-value';
+
+const noop = () => {};
+
+export const PX_TEXT_INPUT_IP_ADDRESS_CONTROL_VALUE_ACCESSOR: any = {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => PlxTextInputIpAddressComponent),
+ multi: true
+};
+@Component({
+ selector: 'plx-text-input-ip-address',
+ template: `
+ <div>
+ <plx-text-input #textInputIpAddress type="text" [(ngModel)]="ipValue"
+ [width]="'280px'" [numberShowSpinner]="false" minlength="1"
+ (keyup)="keyUp($event)" (paste)="paste($event)" class="{{sizeClass}}"></plx-text-input>
+ <div class="plx-text-input-error">{{errMsg}}</div>
+ </div>
+ `,
+ styleUrls: ['text-input.less'],
+ host: {'style': 'display: inline-block;'},
+ providers: [PX_TEXT_INPUT_IP_ADDRESS_CONTROL_VALUE_ACCESSOR]
+})
+
+export class PlxTextInputIpAddressComponent implements OnInit, ControlValueAccessor {
+ @Input() lang: string = 'zh'; //zh|en
+ @Input() size: string; //空代表普通尺寸,sm代表小尺寸
+ @Input() ipAddressCheckTip: string = ''; //
+
+ @Input() @BooleanFieldValue() required: boolean = false;
+
+ @Input() public ipValue: string;
+ @Output() public ipValueChange: EventEmitter<any> = new EventEmitter<any>();
+
+ @Input() public ipValueFlg : boolean;
+ @Output() public ipValueFlgChange: EventEmitter<any> = new EventEmitter<any>();
+
+ private isNull : boolean = true;
+
+ /** Callback registered via registerOnTouched (ControlValueAccessor) */
+ private _onTouchedCallback: () => void = noop;
+ /** Callback registered via registerOnChange (ControlValueAccessor) */
+ private _onChangeCallback: (_: any) => void = noop;
+
+ public errMsgs = {
+ 'zh': {
+ 'empty': '此项不能为空',
+ 'invalidate': 'IP格式不对',
+ 'range': '请输入正确的IPV4地址或IPV6地址',
+ 'range-IPV4': '请输入正确的IPV4',
+ 'range-IPV6': '请输入正确的IPV6'
+ },
+ 'en': {
+ 'empty': 'IP can not be empty',
+ 'invalidate': 'IP format is incorrect',
+ 'range': 'IP range is IPV4 or IPV6',
+ 'range-IPV4': 'IP range is IPV4',
+ 'range-IPV6': 'IP range is IPV6'
+ }
+ };
+ public errMsg: string;
+ public sizeClass: string;
+
+ constructor() {
+ }
+
+ ngOnInit(): void {
+ if (this.size === 'sm') {
+ this.sizeClass = 'plx-input-sm';
+ }
+ this.isNull = this.ipValueFlg;
+ if(this.repIPStr(this.ipValue) === ''&& !this.ipValueFlg){
+ this.ipValueFlg = false;
+ this.emitValue();
+ }
+ }
+
+ public keyUp(event: any): void {
+ this.setValueToOutside(this.validate());
+ this.emitValue();
+ }
+
+ public paste(event: any): void{
+ setTimeout(() => {
+ this.ipValue = event.target.value;
+ this.setValueToOutside(this.validate());
+ this.emitValue();
+ }, 0);
+ }
+
+ private emitValue(){
+ this.ipValueChange.emit(this.ipValue);
+ this.ipValueFlgChange.emit(this.ipValueFlg);
+ }
+
+ private setValueToOutside(validateFlg: boolean): void {
+ this.ipValueFlg = validateFlg;
+ let value;
+ if (validateFlg) {
+ if (this.ipValue) {
+ value = this.ipValue;
+ }
+ if(this.ipValue === "" && !this.isNull){
+ this.ipValueFlg = false;
+ }
+ } else {
+ value = false;
+ }
+ this._onChangeCallback(value);
+ }
+
+ writeValue(value: any): void {
+ //
+ this.errMsg = '';
+ this.ipValue = value;
+ if (value) {
+ this.validate();
+ }
+ }
+
+ registerOnChange(fn: any) {
+ this._onChangeCallback = fn;
+ }
+
+ registerOnTouched(fn: any) {
+ this._onTouchedCallback = fn;
+ }
+
+ public validate(): boolean {
+ this.errMsg = '';
+ if (this.required) {
+ if (!this.ipValue) {
+ this.errMsg = this.errMsgs[this.lang]['empty'];
+ return false;
+ }
+ }
+ if ((this.ipValue) && (!this.ipValue)) {
+ this.errMsg = this.errMsgs[this.lang]['invalidate'];
+ return false;
+ }
+ let blackStr = this.repIPStr(this.ipValue);
+ if(this.ipAddressCheckTip === ''){
+ if(this.ipValue !== '' && blackStr === ''){
+ this.errMsg = this.errMsgs[this.lang]['range'];
+ return false;
+ }
+ }else{
+ if(this.ipValue !== '' && this.ipAddressCheckTip !== blackStr) {
+ this.errMsg = this.errMsgs[this.lang]['range-'+ this.ipAddressCheckTip];
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private repIPStr(value: any): string {
+ let blackStr = '';
+ var regip4 = /^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$/;
+ if (regip4.test(value)) {
+ return "IPV4";
+ }
+ var regip6 = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/;
+ if (regip6.test(value)) {
+ return "IPV6";
+ }
+ return blackStr;
+ }
+}
diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/text-input-ip.component.ts b/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/text-input-ip.component.ts new file mode 100644 index 00000000..7c9d616d --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/text-input-ip.component.ts @@ -0,0 +1,189 @@ +import {Component, forwardRef, Input, OnInit} from '@angular/core'; +import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'; + +import {BooleanFieldValue} from '../core/boolean-field-value'; + +const noop = () => {}; + +export const PX_TEXT_INPUT_IP_CONTROL_VALUE_ACCESSOR: any = { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => PlxTextInputIpComponent), + multi: true +}; + +@Component({ + selector: 'plx-text-input-ip', + template: ` + <div> + <plx-text-input #textInputIp1 type="number" [(ngModel)]="ipValue1" + [width]="'45px'" [numberShowSpinner]="false" maxLength="3" minlength="1" + (keyup)="keyup($event, null, textInputIp2)" (change)="change($event)" + [ngClass]="{'plx-input-sm': size==='sm', 'plx-text-input-ip-invalid': errMsg}"></plx-text-input> + <span class="plx-text-input-ip-dot">.</span> + <plx-text-input #textInputIp2 type="number" [(ngModel)]="ipValue2" + [width]="'45px'" [numberShowSpinner]="false" maxLength="3" minlength="1" + (keyup)="keyup($event, textInputIp1, textInputIp3)" (change)="change($event)" + [ngClass]="{'plx-input-sm': size==='sm', 'plx-text-input-ip-invalid': errMsg}"></plx-text-input> + <span class="plx-text-input-ip-dot">.</span> + <plx-text-input #textInputIp3 type="number" [(ngModel)]="ipValue3" + [width]="'45px'" [numberShowSpinner]="false" maxLength="3" minlength="1" + (keyup)="keyup($event, textInputIp2, textInputIp4)" (change)="change($event)" + [ngClass]="{'plx-input-sm': size==='sm', 'plx-text-input-ip-invalid': errMsg}"></plx-text-input> + <span class="plx-text-input-ip-dot">.</span> + <plx-text-input #textInputIp4 type="number" [(ngModel)]="ipValue4" + [width]="'45px'" [numberShowSpinner]="false" maxLength="3" minlength="1" + (keyup)="keyup($event, textInputIp3, null)" (change)="change($event)" + [ngClass]="{'plx-input-sm': size==='sm', 'plx-text-input-ip-invalid': errMsg}"></plx-text-input> + <div class="plx-text-input-error">{{errMsg}}</div> + </div> + `, + styleUrls: ['text-input.less'], + host: {'style': 'display: inline-block;'}, + providers: [PX_TEXT_INPUT_IP_CONTROL_VALUE_ACCESSOR] +}) + +export class PlxTextInputIpComponent implements OnInit, ControlValueAccessor { + @Input() lang: string = 'zh'; //zh|en + @Input() size: string; //空代表普通尺寸,sm代表小尺寸 + @Input() @BooleanFieldValue() required: boolean = false; + /** Callback registered via registerOnTouched (ControlValueAccessor) */ + private _onTouchedCallback: () => void = noop; + /** Callback registered via registerOnChange (ControlValueAccessor) */ + private _onChangeCallback: (_: any) => void = noop; + public ipValue1: number; + public ipValue2: number; + public ipValue3: number; + public ipValue4: number; + public errMsgs = { + 'zh': { + 'empty': 'IP不能为空', + 'invalidate': '非法IP', + 'range': 'IP范围[0.0.0.0]~[255.255.255.255]' + }, + 'en': { + 'empty': 'IP can not be empty', + 'invalidate': 'Invalid IP', + 'range': 'IP range is [0.0.0.0]~[255.255.255.255]' + } + }; + public errMsg: string; + + constructor() { + } + + ngOnInit(): void { + } + + public change(event: any) :void { + event.target.value = this.repNumber(event.target.value); + this.setValueToOutside(this.validate()); + } + + public keyup(event: any, frontElement: any, backElement: any): void { + event.target.value = this.repNumber(event.target.value); + if (((event.keyCode === 13 || event.keyCode === 110 || event.keyCode === 190) + && event.target.value.length !== 0) + || event.target.value.length === 3) { //enter:13,dot:110、190 + if (event.keyCode !== 9) { //tab:9 + if (backElement) { + backElement.autoFocus = true; + backElement.inputViewChild.nativeElement.select(); + } else { + if (event.keyCode !== 110 && event.keyCode !== 190) { + event.target.blur(); + } + } + } + } + + if (event.target.value.length === 0 // backspace:8 delete:46 + && (event.keyCode === 8 || event.keyCode === 46)) { + if (frontElement) { + frontElement.autoFocus = true; + frontElement.inputViewChild.nativeElement.select(); + } + } + + this.setValueToOutside(this.validate()); + } + + private setValueToOutside(validateFlg: boolean): void { + let value; + if (validateFlg) { + if (this.ipValue1 && this.ipValue2 && this.ipValue3 && this.ipValue4) { + value = this.ipValue1 + '.' + + this.ipValue2 + '.'+ this.ipValue3 + '.'+ this.ipValue4; + } + } else { + value = false; + } + this._onChangeCallback(value); + } + + writeValue(value: any): void { + // + this.errMsg = ''; + if (value) { + if (this.isIPStr(value)) { + let ipArr = value.split('.'); + this.ipValue1 = ipArr[0]; + this.ipValue2 = ipArr[1]; + this.ipValue3 = ipArr[2]; + this.ipValue4 = ipArr[3]; + } else { + this.errMsg = this.errMsgs[this.lang]['invalidate'] + ' : ' + value; + } + } + } + + registerOnChange(fn: any) { + this._onChangeCallback = fn; + } + + registerOnTouched(fn: any) { + this._onTouchedCallback = fn; + } + + public validate(): boolean { + this.errMsg = ''; + if (this.required) { + if (!this.ipValue1 && !this.ipValue2 && !this.ipValue3 && !this.ipValue4) { + this.errMsg = this.errMsgs[this.lang]['empty']; + return false; + } + } + if ((this.ipValue1 || this.ipValue2 || this.ipValue3 || this.ipValue4) + && (!this.ipValue1 || !this.ipValue2 || !this.ipValue3 || !this.ipValue4)) { + this.errMsg = this.errMsgs[this.lang]['invalidate']; + return false; + } + if ((this.ipValue1 && (this.ipValue1 < 0 || this.ipValue1 > 255)) + || (this.ipValue2 && (this.ipValue2 < 0 || this.ipValue2 > 255)) + || (this.ipValue3 && (this.ipValue3 < 0 || this.ipValue3 > 255)) + || (this.ipValue4 && (this.ipValue4 < 0 || this.ipValue4 > 255))) { + this.errMsg = this.errMsgs[this.lang]['range']; + return false; + } + + return true; + } + + private repNumber(value: any): number { + var reg = /^[\d]+$/g; + if (!reg.test(value)) { + let txt = value; + txt.replace(/[^0-9]+/, function (char, index, val) { //匹配第一次非数字字符 + value = val.replace(/\D/g, ""); //将非数字字符替换成"" + }) + } + return value; + } + + private isIPStr(value: any): boolean { + var regip4 = /^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$/; + if (regip4.test(value)) { + return true; + } + return false; + } +} diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/text-input.component.ts b/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/text-input.component.ts new file mode 100644 index 00000000..9b5a01e9 --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/text-input.component.ts @@ -0,0 +1,765 @@ +import {AfterContentInit, Component, ElementRef, EventEmitter, forwardRef, HostBinding, HostListener, Input, OnInit, Output, Renderer2, ViewChild} from '@angular/core'; +import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'; +import {Observable} from 'rxjs/Observable'; + +import {BooleanFieldValue} from '../core/boolean-field-value'; +import {NumberWrapperParseFloat} from '../core/number-wrapper-parse'; +import {UUID} from '../core/uuid'; + +const noop = () => {}; + +export const PX_TEXT_INPUT_CONTROL_VALUE_ACCESSOR: any = { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => PlxTextInputComponent), + multi: true +}; + +@Component({ + selector: 'plx-text-input', + templateUrl: 'text-input.html', + styleUrls: ['text-input.less'], + host: {'style': 'display: inline-block;'}, + providers: [PX_TEXT_INPUT_CONTROL_VALUE_ACCESSOR] +}) + +export class PlxTextInputComponent implements ControlValueAccessor, OnInit, + AfterContentInit { + private _focused: boolean = false; + private _value: any = ''; + /** Callback registered via registerOnTouched (ControlValueAccessor) */ + private _onTouchedCallback: () => void = noop; + /** Callback registered via registerOnChange (ControlValueAccessor) */ + private _onChangeCallback: (_: any) => void = noop; + + /** Readonly properties. */ + get empty() { + return this._value === null || this._value === ''; + } + get inputId(): string { + return `${this.id}`; + } + get isShowHintLabel() { + return this._focused && this.hintLabel !== null; + } + get inputType() { + return this.type === 'number' ? 'text' : this.type; + } + + @Input() id: string = `plx-input-${UUID.UUID()}`; + @Input() name: string = null; + @Input() hintLabel: string = null; + @Input() lang: string = 'zh'; + @Input() @BooleanFieldValue() disabled: boolean = false; + + @Input() numberShowSpinner = true; + @Input() max: string|number = null; + @Input() maxLength: number = 64; + @Input() min: string|number = null; + @Input() minLength: number = null; + @Input() placeholder: string = ''; + @Input() @BooleanFieldValue() readOnly: boolean = false; + @Input() @BooleanFieldValue() required: boolean = false; + @Input() @BooleanFieldValue() notShowOption: boolean = true; + @Input() type: string = 'text'; + @Input() tabIndex: number = null; + @Input() pattern: string = null; + + @Input() @BooleanFieldValue() shortInput: boolean = false; + @Input() unit: string = null; + @Input() unitOptions: string[] = null; + @Input() prefix: string = null; + @Input() suffixList: string[] = null; + + // @Input() precision: number = 0; + @Input() step: number = 1; + @Input() width: string = '400px'; + @Input() unitWidth: string = '45px'; + @Input() unitOptionWidth: string = '84px'; + @Input() prefixWidth: string = '70px'; + @Input() historyList: string[]; + + @Input() prefixOptions: string[] = []; + @Input() prefixOptionWidth: string = '84px'; + + @Input() @BooleanFieldValue() passwordSwitch: boolean = false; + + @ViewChild('input') inputViewChild: ElementRef; + @ViewChild('inputOutter') pxTextInputElement: ElementRef; + + @HostBinding('class.input-invalid') selectClass: boolean = true; + + isDisabledUp: boolean = false; + isDisabledDown: boolean = false; + displayDataList: string[]; + currentPrecision: number = 0; + keyPattern: RegExp = /[0-9\-]/; + langPattern: RegExp = + /[a-zA-Z]|[\u4e00-\u9fa5]|[\uff08\uff09\u300a\u300b\u2014\u2014\uff1a\uff1b\uff0c\u201c\u201d\u2018\u2019\+\=\{\}\u3001\u3002\u3010\u3011\<\>\uff01\uff1f\/\|]/g; + timer: any; + optionalLabel: string = null; + hasSelection = false; + _precision: number = 0; + displayValue: any; + + isOpenDataList: boolean = false; + dataListClicked: boolean = false; + isOpenSuffixList: boolean = false; + suffixListClicked: boolean = false; + + showUnit: string; + isShowUnitOption: boolean = false; + unitOptionClicked: boolean = false; + + prefixOptionClicked: boolean = false; + isShowPrefixOption = false; + showPrefix: string; + showPassword: boolean = false; + tooltipText: string; + tooltipTexts = { + 'zh': { + 'true': '隐藏', + 'false': '显示', + }, + 'en': { + 'true': 'hidden', + 'false': 'show', + } + }; + isPwdSwithHover: boolean = false; + isPwdSwithClick: boolean = false; + + _autoFocus: boolean = false; + @Input() + set autoFocus(value: boolean) { + this._autoFocus = value; + + const that = this; + if (this._autoFocus) { + setTimeout(() => { + that.inputViewChild.nativeElement.focus(); + }, 0); + } + } + get autoFocus() { + return this._autoFocus; + } + + @Input() + set precision(value: string) { + this._precision = parseInt(value); + } + get precision() { + return this._value; + } + + get inputWidth() { + if (this.prefixOptions && this.prefixOptions.length > 0) { + if (this.unitOptions && this.unitOptions.length > 0) { + return `calc(${this.width} - ${this.prefixOptionWidth} - ${this.unitOptionWidth})`; + } else if (this.unit !== null) { + return `calc(${this.width} - ${this.prefixOptionWidth} - ${this.unitWidth})`; + } else { + return `calc(${this.width} - ${this.prefixOptionWidth})`; + } + } + + if (this.unit !== null && this.prefix !== null) { + return `calc(${this.width} - ${this.unitWidth} - ${this.prefixWidth})`; + } + + if (this.unit !== null) { + return `calc(${this.width} - ${this.unitWidth})`; + } + + if (!!this.unitOptions && this.unitOptions.length !== 0 && + this.prefix !== null) { + return `calc(${this.width} - ${this.unitOptionWidth} - ${ + this.prefixWidth + })`; + } + + if (!!this.unitOptions && this.unitOptions.length !== 0) { + return `calc(${this.width} - ${this.unitOptionWidth})`; + } + if (this.prefix !== null) { + return `calc(${this.width} - ${this.prefixWidth})`; + } + return this.width; + } + + + get hasUnit() { + return this.unit !== null; + } + get hasUnitOption() { + return this.showUnit !== undefined; + } + get hasPrefix() { + return this.prefix !== null; + } + get hasPrefixOption() { + return this.prefixOptions && this.prefixOptions.length > 0; + } + get isFocus() { + return this._focused; + } + + private _blurEmitter: EventEmitter<FocusEvent> = + new EventEmitter<FocusEvent>(); + private _focusEmitter: EventEmitter<FocusEvent> = + new EventEmitter<FocusEvent>(); + private click = new EventEmitter<any>(); + private unitChange = new EventEmitter<any>(); + @Output() public prefixChange = new EventEmitter<any>(); + + @Output('blur') + get onBlur(): Observable<FocusEvent> { + return this._blurEmitter.asObservable(); + } + + @Output('focus') + get onFocus(): Observable<FocusEvent> { + return this._focusEmitter.asObservable(); + } + + @HostListener('focus') + onHostFocus() { + this.renderer.addClass(this.el.nativeElement, 'input-focus'); + this.renderer.removeClass(this.el.nativeElement, 'input-blur'); + } + + @HostListener('blur') + onHostBlur() { + this.renderer.addClass(this.el.nativeElement, 'input-blur'); + this.renderer.removeClass(this.el.nativeElement, 'input-focus'); + } + + @Input() + set value(v: any) { + v = this.filterZhChar(v); + v = this._convertValueForInputType(v); + if (v !== this._value) { + this._value = v; + if (this.type === 'number') { + if (this._value === '') { + this._onChangeCallback(null); + } else if (isNaN(this._value)) { + this._onChangeCallback(this._value); + } else { + this._onChangeCallback(NumberWrapperParseFloat(this._value)); + } + } else { + this._onChangeCallback(this._value); + } + } + } + get value(): any { + return this._value; + } + + constructor( + private el: ElementRef, private renderer: Renderer2) {} + + ngOnInit() { + if (this.shortInput) { + this.width = '120px'; + this.unitWidth = '40px'; + this.prefixWidth = '40px'; + } + this.translateLabel(); + + if (!!this.unitOptions && this.unitOptions.length !== 0) { + this.showUnit = this.unitOptions[0]; + } + + if (!!this.prefixOptions && this.prefixOptions.length !== 0) { + this.showPrefix = this.prefixOptions[0]; + } + } + + ngAfterContentInit() { + if (this.pxTextInputElement) { + Array.from(this.pxTextInputElement.nativeElement.childNodes) + .forEach((node: HTMLElement) => { + if (node.nodeType === 3) { + this.pxTextInputElement.nativeElement.removeChild(node); + } + }); + } + } + private translateLabel() { + if (this.lang === 'zh') { + this.optionalLabel = '(可选)'; + } else { + this.optionalLabel = '(Optional)'; + } + } + + _handleFocus(event: FocusEvent) { + this._focused = true; + this._focusEmitter.emit(event); + } + + _handleBlur(event: FocusEvent) { + this._focused = false; + this._onTouchedCallback(); + this._blurEmitter.emit(event); + } + + _checkValueLimit(value: any) { + if (this.type === 'number') { + if ((value === '' || value === undefined || value === null) && + !this.required) { + return ''; + } else if ( + this.min !== null && + NumberWrapperParseFloat(value) < NumberWrapperParseFloat(this.min)) { + return this.min; + } else if ( + this.max !== null && + NumberWrapperParseFloat(value) > NumberWrapperParseFloat(this.max)) { + return this.max; + } else { + return value; + } + } + return value; + } + + _checkValue() { + this.value = this._checkValueLimit(this.value); + this.displayValue = this.value; + } + + _handleChange(event: Event) { + this.value = (<HTMLInputElement>event.target).value; + this._onTouchedCallback(); + } + + openDataList() { + this.dataListClicked = true; + if (this.historyList) { + if (this.value) { + this.filterOption(this.value); + } else { + this.displayDataList = this.historyList; + if (!this.isOpenDataList) { + this.isOpenDataList = true; + } + } + } + } + + _handleClick(event: Event) { + if (this.isShowUnitOption) { + this.isShowUnitOption = false; + } + this.click.emit(event); + + if (this.historyList) { + this.openDataList(); + } + } + + _handleSelect(event: Event) { // 输入框文本被选中时处理 + if (!this.hasSelection) { + this.hasSelection = true; + } + } + deleteSelection() { // 删除选中文本, + document.getSelection().deleteFromDocument(); + this.hasSelection = false; + } + + _onWindowClick(event: Event) { + if (this.historyList) { + if (!this.dataListClicked) { + this.isOpenDataList = false; + } + this.dataListClicked = false; + } + + if (this.suffixList) { + if (!this.suffixListClicked) { + this.isOpenSuffixList = false; + } + this.suffixListClicked = false; + } + + if (this.unitOptions) { + if (!this.unitOptionClicked) { + this.isShowUnitOption = false; + } + this.unitOptionClicked = false; + } + + if (this.prefixOptions) { + if (!this.prefixOptionClicked) { + this.isShowPrefixOption = false; + } + this.prefixOptionClicked = false; + } + } + + _showPrefixOption(event: Event) { + this.isShowPrefixOption = !this.isShowPrefixOption; + this.prefixOptionClicked = true; + } + + _showUnitOption(event: Event) { + this.isShowUnitOption = !this.isShowUnitOption; + this.unitOptionClicked = true; + } + + _setUnit(unitValue: string) { + this.unitOptionClicked = true; + this.showUnit = unitValue; + this.unitChange.emit(unitValue); + this.isShowUnitOption = false; + } + + _setPrefix(value: string) { + if (value !== this.showPrefix) { + this.showPrefix = value; + this.prefixChange.emit(value); + } + this.prefixOptionClicked = true; + this.isShowPrefixOption = false; + } + + filterOption(value: any) { + this.displayDataList = []; + this.displayDataList = this.historyList.filter((data: string) => { + return data.toLowerCase().indexOf(value.toLowerCase()) > -1; + }); + + this.isOpenDataList = this.displayDataList.length !== 0; + } + + concatValueAndSuffix(value: string) { + const that = this; + that.displayDataList = []; + let mark = '@'; + if (value === '') { + that.displayDataList = []; + } else if (value.trim().toLowerCase().indexOf(mark) === -1) { + that.displayDataList.push(value); + that.suffixList.map((item: string) => { + let tempValue = value + mark + item; + that.displayDataList.push(tempValue); + }); + } else { + that.suffixList.map((item: string) => { + let tempValue = value.split(mark)[0] + mark + item; + that.displayDataList.push(tempValue); + }); + that.displayDataList = that.displayDataList.filter((item: string) => { + return item.trim().toLowerCase().indexOf(value) > -1; + }); + } + + that.isOpenSuffixList = that.displayDataList.length !== 0; + } + + _handleInput(event: any) { + let inputValue = event.target.value.trim().toLowerCase(); + + if (this.historyList) { + this.filterOption(inputValue); + } + + if (this.suffixList) { + this.concatValueAndSuffix(inputValue); + } + } + + chooseInputData(data: any) { + this.displayValue = data; + this.value = data; + + this.isOpenDataList = false; + this.dataListClicked = true; + this.isOpenSuffixList = false; + this.suffixListClicked = true; + } + + writeValue(value: any) { + this.displayValue = this._checkValueLimit(value); + this._value = this.displayValue; + } + + registerOnChange(fn: any) { + this._onChangeCallback = fn; + } + + registerOnTouched(fn: any) { + this._onTouchedCallback = fn; + } + + private getConvertValue(v: any) { + this.currentPrecision = v.toString().split('.')[1].length; + if (this.currentPrecision === 0) { // 输入小数点,但小数位数为0时 + return v; + } + if (this.currentPrecision < + this._precision) { // 输入小数点,且小数位数不为0 + return this.toFixed(v, this.currentPrecision); + } else { + return this.toFixed(v, this._precision); + } + } + + private filterZhChar(v: any) { + if (this.type === 'number') { + let reg = /[^0-9.-]/; + while (reg.test(v)) { + v = v.replace(reg, ''); + } + } + return v; + } + + private _convertValueForInputType(v: any): any { + switch (this.type) { + case 'number': { + if (v === '' || v === '-') { + return v; + } + + if (v.toString().indexOf('.') === -1) { // 整数 + return this.toFixed(v, 0); + } else { + return this.getConvertValue(v); + } + } + default: + return v; + } + } + + private toFixed(value: number, precision: number) { + return Number(value).toFixed(precision); + } + + repeat(interval: number, dir: number) { + let i = interval || 500; + + this.clearTimer(); + this.timer = setTimeout(() => { + this.repeat(40, dir); + }, i); + + this.spin(dir); + } + + clearTimer() { + if (this.timer) { + clearInterval(this.timer); + } + } + + spin(dir: number) { + let step = this.step * dir; + let currentValue = this._convertValueForInputType(this.value) || 0; + + this.value = Number(currentValue) + step; + + if (this.maxLength !== null && + this.value.toString().length > this.maxLength) { + this.value = currentValue; + } + + if (this.min !== null && this.value <= NumberWrapperParseFloat(this.min)) { + this.value = this.min; + this.isDisabledDown = true; + } + + if (this.max !== null && this.value >= NumberWrapperParseFloat(this.max)) { + this.value = this.max; + this.isDisabledUp = true; + } + this.displayValue = this.value; + this._onChangeCallback(NumberWrapperParseFloat(this.value)); + } + + onUpButtonMousedown(event: Event, input: HTMLInputElement) { + if (!this.disabled && this.type === 'number') { + input.focus(); + this.repeat(null, 1); + event.preventDefault(); + } + } + + onUpButtonMouseup(event: Event) { + if (!this.disabled) { + this.clearTimer(); + } + } + + onUpButtonMouseleave(event: Event) { + if (!this.disabled) { + this.clearTimer(); + } + } + + onDownButtonMousedown(event: Event, input: HTMLInputElement) { + if (!this.disabled && this.type === 'number') { + input.focus(); + this.repeat(null, -1); + event.preventDefault(); + } + } + + onDownButtonMouseup(event: Event) { + if (!this.disabled) { + this.clearTimer(); + } + } + + onDownButtonMouseleave(event: Event) { + if (!this.disabled) { + this.clearTimer(); + } + } + + onInputKeydown(event: KeyboardEvent) { + if (this.type === 'number') { + if (event.which === 229) { + event.preventDefault(); + return; + } else if (event.which === 38) { + this.spin(1); + event.preventDefault(); + } else if (event.which === 40) { + this.spin(-1); + event.preventDefault(); + } + } + } + onInputKeyPress(event: KeyboardEvent) { + let inputChar = String.fromCharCode(event.charCode); + if (this.type === 'number') { + if (event.which === 8) { + return; + } + + // if (!this.isValueLimit()) { + // this.handleSelection(event); + // } + + if (inputChar === '-' && this.min !== null && + NumberWrapperParseFloat(this.min) >= 0) { + event.preventDefault(); + return; + } + if (this.isIllegalNumberInputChar(event) || + this.isIllegalIntergerInput(inputChar)) { + event.preventDefault(); + return; + } + if (this.isIllegalFloatInput( + inputChar)) { // 当该函数返回true时,执行两种情景 + this.handleSelection(event); + } + if (this.hasSelection) { // 文本被选中,执行文本替换 + this.deleteSelection(); + } + } + } + + private handleSelection(event: any) { + if (this.hasSelection) { // 文本被选中,执行文本替换 + this.deleteSelection(); + } else { // 无选中文本,阻止非法输入 + event.preventDefault(); + } + } + // private isValueLimit() { + // if (this.min !== null && NumberWrapperParseFloat(this.value) !== 0 && + // this.value <= NumberWrapperParseFloat(this.min)) { + // return false; + // } + // if (this.max !== null && NumberWrapperParseFloat(this.value) !== 0 && + // this.value >= NumberWrapperParseFloat(this.max)) { + // return false; + // } + // return true; + // } + + private isIllegalNumberInputChar(event: KeyboardEvent) { + /* 8:backspace, 46:. */ + return !this.keyPattern.test(String.fromCharCode(event.charCode)) && + event.which !== 46 && event.which !== 0; + } + + private isIllegalIntergerInput(inputChar: string) { + return this._precision === 0 && + (inputChar === '.' || + (this._value && this._value && this._value.toString().length > 0 && + inputChar === '-')); + } + + private isIllegalFloatInput(inputChar: string) { + return this._precision > 0 && this._value && + ((this._value.toString().length > 0 && inputChar === '-') || + ((this._value.toString() === '' || + this._value.toString().indexOf('.') > 0) && + inputChar === '.') || + (this._value.toString().indexOf('.') > 0 && + this._value.toString().split('.')[1].length === this._precision)); + } + + onInput(event: Event, inputValue: string) { + this.value = inputValue; + } + + //处理鼠标经过上下箭头时,样式设置 + isEmptyValue() { + if (this.value === undefined || this.value === null || this.value === '') { + return true; + } + return false; + } + + isDisabledUpCaret() { + if (this.isEmptyValue()) { + return true; + } else if ( + this.max !== null && + (NumberWrapperParseFloat(this.value) >= + NumberWrapperParseFloat(this.max))) { + return true; + } + return false; + } + + isDisabledDownCaret() { + if (this.isEmptyValue()) { + return true; + } else if ( + this.min !== null && + (NumberWrapperParseFloat(this.value) <= + NumberWrapperParseFloat(this.min))) { + return true; + } + return false; + } + + _handleMouseEnterUp() { + this.isDisabledUp = this.isDisabledUpCaret(); + } + + _handleMouseEnterDown() { + this.isDisabledDown = this.isDisabledDownCaret(); + } + + public switch(): void { + this.showPassword = !this.showPassword; + this.showPassword?this.inputViewChild.nativeElement.type = + 'text':this.inputViewChild.nativeElement.type = 'password'; + } + + private setPasswordTooltip(): void { + this.tooltipTexts[this.lang][this.showPassword.toString()] + } +} diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/text-input.html b/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/text-input.html new file mode 100644 index 00000000..9065badd --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/text-input.html @@ -0,0 +1,69 @@ +<div #inputOutter class="text-input" + [class.text-input-with-hint]="isShowHintLabel" + [class.short-text-input]="shortInput" + [class.text-input-with-unit]="hasUnit" + [class.text-input-with-unitOption]="hasUnitOption" + [class.text-input-with-prefix]="hasPrefix" + [class.text-input-with-prefixOption]="hasPrefixOption" + [class.text-input-with-passwordSwith]="passwordSwitch" + [class.input-right-border]="isShowUnitOption" + [class.input-left-border]="isShowPrefixOption" + [class.input-right-border-pwdswith-hover]="isPwdSwithHover" + [class.input-right-border-pwdswith-click]="isPwdSwithClick" + (window:click)="_onWindowClick($event)"> + <div #prefixOpt *ngIf="hasPrefixOption" [class.prefix-focus]="isShowPrefixOption" class="text-input-prefix-option" (click)="_showPrefixOption($event)">{{showPrefix}}<span class="toggle"></span></div> + <div *ngIf="isShowPrefixOption" class="plx-text-input-prefix-group"> + <li *ngFor="let option of prefixOptions" (click)="_setPrefix(option)" [ngClass]="{'group-selected':showPrefix===option}">{{option}}</li> + </div> + <div *ngIf="hasPrefix" class="text-input-prefix" [style.width]="prefixWidth">{{prefix}}</div> + <span [class.input-span-focus]="isFocus ||isOpenDataList" class="input-span"> + <input #input + class="plx-input" + [disabled]="disabled" + [id]="inputId" + [name]="name" + [attr.max]="max" + [attr.maxlength]="maxLength" + [attr.min]="min" + [attr.minlength]="minLength" + [attr.tabindex]="tabIndex" + [attr.pattern]="pattern" + [placeholder]="placeholder" + [readonly]="readOnly" + [required]="required" + [type]="inputType" + [(ngModel)]="displayValue" + (click)="_handleClick($event)" + (focus)="_handleFocus($event)" + (blur)="_handleBlur($event);_checkValue()" + (input)="_handleInput($event)" + (change)="_handleChange($event)" + (select)="_handleSelect($event)" + (keydown)="onInputKeydown($event)" (keyup)="onInput($event,input.value)" + (keypress)="onInputKeyPress($event)" [style.width]="inputWidth" validateOnBlur/> + <a class="input-spinner-up" [style.cursor]="isDisabledUp ? 'not-allowed' : 'pointer'" *ngIf="type === 'number' && numberShowSpinner" (mouseenter)="_handleMouseEnterUp()" + (mouseleave)="onUpButtonMouseleave($event)" (mousedown)="onUpButtonMousedown($event,input)" + (mouseup)="onUpButtonMouseup($event)"> + <span class="caret-up" [class.caret-up-hover]="!isDisabledUp"></span> + </a> + <a class="input-spinner-down" [style.cursor]="isDisabledDown ? 'not-allowed':'pointer'" *ngIf="type === 'number' && numberShowSpinner" (mouseenter)="_handleMouseEnterDown()" + (mouseleave)="onDownButtonMouseleave($event)" (mousedown)="onDownButtonMousedown($event,input)" + (mouseup)="onDownButtonMouseup($event)"> + <span class="caret-down" [class.caret-down-hover]="!isDisabledDown"></span> + </a> + </span><div *ngIf="hasUnit" class="text-input-unit" [style.width]="unitWidth">{{unit}}</div> + + <div *ngIf="hasUnitOption" [class.unit-focus]="isShowUnitOption" class="text-input-unit-option" (click)="_showUnitOption($event)" [style.width]="unitOptionWidth" >{{showUnit}}<span class="toggle"></span></div> + <div *ngIf="isShowUnitOption" class="plx-text-input-unit-group" [style.margin-left]="inputWidth"> + <li *ngFor="let option of unitOptions" (click)="_setUnit(option)" [ngClass]="{'group-selected':showUnit===option}">{{option}}</li> + </div> + <div *ngIf="!required && !notShowOption" class="text-input-optional">{{optionalLabel}}</div> + <div *ngIf="isShowHintLabel" class="text-input-hint">{{hintLabel}}</div> + <div *ngIf = "isOpenDataList || isOpenSuffixList" class = "text-input-dataList"> + <li *ngFor="let data of displayDataList" (click)="chooseInputData(data)">{{data}}</li> + </div> + <span *ngIf="passwordSwitch" placement="bottom" plxTooltip="{{tooltipText}}" [ngClass]="showPassword?'ict-eye-closed':'ict-eye'" + class="plx-input-passwordSwitch" (click)='switch()' + (mouseover)="isPwdSwithHover=true" (mouseleave)="isPwdSwithHover=false" + (mousedown)="isPwdSwithClick=true" (mouseup)="isPwdSwithClick=false"></span> +</div> diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/text-input.less b/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/text-input.less new file mode 100644 index 00000000..6a93c1c1 --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/text-input.less @@ -0,0 +1,423 @@ +@import "../../assets/components/themes/default/theme.less"; +@import "../../assets/components/themes/common/plx-input.less"; + +@input-short-width: 120px; +@padding-left: 10px; +@padding: 10px; +@border-width: 1px; +@unit-span-width: 45px; +@unit-option-width: 84px; +@short-unit-span-width: 40px; +@prefix-span-width: 70px; +@prefix-option-width: 84px; +@short-prefix-span-width: 40px; +@password-switch: 40px; + +.font { + font-family: @font-family; + font-size: @font-size; +} + +.text-input { + //height: @input-height; + //position: relative; + //margin-bottom: 0; + display: inline-block; + + .caret-down { + display: block; + width: 0; + height: 0; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 4px solid lighten(@fonticon-color, 5%); + margin-top: 4px; + margin-bottom: 10px; + + &.caret-down-hover:hover, &.caret-down-hover:active { + border-top: 4px solid @primary-color; + } + } + .caret-up { + display: block; + width: 0; + height: 0; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-bottom: 4px solid lighten(@fonticon-color, 5%); + margin-top: 10px; + + &.caret-up-hover:hover, &.caret-up-hover:active { + border-bottom: 4px solid @primary-color; + } + } + .toggle { + float: right; + margin-right: 10px; + margin-top: 14px; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 4px solid lighten(@fonticon-color, 5%); + } + .text-input-dataList { + margin-top: 2px; + position: absolute; + z-index: @z-index-dropdown; + border: 1px solid @gray-grade-7; + background: #fff; + cursor: pointer; + border-radius: @radius; + li { + list-style: none; + height: @input-height; + width: @input-width; + padding-left: @padding-left; + &:hover { + background-color: @hover-bg-color; + } + } + } +} + +.input-span { + display: inline-block; + overflow: visible; + padding: 0; + position: relative; +} + +.text-input-with-hint { + margin-bottom: -8px; + :host(.ng-touched.ng-invalid.input-blur) & { + height: auto; + margin-bottom: 0; + } +} + +.plx-text-input-unit-group, .plx-text-input-prefix-group { + position: absolute; + margin-top: @overlay-margin-top; + width: @unit-option-width; + z-index: @z-index-dropdown; + border-radius: @radius; + background: @component-bg; + border: 1px solid @border-color-base; + .shadow; + cursor: pointer; + li { + padding-left: 10px; + height: @input-height; + list-style: none; + line-height: @input-height; + font-size: @font-size; + &:hover { + background-color: @hover-bg-color; + } + &.group-selected, &.group-selected:hover { + background-color: @selected-bg-color; + color: @text-color; + } + } +} + +.text-input-optional { + display: inline-block; + margin-right: 6px; + padding-left:5px; +} + +.input-right-border .plx-input { + border-right: 1px solid @primary-color; +} + +.input-left-border .plx-input { + border-left: 1px solid @primary-color; +} + +.text-input-hint { + top: 42px; + left: 10px; + font-family: @font-family; + font-size: @font-size; + color: @disabled-text-color; + :host(.ng-touched.ng-invalid.input-blur) & { + display: none; + } +} + +.text-input-prefix { + .font; + display: inline-block; + width: @prefix-span-width; + height: @input-height; + text-align: center; + line-height: @input-height; + border-top-left-radius: @radius; + border-bottom-left-radius: @radius; + color: @disabled-text-color; + border: 1px solid @border-color-base; + border-right: 0; + vertical-align: middle; + .short-text-input & { + width: @short-prefix-span-width; + } + .input-span-focus & { + border-color: @primary-color; + } + .input-invalid.ng-dirty.ng-invalid.ng-touched.input-blur &, + .input-invalid.ng-dirty.ng-invalid.ng-touched.input-blur .input-span-focus:focus & { + border-color: @error-color; + } +} + +.input-unit { + .font; + display: inline-block; + height: @input-height; + text-align: center; + line-height: @input-height; + border-top-right-radius: @radius; + border-bottom-right-radius: @radius; +} + +.text-input-unit { + border: 1px solid @border-color-base; + border-left: 0; + .input-unit; + color: @disabled-text-color; + width: @unit-span-width; + text-align: center; + vertical-align: middle; + .short-text-input & { + width: @short-unit-span-width; + } + .input-span-focus & { + border-color: @primary-color; + } +} + +.text-input-prefix-option { + .font; + display: inline-block; + height: @input-height; + text-align: center; + line-height: @input-height; + border-top-left-radius: @radius; + border-bottom-left-radius: @radius; + width: @prefix-option-width; + text-align: left; + padding-left: 10px; + cursor: pointer; + border: 1px solid @border-color-base;; + border-right: 0; + vertical-align: middle; + &.prefix-focus { + border-color: @primary-color; + } +} + +.text-input-unit-option { + &:extend(.input-unit); + width: @unit-option-width; + text-align: left; + padding-left: 10px; + cursor: pointer; + border: 1px solid @border-color-base;; + border-left: 0; + vertical-align: middle; + .input-span-focus & { + border-color: @primary-color; + } +} + +.text-input-with-unitOption { + div.unit-focus { + border-color: @primary-color; + } +} + +.plx-input { + .short-text-input & { + width: @input-short-width; + } + + .text-input-with-unit & { + width: @input-width - @unit-span-width; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + + .text-input-with-unitOption & { + width: @input-width - @unit-option-width; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + + .text-input-with-prefix & { + width: @input-width - @prefix-span-width; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + + .text-input-with-prefixOption & { + width: @input-width - @prefix-option-width; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + + .text-input-with-passwordSwith & { + width: @input-width - @password-switch; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + + .text-input-with-prefix.text-input-with-unit & { + width: @input-width - @prefix-span-width - @unit-span-width; + } + + .text-input-with-prefix.text-input-with-unitOption & { + width: @input-width - @prefix-span-width - @unit-option-width; + } + + .short-text-input.text-input-with-prefix & { + width: @input-short-width - @short-prefix-span-width; + } + + .short-text-input.text-input-with-unit & { + width: @input-short-width - @short-unit-span-width; + } + + .short-text-input.text-input-with-prefix.text-input-with-unit & { + width: @input-short-width - @short-prefix-span-width - @short-unit-span-width; + } +} + +.input-spinner() { + cursor: pointer; + display: block; + font-size: 12px; + position: absolute; + margin: 0; + right: 0; + overflow: hidden; + border: none; + padding: 0; + text-align: center; + vertical-align: middle; + width: 18px; +} + +.input-spinner-up { + .input-spinner(); + top: 0; +} + +.input-spinner-down { + .input-spinner(); + bottom: 0; +} + +:host(.plx-input-sm) { + .plx-input-sm-common; +} + +.plx-input-sm { + .plx-input-sm-common; +} + +.plx-input-sm-common { + .plx-input { + height: @input-height-sm; + line-height: @input-height-sm; + } + .text-input-prefix, + .text-input-unit, + .text-input-unit-option, + .text-input-prefix-option { + height: @input-height-sm; + line-height: @input-height-sm; + } + div.text-input-dataList { + height: @input-height-sm; + } + .toggle { + margin-top: 11px; + } + .caret-down { + margin-bottom: 8px; + } + .caret-up { + margin-top: 8px; + } + .plx-input-passwordSwitch { + line-height: @input-height-sm - 2px; + } +} + +.plx-input-passwordSwitch { + display: inline-block; + line-height: @input-height - 2px; + width: @password-switch; + text-align: center; + vertical-align: middle; + background-color: @common-color; + border: 1px solid @border-color-base; + border-left: 0; + border-bottom-right-radius: @radius; + border-top-right-radius: @radius; + cursor: pointer; + &:focus, + &:hover { + border-color: @btn-common-color-border-hover; + background-color: @common-color-hover; + &.ict-eye-closed, &.ict-eye { + color: @btn-common-color-text-hover; + } + } + &:active { + background-color: @common-color-click; + border-color: @btn-common-color-border-click; + &.ict-eye-closed, &.ict-eye { + color: @btn-common-color-text-click; + } + } + &.ict-eye-closed, &.ict-eye { + color: @fonticon-color; + font-size: 16px; + } + } +.input-right-border-pwdswith-hover .plx-input { + border-right-color: @btn-common-color-border-hover; +} +.input-right-border-pwdswith-click .plx-input { + border-right-color: @btn-common-color-border-click; +} + +.plx-text-input-ip-dot { + display: inline-block; + vertical-align: bottom; + color: #999; +} + +.plx-text-input-error { + font-size: 12px; + color: @error-color; + margin-top: 5px; +} + +:host(.plx-text-input-ip-invalid) { + .plx-text-input-ip-invalid-common; +} + +.plx-text-input-ip-invalid { + .plx-text-input-ip-invalid-common; +} + +.plx-text-input-ip-invalid-common { + .plx-input { + border-color: @error-color; + } + .input-span-focus .plx-input { + border-color: @primary-color; + } +} diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/text-input.module.ts b/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/text-input.module.ts new file mode 100644 index 00000000..4374770a --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/text-input.module.ts @@ -0,0 +1,31 @@ +import {CommonModule} from '@angular/common'; +import {NgModule} from '@angular/core'; +import {FormsModule} from '@angular/forms'; + +import {PlxTooltipModule} from '../plx-tooltip/plx-tooltip.module'; +import {Ipv4ValidatorDirective} from './ipv4-validator.directive'; +import {Ipv6ValidatorDirective} from './ipv6-validator.directive'; +import {MaxValidatorDirective} from './max-validator.directive'; +import {MinValidatorDirective} from './min-validator.directive'; +import {PlxTextInputComponent} from './text-input.component'; +import {PlxValidateOnBlurDirective} from './validate-on-blur.directive'; +import {PlxTextInputIpComponent} from './text-input-ip.component'; +import {PlxTextInputIpAddressComponent} from './text-input-ip-address.component'; + + +@NgModule({ + imports: [CommonModule, FormsModule, PlxTooltipModule], + declarations: [ + PlxTextInputComponent, Ipv4ValidatorDirective, Ipv6ValidatorDirective, + MaxValidatorDirective, MinValidatorDirective, PlxValidateOnBlurDirective, + PlxTextInputIpComponent, PlxTextInputIpAddressComponent + ], + exports: [ + PlxTextInputComponent, Ipv4ValidatorDirective, Ipv6ValidatorDirective, + MaxValidatorDirective, MinValidatorDirective, PlxValidateOnBlurDirective, + PlxTextInputIpComponent, PlxTextInputIpAddressComponent + ] +}) + +export class PlxTextInputModule { +} diff --git a/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/validate-on-blur.directive.ts b/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/validate-on-blur.directive.ts new file mode 100644 index 00000000..b4a940c8 --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/plx-text-input/validate-on-blur.directive.ts @@ -0,0 +1,18 @@ +import {Directive, HostListener} from '@angular/core'; +import {NgControl} from '@angular/forms'; + +@Directive({selector: '[validateOnBlur]'}) + +export class PlxValidateOnBlurDirective { + constructor(private formControl: NgControl) {} + + @HostListener('focus') + onFocus() { + // this.formControl.control.markAsUntouched(false); + } + + @HostListener('blur') + onBlur() { + // this.formControl.control.markAsTouched(true); + } +} |