Click outside optimized and use at input and search-input

This commit is contained in:
Konstantinos Triantafyllou 2022-04-13 13:00:40 +03:00
parent 901078ea42
commit e6b2f41c8c
5 changed files with 48 additions and 89 deletions

View File

@ -17,6 +17,7 @@ import {HelperFunctions} from "../../utils/HelperFunctions.class";
import {Subscription} from "rxjs";
import {EnvProperties} from "../../utils/properties/env-properties";
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';
@ -49,8 +50,8 @@ declare var UIkit;
[class.focused]="focused" [ngClass]="inputClass" [class.hint]="hint"
[class.active]="(formAsControl?.value || formAsArray?.length > 0 || getLabel(formAsControl?.value)) && !focused"
[class.danger]="(formControl.invalid && (formControl.touched || searchControl?.touched)) || (searchControl?.invalid && searchControl?.touched)">
<div #inputBox class="input-box" [class.select]="type === 'select'"
[class.static]="placeholderInfo?.static">
<div #inputBox class="input-box" [class.select]="type === 'select'" click-outside-or-esc
[class.static]="placeholderInfo?.static" (clickOutside)="click($event)">
<div *ngIf="!placeholderInfo?.static && placeholderInfo.label" class="placeholder">
<label>{{placeholderInfo.label}} <sup *ngIf="required">*</sup></label>
</div>
@ -267,11 +268,8 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
}
}
@HostListener('document:click', ['$event'])
click(event) {
if(event.isTrusted) {
this.focus(this.inputBox && this.inputBox.nativeElement.contains(event.target), event);
}
click(event: ClickEvent) {
this.focus(!event.clicked, event);
}
ngOnInit() {
@ -401,11 +399,7 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
}
});
}
stopPropagation() {
event.stopPropagation();
}
remove(index: number, event) {
this.formAsArray.removeAt(index);
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.markAsDirty();
this.focus(true);
}
this.searchControl.setValue('');
}
@ -470,6 +463,13 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
}
} else {
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) {
this.add(event);
}

View File

@ -3,12 +3,14 @@ import {InputComponent} from "./input.component";
import {SharedModule} from "../../shared/shared.module";
import {IconsModule} from "../../utils/icons/icons.module";
import {SafeHtmlPipeModule} from "../../utils/pipes/safeHTMLPipe.module";
import {ClickModule} from "../../utils/click/click.module";
@NgModule({
imports: [
SharedModule,
IconsModule,
SafeHtmlPipeModule
SafeHtmlPipeModule,
ClickModule
],
exports: [
InputComponent

View File

@ -12,16 +12,17 @@ import {
import {AbstractControl} from '@angular/forms';
import {MatAutocompleteTrigger} from '@angular/material/autocomplete';
import {InputComponent} from "../input/input.component";
import {ClickEvent} from "../../utils/click/click-outside-or-esc.directive";
@Component({
selector: '[search-input]',
template: `
<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-width-expand">
<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>
</div>
<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) {
if(event.isTrusted && this.expandable && !this.disabled) {
this.expand(this.searchInput && this.searchInput.nativeElement.contains(event.target));
click(event: ClickEvent) {
if(this.expandable && !this.disabled) {
this.expand(!event.clicked);
}
}

View File

@ -4,9 +4,10 @@ import {SearchInputComponent} from './search-input.component';
import {MatAutocompleteModule} from '@angular/material/autocomplete';
import {IconsModule} from '../../utils/icons/icons.module';
import {InputModule} from "../input/input.module";
import {ClickModule} from "../../utils/click/click.module";
@NgModule({
imports: [SharedModule, MatAutocompleteModule, IconsModule, InputModule],
imports: [SharedModule, MatAutocompleteModule, IconsModule, InputModule, ClickModule],
declarations: [SearchInputComponent],
exports: [SearchInputComponent]
})

View File

@ -1,79 +1,35 @@
import {Directive, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core';
import {fromEvent, Subscriber} from 'rxjs';
import {delay, tap} from 'rxjs/operators';
import {Directive, ElementRef, EventEmitter, HostListener, Output} from '@angular/core';
export interface ClickEvent {
event: any,
clicked: boolean;
}
@Directive({
selector: '[click-outside-or-esc]'
})
export class ClickOutsideOrEsc {
@Output('clickOutside') clickOutside: EventEmitter<ClickEvent> = new EventEmitter<ClickEvent>();
export class ClickOutsideOrEsc implements OnInit, OnDestroy {
private listening: boolean;
private subscriptions: any[] = [];
@Input()
public targetId = null;
@Input()
public escClose = true;
@Input()
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();
constructor(private elementRef: ElementRef) {}
@HostListener('document:click', ['$event'])
click(event) {
if(event.isTrusted) {
this.clickOutside.emit({
event: event,
clicked: !(this.elementRef && this.elementRef.nativeElement.contains(event.target))
});
}
this.subscriptions = [];
}
onGlobalClick(event: MouseEvent) {
if (event instanceof MouseEvent && this.listening === true) {
let element: HTMLElement = <HTMLElement>event.target;
while (element) {
if(element.id === this.targetId) {
this.clickOutside.emit({
target: (event.target || null),
value: false
});
return;
}
element = element.parentElement;
}
if(this.clickClose) {
this.clickOutside.emit({
target: (event.target || null),
value: true
});
}
@HostListener('window:keydown', ['$event'])
keyEvent(event: KeyboardEvent) {
if(event.code === 'Escape') {
this.clickOutside.emit({
event: event,
clicked: true
});
}
}
}