From 4808b7f98b34824e2cc626032ef8a1c5a1717352 Mon Sep 17 00:00:00 2001 From: Aaron Steinfeld Date: Mon, 1 Nov 2021 17:48:13 -0400 Subject: [PATCH] fix: incorrect focus on editing filter chips --- .../src/combo-box/combo-box.component.ts | 8 ++++++-- .../filter-bar/filter-bar.component.ts | 17 ++++++++++------- .../filter-chip/filter-chip.component.ts | 19 ++++++++++++++++++- 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/projects/components/src/combo-box/combo-box.component.ts b/projects/components/src/combo-box/combo-box.component.ts index 39885afb5..77d05fa38 100644 --- a/projects/components/src/combo-box/combo-box.component.ts +++ b/projects/components/src/combo-box/combo-box.component.ts @@ -255,7 +255,7 @@ export class ComboBoxComponent implements AfterViewInit, OnChan public onOptionClick(option: ComboBoxOption): void { this.setText(option.text); this.hidePopover(); - this.input.nativeElement.focus(); + this.focus(); this.selection.emit(this.buildResult()); } @@ -325,7 +325,7 @@ export class ComboBoxComponent implements AfterViewInit, OnChan * This might just be a mac or browser specific thing, but when an input box gets tabbed into it doesn't * get focus, but the entire text content is selected. Let's use that to give ourselves focus as well. */ - this.input.nativeElement.focus(); + this.focus(); } public onEscape(): void { @@ -369,6 +369,10 @@ export class ComboBoxComponent implements AfterViewInit, OnChan return this.createOption !== undefined && !isNil(this.text); } + public focus(): void { + this.input.nativeElement.focus(); + } + private buildResult(): ComboBoxResult { return { text: this.text, option: this.findOption(this.text) }; } diff --git a/projects/components/src/filtering/filter-bar/filter-bar.component.ts b/projects/components/src/filtering/filter-bar/filter-bar.component.ts index a5d0d51f7..982b5160a 100644 --- a/projects/components/src/filtering/filter-bar/filter-bar.component.ts +++ b/projects/components/src/filtering/filter-bar/filter-bar.component.ts @@ -2,7 +2,6 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, - ElementRef, EventEmitter, Input, OnChanges, @@ -13,13 +12,15 @@ import { } from '@angular/core'; import { IconType } from '@hypertrace/assets-library'; import { TypedSimpleChanges } from '@hypertrace/common'; +import { isEqual } from 'lodash-es'; import { BehaviorSubject, Observable, Subscription } from 'rxjs'; -import { mergeMap } from 'rxjs/operators'; +import { distinctUntilChanged, switchMap } from 'rxjs/operators'; import { IconSize } from '../../icon/icon-size'; import { Filter } from '../filter/filter'; import { FilterAttribute } from '../filter/filter-attribute'; import { FilterUrlService } from '../filter/filter-url.service'; import { FilterBarService } from './filter-bar.service'; +import { FilterChipComponent } from './filter-chip/filter-chip.component'; @Component({ selector: 'ht-filter-bar', @@ -83,14 +84,16 @@ export class FilterBarComponent implements OnChanges, OnInit, OnDestroy { @Output() public readonly filtersChange: EventEmitter = new EventEmitter(); - @ViewChild('filterInput', { read: ElementRef }) - public readonly filterInput!: ElementRef; + @ViewChild('filterInput') + public readonly filterInput?: FilterChipComponent; public isFocused: boolean = false; private readonly attributeSubject$: BehaviorSubject = new BehaviorSubject([]); private readonly internalFiltersSubject$: BehaviorSubject = new BehaviorSubject([]); - public readonly internalFilters$: Observable = this.internalFiltersSubject$.asObservable(); + public readonly internalFilters$: Observable = this.internalFiltersSubject$ + .asObservable() + .pipe(distinctUntilChanged(isEqual)); private subscription?: Subscription; @@ -123,7 +126,7 @@ export class FilterBarComponent implements OnChanges, OnInit, OnDestroy { private subscribeToUrlFilterChanges(): void { this.subscription = this.attributeSubject$ - .pipe(mergeMap(attributes => this.filterUrlService.getUrlFiltersChanges$(attributes))) + .pipe(switchMap(attributes => this.filterUrlService.getUrlFiltersChanges$(attributes))) .subscribe(filters => this.onFiltersChanged(filters, true, false)); } @@ -168,7 +171,7 @@ export class FilterBarComponent implements OnChanges, OnInit, OnDestroy { } private resetFocus(): void { - this.filterInput?.nativeElement.focus(); + this.filterInput?.focus(); } private updateFilter(oldFilter: Filter, newFilter: Filter): void { diff --git a/projects/components/src/filtering/filter-bar/filter-chip/filter-chip.component.ts b/projects/components/src/filtering/filter-bar/filter-chip/filter-chip.component.ts index 99a41028f..2937fdc3b 100644 --- a/projects/components/src/filtering/filter-bar/filter-chip/filter-chip.component.ts +++ b/projects/components/src/filtering/filter-bar/filter-chip/filter-chip.component.ts @@ -1,6 +1,16 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + OnChanges, + OnInit, + Output, + ViewChild +} from '@angular/core'; import { TypedSimpleChanges } from '@hypertrace/common'; import { ComboBoxMode, ComboBoxOption, ComboBoxResult } from '../../../combo-box/combo-box-api'; +import { ComboBoxComponent } from '../../../combo-box/combo-box.component'; import { Filter, IncompleteFilter } from '../../filter/filter'; import { FilterAttribute } from '../../filter/filter-attribute'; import { FilterChipService } from './filter-chip.service'; @@ -43,6 +53,9 @@ export class FilterChipComponent implements OnInit, OnChanges { @Output() public readonly clear: EventEmitter = new EventEmitter(); + @ViewChild(ComboBoxComponent) + public readonly comboBox?: ComboBoxComponent; + public text?: string; public options?: ComboBoxOption[]; @@ -86,6 +99,10 @@ export class FilterChipComponent implements OnInit, OnChanges { this.clear.emit(); } + public focus(): void { + this.comboBox?.focus(); + } + private isValidFilter(result: ComboBoxResult): result is ComboBoxResult { return result.option?.value !== undefined && result.option?.value.value !== undefined; }