Click outside optimized and use at input and search-input
This commit is contained in:
parent
901078ea42
commit
e6b2f41c8c
|
@ -17,6 +17,7 @@ import {HelperFunctions} from "../../utils/HelperFunctions.class";
|
||||||
import {Subscription} from "rxjs";
|
import {Subscription} from "rxjs";
|
||||||
import {EnvProperties} from "../../utils/properties/env-properties";
|
import {EnvProperties} from "../../utils/properties/env-properties";
|
||||||
import {properties} from "../../../../environments/environment";
|
import {properties} from "../../../../environments/environment";
|
||||||
|
import {ClickEvent} from "../../utils/click/click-outside-or-esc.directive";
|
||||||
|
|
||||||
export type InputType = 'text' | 'URL' | 'logoURL' | 'autocomplete' | 'autocomplete_soft' | 'textarea' | 'select' | 'chips';
|
export type InputType = 'text' | 'URL' | 'logoURL' | 'autocomplete' | 'autocomplete_soft' | 'textarea' | 'select' | 'chips';
|
||||||
|
|
||||||
|
@ -49,8 +50,8 @@ declare var UIkit;
|
||||||
[class.focused]="focused" [ngClass]="inputClass" [class.hint]="hint"
|
[class.focused]="focused" [ngClass]="inputClass" [class.hint]="hint"
|
||||||
[class.active]="(formAsControl?.value || formAsArray?.length > 0 || getLabel(formAsControl?.value)) && !focused"
|
[class.active]="(formAsControl?.value || formAsArray?.length > 0 || getLabel(formAsControl?.value)) && !focused"
|
||||||
[class.danger]="(formControl.invalid && (formControl.touched || searchControl?.touched)) || (searchControl?.invalid && searchControl?.touched)">
|
[class.danger]="(formControl.invalid && (formControl.touched || searchControl?.touched)) || (searchControl?.invalid && searchControl?.touched)">
|
||||||
<div #inputBox class="input-box" [class.select]="type === 'select'"
|
<div #inputBox class="input-box" [class.select]="type === 'select'" click-outside-or-esc
|
||||||
[class.static]="placeholderInfo?.static">
|
[class.static]="placeholderInfo?.static" (clickOutside)="click($event)">
|
||||||
<div *ngIf="!placeholderInfo?.static && placeholderInfo.label" class="placeholder">
|
<div *ngIf="!placeholderInfo?.static && placeholderInfo.label" class="placeholder">
|
||||||
<label>{{placeholderInfo.label}} <sup *ngIf="required">*</sup></label>
|
<label>{{placeholderInfo.label}} <sup *ngIf="required">*</sup></label>
|
||||||
</div>
|
</div>
|
||||||
|
@ -267,11 +268,8 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostListener('document:click', ['$event'])
|
click(event: ClickEvent) {
|
||||||
click(event) {
|
this.focus(!event.clicked, event);
|
||||||
if(event.isTrusted) {
|
|
||||||
this.focus(this.inputBox && this.inputBox.nativeElement.contains(event.target), event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
@ -401,11 +399,7 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
stopPropagation() {
|
|
||||||
event.stopPropagation();
|
|
||||||
}
|
|
||||||
|
|
||||||
remove(index: number, event) {
|
remove(index: number, event) {
|
||||||
this.formAsArray.removeAt(index);
|
this.formAsArray.removeAt(index);
|
||||||
this.formAsArray.markAsDirty();
|
this.formAsArray.markAsDirty();
|
||||||
|
@ -439,7 +433,6 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
||||||
}
|
}
|
||||||
this.formAsArray.push(new FormControl(this.searchControl.value, this.validators));
|
this.formAsArray.push(new FormControl(this.searchControl.value, this.validators));
|
||||||
this.formAsArray.markAsDirty();
|
this.formAsArray.markAsDirty();
|
||||||
this.focus(true);
|
|
||||||
}
|
}
|
||||||
this.searchControl.setValue('');
|
this.searchControl.setValue('');
|
||||||
}
|
}
|
||||||
|
@ -470,6 +463,13 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.open(false);
|
this.open(false);
|
||||||
|
if (this.input) {
|
||||||
|
this.input.nativeElement.blur();
|
||||||
|
} else if (this.textArea) {
|
||||||
|
this.textArea.nativeElement.blur();
|
||||||
|
} else if (this.searchInput) {
|
||||||
|
this.searchInput.nativeElement.blur();
|
||||||
|
}
|
||||||
if (this.searchControl) {
|
if (this.searchControl) {
|
||||||
this.add(event);
|
this.add(event);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,14 @@ import {InputComponent} from "./input.component";
|
||||||
import {SharedModule} from "../../shared/shared.module";
|
import {SharedModule} from "../../shared/shared.module";
|
||||||
import {IconsModule} from "../../utils/icons/icons.module";
|
import {IconsModule} from "../../utils/icons/icons.module";
|
||||||
import {SafeHtmlPipeModule} from "../../utils/pipes/safeHTMLPipe.module";
|
import {SafeHtmlPipeModule} from "../../utils/pipes/safeHTMLPipe.module";
|
||||||
|
import {ClickModule} from "../../utils/click/click.module";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
SharedModule,
|
SharedModule,
|
||||||
IconsModule,
|
IconsModule,
|
||||||
SafeHtmlPipeModule
|
SafeHtmlPipeModule,
|
||||||
|
ClickModule
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
InputComponent
|
InputComponent
|
||||||
|
|
|
@ -12,16 +12,17 @@ import {
|
||||||
import {AbstractControl} from '@angular/forms';
|
import {AbstractControl} from '@angular/forms';
|
||||||
import {MatAutocompleteTrigger} from '@angular/material/autocomplete';
|
import {MatAutocompleteTrigger} from '@angular/material/autocomplete';
|
||||||
import {InputComponent} from "../input/input.component";
|
import {InputComponent} from "../input/input.component";
|
||||||
|
import {ClickEvent} from "../../utils/click/click-outside-or-esc.directive";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: '[search-input]',
|
selector: '[search-input]',
|
||||||
template: `
|
template: `
|
||||||
<div class="uk-flex uk-flex-right uk-width-1-1">
|
<div class="uk-flex uk-flex-right uk-width-1-1">
|
||||||
<div #searchInput class="search-input" [class.collapsed]="hidden" [ngClass]="searchInputClass">
|
<div #searchInput click-outside-or-esc (clickOutside)="click($event)" class="search-input" [class.collapsed]="hidden" [ngClass]="searchInputClass">
|
||||||
<div class="uk-flex uk-flex-middle">
|
<div class="uk-flex uk-flex-middle">
|
||||||
<div class="uk-width-expand">
|
<div class="uk-width-expand">
|
||||||
<div #input [class.uk-hidden]="hidden" input [formInput]="searchControl" inputClass="search" [disabledIcon]="null"
|
<div #input [class.uk-hidden]="hidden" input [formInput]="searchControl" inputClass="search" [disabledIcon]="null"
|
||||||
[placeholder]="{label: placeholder, static: true}" [(value)]="value" (valueChange)="valueChange.emit($event)"
|
[placeholder]="{label: placeholder, static: true}" [value]="value" (valueChange)="valueChange.emit($event)"
|
||||||
[disabled]="disabled" [type]="(options.length > 0?'autocomplete_soft':'text')" [options]="options"></div>
|
[disabled]="disabled" [type]="(options.length > 0?'autocomplete_soft':'text')" [options]="options"></div>
|
||||||
</div>
|
</div>
|
||||||
<div [class.uk-hidden]="(!searchControl?.value && !value) || disabled" class="uk-width-auto">
|
<div [class.uk-hidden]="(!searchControl?.value && !value) || disabled" class="uk-width-auto">
|
||||||
|
@ -66,10 +67,9 @@ export class SearchInputComponent implements OnInit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostListener('document:click', ['$event'])
|
click(event: ClickEvent) {
|
||||||
click(event) {
|
if(this.expandable && !this.disabled) {
|
||||||
if(event.isTrusted && this.expandable && !this.disabled) {
|
this.expand(!event.clicked);
|
||||||
this.expand(this.searchInput && this.searchInput.nativeElement.contains(event.target));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,10 @@ import {SearchInputComponent} from './search-input.component';
|
||||||
import {MatAutocompleteModule} from '@angular/material/autocomplete';
|
import {MatAutocompleteModule} from '@angular/material/autocomplete';
|
||||||
import {IconsModule} from '../../utils/icons/icons.module';
|
import {IconsModule} from '../../utils/icons/icons.module';
|
||||||
import {InputModule} from "../input/input.module";
|
import {InputModule} from "../input/input.module";
|
||||||
|
import {ClickModule} from "../../utils/click/click.module";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [SharedModule, MatAutocompleteModule, IconsModule, InputModule],
|
imports: [SharedModule, MatAutocompleteModule, IconsModule, InputModule, ClickModule],
|
||||||
declarations: [SearchInputComponent],
|
declarations: [SearchInputComponent],
|
||||||
exports: [SearchInputComponent]
|
exports: [SearchInputComponent]
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,79 +1,35 @@
|
||||||
import {Directive, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core';
|
import {Directive, ElementRef, EventEmitter, HostListener, Output} from '@angular/core';
|
||||||
import {fromEvent, Subscriber} from 'rxjs';
|
|
||||||
import {delay, tap} from 'rxjs/operators';
|
export interface ClickEvent {
|
||||||
|
event: any,
|
||||||
|
clicked: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: '[click-outside-or-esc]'
|
selector: '[click-outside-or-esc]'
|
||||||
})
|
})
|
||||||
|
export class ClickOutsideOrEsc {
|
||||||
|
@Output('clickOutside') clickOutside: EventEmitter<ClickEvent> = new EventEmitter<ClickEvent>();
|
||||||
|
|
||||||
export class ClickOutsideOrEsc implements OnInit, OnDestroy {
|
constructor(private elementRef: ElementRef) {}
|
||||||
private listening: boolean;
|
|
||||||
private subscriptions: any[] = [];
|
@HostListener('document:click', ['$event'])
|
||||||
@Input()
|
click(event) {
|
||||||
public targetId = null;
|
if(event.isTrusted) {
|
||||||
@Input()
|
this.clickOutside.emit({
|
||||||
public escClose = true;
|
event: event,
|
||||||
@Input()
|
clicked: !(this.elementRef && this.elementRef.nativeElement.contains(event.target))
|
||||||
public clickClose = true;
|
|
||||||
@Output('clickOutside') clickOutside: EventEmitter<Object>;
|
|
||||||
|
|
||||||
constructor(private _elRef: ElementRef) {
|
|
||||||
this.listening = false;
|
|
||||||
this.clickOutside = new EventEmitter();
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
if(typeof document !== 'undefined') {
|
|
||||||
this.subscriptions.push(fromEvent(document, 'click').pipe(
|
|
||||||
delay(1),
|
|
||||||
tap(() => {
|
|
||||||
this.listening = true;
|
|
||||||
})).subscribe((event: MouseEvent) => {
|
|
||||||
this.onGlobalClick(event);
|
|
||||||
}));
|
|
||||||
this.subscriptions.push(fromEvent(document, 'click').pipe(
|
|
||||||
delay(1),
|
|
||||||
tap(() => {
|
|
||||||
this.listening = true;
|
|
||||||
})).subscribe((event: KeyboardEvent) => {
|
|
||||||
if (event.keyCode === 27 && this.escClose) {
|
|
||||||
this.clickOutside.emit({
|
|
||||||
target: (event.target || null),
|
|
||||||
value: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy() {
|
|
||||||
if (this.subscriptions) {
|
|
||||||
this.subscriptions.forEach((subscription: Subscriber<any>) => {
|
|
||||||
subscription.unsubscribe();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.subscriptions = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onGlobalClick(event: MouseEvent) {
|
@HostListener('window:keydown', ['$event'])
|
||||||
if (event instanceof MouseEvent && this.listening === true) {
|
keyEvent(event: KeyboardEvent) {
|
||||||
let element: HTMLElement = <HTMLElement>event.target;
|
if(event.code === 'Escape') {
|
||||||
while (element) {
|
this.clickOutside.emit({
|
||||||
if(element.id === this.targetId) {
|
event: event,
|
||||||
this.clickOutside.emit({
|
clicked: true
|
||||||
target: (event.target || null),
|
});
|
||||||
value: false
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
element = element.parentElement;
|
|
||||||
}
|
|
||||||
if(this.clickClose) {
|
|
||||||
this.clickOutside.emit({
|
|
||||||
target: (event.target || null),
|
|
||||||
value: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue