From 78b8e921482793c35f04e1501397b4ea74f273bd Mon Sep 17 00:00:00 2001 From: Alex Inkin Date: Tue, 31 Jan 2023 20:59:26 +0800 Subject: [PATCH] fix(core): `DropdownHover` properly reflect state for `HostedDropdown` (#3507) --- .../hosted-dropdown.component.ts | 39 ++++++++++--------- .../dropdown/dropdown-hover.directive.ts | 8 +++- .../directives/dropdown/dropdown.component.ts | 7 +++- .../dropdown-hover/examples/2/index.html | 1 + 4 files changed, 34 insertions(+), 21 deletions(-) diff --git a/projects/core/components/hosted-dropdown/hosted-dropdown.component.ts b/projects/core/components/hosted-dropdown/hosted-dropdown.component.ts index 20d10a322bf4..0303e19f4aa9 100644 --- a/projects/core/components/hosted-dropdown/hosted-dropdown.component.ts +++ b/projects/core/components/hosted-dropdown/hosted-dropdown.component.ts @@ -32,8 +32,8 @@ import { } from '@taiga-ui/core/directives/dropdown'; import {tuiIsEditingKey} from '@taiga-ui/core/utils/miscellaneous'; import {PolymorpheusContent} from '@tinkoff/ng-polymorpheus'; -import {BehaviorSubject, combineLatest, EMPTY, Observable} from 'rxjs'; -import {map, startWith} from 'rxjs/operators'; +import {BehaviorSubject, EMPTY, merge} from 'rxjs'; +import {distinctUntilChanged, skip} from 'rxjs/operators'; import {TuiHostedDropdownConnectorDirective} from './hosted-dropdown-connector.directive'; @@ -42,6 +42,7 @@ export interface TuiHostedDropdownContext close(): void; } +/* eslint-disable @typescript-eslint/member-ordering */ @Component({ selector: 'tui-hosted-dropdown', templateUrl: './hosted-dropdown.template.html', @@ -59,7 +60,8 @@ export class TuiHostedDropdownComponent implements TuiFocusableElementAccessor { @ViewChild(TuiDropdownDirective) private readonly dropdownDirective?: TuiDropdownDirective; - private readonly manual$ = new BehaviorSubject(false); + /** TODO: rename in 4.0 */ + readonly openChange = new BehaviorSubject(false); @ViewChild(TuiActiveZoneDirective) readonly activeZone!: TuiActiveZoneDirective; @@ -76,34 +78,32 @@ export class TuiHostedDropdownComponent implements TuiFocusableElementAccessor { @tuiDefaultProp() canOpen = true; - @Output() - readonly openChange = new EventEmitter(); + @Output('openChange') + readonly open$ = merge(this.openChange, this.hover$ || EMPTY).pipe( + skip(1), + distinctUntilChanged(), + ); @Output() readonly focusedChange = new EventEmitter(); readonly context!: TuiContextWithImplicit; - readonly open$ = combineLatest([ - this.manual$, - (this.hover$ || EMPTY).pipe(startWith(false)), - ]).pipe(map(([manual, hover]) => manual || hover)); - constructor( @Optional() @Inject(TuiDropdownHoverDirective) - private readonly hover$: Observable | null, + private readonly hover$: TuiDropdownHoverDirective | null, @Inject(ElementRef) private readonly elementRef: ElementRef, ) {} @Input() @tuiDefaultProp() set open(open: boolean) { - this.manual$.next(open); + this.openChange.next(open); } get open(): boolean { - return this.manual$.value; + return this.openChange.value; } get host(): HTMLElement { @@ -150,7 +150,11 @@ export class TuiHostedDropdownComponent implements TuiFocusableElementAccessor { @HostListener('click', ['$event.target']) onClick(target: HTMLElement): void { - if (!this.hostEditable && this.computedHost.contains(target)) { + if ( + !this.hostEditable && + this.computedHost.contains(target) && + !this.hover$?.hovered + ) { this.updateOpen(!this.open); } } @@ -198,12 +202,9 @@ export class TuiHostedDropdownComponent implements TuiFocusableElementAccessor { } updateOpen(open: boolean): void { - if (open && !this.canOpen) { - return; + if (!open || this.canOpen) { + this.open = open; } - - this.open = open; - this.openChange.emit(open); } readonly close = (): void => this.updateOpen(false); diff --git a/projects/core/directives/dropdown/dropdown-hover.directive.ts b/projects/core/directives/dropdown/dropdown-hover.directive.ts index 10f3a48bf85c..5062b653be16 100644 --- a/projects/core/directives/dropdown/dropdown-hover.directive.ts +++ b/projects/core/directives/dropdown/dropdown-hover.directive.ts @@ -2,7 +2,7 @@ import {Directive, Inject, Input} from '@angular/core'; import {tuiDefaultProp, TuiHoveredService} from '@taiga-ui/cdk'; import {tuiAsDriver, TuiDriver} from '@taiga-ui/core/abstract'; import {merge, Observable, of, Subject} from 'rxjs'; -import {delay, switchMap} from 'rxjs/operators'; +import {delay, share, switchMap, tap} from 'rxjs/operators'; @Directive({ selector: '[tuiDropdownHover]:not(ng-container)', @@ -14,6 +14,10 @@ export class TuiDropdownHoverDirective extends TuiDriver { switchMap(visible => of(visible).pipe(delay(visible ? this.showDelay : this.hideDelay)), ), + tap(visible => { + this.hovered = visible; + }), + share(), ); @Input('tuiDropdownShowDelay') @@ -24,6 +28,8 @@ export class TuiDropdownHoverDirective extends TuiDriver { @tuiDefaultProp() hideDelay = 500; + hovered = false; + constructor( @Inject(TuiHoveredService) private readonly hovered$: Observable, ) { diff --git a/projects/core/directives/dropdown/dropdown.component.ts b/projects/core/directives/dropdown/dropdown.component.ts index b4142968c963..dfb8725a7070 100644 --- a/projects/core/directives/dropdown/dropdown.component.ts +++ b/projects/core/directives/dropdown/dropdown.component.ts @@ -5,6 +5,7 @@ import { ElementRef, HostBinding, Inject, + OnDestroy, Optional, Self, } from '@angular/core'; @@ -42,7 +43,7 @@ import {TUI_DROPDOWN_OPTIONS, TuiDropdownOptions} from './dropdown-options.direc providers: [TuiDestroyService, TuiPositionService], animations: [tuiDropdownAnimation], }) -export class TuiDropdownComponent { +export class TuiDropdownComponent implements OnDestroy { @HostBinding('@tuiDropdownAnimation') readonly dropdownAnimation = { value: TuiDropdownAnimation.FadeInTop, @@ -70,6 +71,10 @@ export class TuiDropdownComponent { }); } + ngOnDestroy(): void { + this.onHoveredChange(false); + } + onHoveredChange(hovered: boolean): void { if (this.hoverDirective) { this.hoverDirective.toggle(hovered); diff --git a/projects/demo/src/modules/directives/dropdown-hover/examples/2/index.html b/projects/demo/src/modules/directives/dropdown-hover/examples/2/index.html index 4a4d74f01f78..6d37313cd3ed 100644 --- a/projects/demo/src/modules/directives/dropdown-hover/examples/2/index.html +++ b/projects/demo/src/modules/directives/dropdown-hover/examples/2/index.html @@ -52,3 +52,4 @@ +

Current state: {{ open ? 'open' : 'closed' }}