diff --git a/packages/components/navbar-ic/navbar-ic-header.scss b/packages/components/navbar-ic/navbar-ic-header.scss
index 9ce4e01d53..628a3996f5 100644
--- a/packages/components/navbar-ic/navbar-ic-header.scss
+++ b/packages/components/navbar-ic/navbar-ic-header.scss
@@ -42,10 +42,6 @@
&.kbq-navbar-ic-header_interactive {
cursor: pointer;
}
-
- &.kbq-collapsed .kbq-navbar-ic-title {
- display: none;
- }
}
a.kbq-navbar-ic-header {
diff --git a/packages/components/navbar-ic/navbar-ic-header.ts b/packages/components/navbar-ic/navbar-ic-header.ts
index a979bde015..44d38847bf 100644
--- a/packages/components/navbar-ic/navbar-ic-header.ts
+++ b/packages/components/navbar-ic/navbar-ic-header.ts
@@ -1,6 +1,7 @@
import {
AfterContentInit,
ChangeDetectionStrategy,
+ ChangeDetectorRef,
Component,
ContentChild,
DestroyRef,
@@ -10,8 +11,10 @@ import {
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { kbqInjectNativeElement, PopUpPlacements, PopUpTriggers } from '@koobiq/components/core';
import { KbqTooltipTrigger } from '@koobiq/components/tooltip';
+import { distinctUntilChanged } from 'rxjs/operators';
import { KbqNavbarIc } from './navbar-ic';
import { KbqNavbarIcLogo, KbqNavbarIcTitle } from './navbar-ic-item';
+import { toggleNavbarIcItemAnimation } from './navbar-ic.animation';
@Component({
standalone: true,
@@ -19,6 +22,11 @@ import { KbqNavbarIcLogo, KbqNavbarIcTitle } from './navbar-ic-item';
exportAs: 'kbqNavbarIcHeader',
template: `
`,
@@ -28,12 +36,14 @@ import { KbqNavbarIcLogo, KbqNavbarIcTitle } from './navbar-ic-item';
'[class.kbq-navbar-ic-header_interactive]': 'isLink'
},
changeDetection: ChangeDetectionStrategy.OnPush,
- encapsulation: ViewEncapsulation.None
+ encapsulation: ViewEncapsulation.None,
+ animations: [toggleNavbarIcItemAnimation()]
})
export class KbqNavbarIcHeader extends KbqTooltipTrigger implements AfterContentInit {
protected readonly navbar = inject(KbqNavbarIc);
protected readonly nativeElement = kbqInjectNativeElement();
protected readonly destroyRef = inject(DestroyRef);
+ protected readonly changeDetectorRef = inject(ChangeDetectorRef);
@ContentChild(KbqNavbarIcLogo) logo: KbqNavbarIcLogo;
@ContentChild(KbqNavbarIcTitle) title: KbqNavbarIcTitle;
@@ -61,6 +71,10 @@ export class KbqNavbarIcHeader extends KbqTooltipTrigger implements AfterContent
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => this.title?.checkTextOverflown());
+ this.navbar.state.pipe(distinctUntilChanged(), takeUntilDestroyed(this.destroyRef)).subscribe(() => {
+ this.changeDetectorRef.detectChanges();
+ });
+
this.title?.checkTextOverflown();
if (this.title?.isTextOverflown) {
diff --git a/packages/components/navbar-ic/navbar-ic-item.html b/packages/components/navbar-ic/navbar-ic-item.html
index 8120e26d10..96a858ce25 100644
--- a/packages/components/navbar-ic/navbar-ic-item.html
+++ b/packages/components/navbar-ic/navbar-ic-item.html
@@ -2,7 +2,7 @@
+
@if (showDropDownAngle) {
diff --git a/packages/components/navbar-ic/navbar-ic-item.scss b/packages/components/navbar-ic/navbar-ic-item.scss
index 9ac71e42cd..4c6302be93 100644
--- a/packages/components/navbar-ic/navbar-ic-item.scss
+++ b/packages/components/navbar-ic/navbar-ic-item.scss
@@ -23,10 +23,6 @@
top: 0;
right: 0;
}
-
- & .kbq-navbar-ic-item__title-container {
- display: none;
- }
}
& > .kbq-icon {
diff --git a/packages/components/navbar-ic/navbar-ic-item.ts b/packages/components/navbar-ic/navbar-ic-item.ts
index 44b3910bba..33f152eaa6 100644
--- a/packages/components/navbar-ic/navbar-ic-item.ts
+++ b/packages/components/navbar-ic/navbar-ic-item.ts
@@ -34,6 +34,7 @@ import { KbqTooltipTrigger, TooltipModifier } from '@koobiq/components/tooltip';
import { Subject } from 'rxjs';
import { take } from 'rxjs/operators';
import { KbqNavbarIc } from './navbar-ic';
+import { toggleNavbarIcItemAnimation } from './navbar-ic.animation';
/**
* The maximum number of characters that can be placed in a title without being wrapped.
@@ -261,10 +262,12 @@ export class KbqNavbarIcFocusableItem implements AfterContentInit, AfterViewInit
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
hostDirectives: [KbqRectangleItem],
- imports: [KbqIcon]
+ imports: [KbqIcon],
+ animations: [toggleNavbarIcItemAnimation()]
})
export class KbqNavbarIcItem extends KbqTooltipTrigger implements AfterContentInit {
readonly rectangleElement = inject(KbqRectangleItem);
+ readonly navbar = inject(KbqNavbarIc);
readonly navbarFocusableItem = inject(KbqNavbarIcFocusableItem);
private changeDetectorRef = inject(ChangeDetectorRef);
@@ -310,11 +313,7 @@ export class KbqNavbarIcItem extends KbqTooltipTrigger implements AfterContentIn
}
get disabled(): boolean {
- if (this._disabled !== undefined) {
- return this._disabled;
- }
-
- return !this.collapsed && !this.hasCroppedText;
+ return this.collapsed || !this.hasCroppedText || this._disabled;
}
set disabled(value) {
@@ -341,7 +340,7 @@ export class KbqNavbarIcItem extends KbqTooltipTrigger implements AfterContentIn
}
this.rectangleElement.state.subscribe(() => {
- this.collapsed = this.rectangleElement.collapsed;
+ this.disabled = this.collapsed = this.rectangleElement.collapsed;
this.changeDetectorRef.markForCheck();
});
diff --git a/packages/components/navbar-ic/navbar-ic.animation.ts b/packages/components/navbar-ic/navbar-ic.animation.ts
index 570410e003..84076e2178 100644
--- a/packages/components/navbar-ic/navbar-ic.animation.ts
+++ b/packages/components/navbar-ic/navbar-ic.animation.ts
@@ -4,5 +4,13 @@ export function toggleNavbarIcAnimation(): AnimationTriggerMetadata {
return trigger('toggle', [
state('0', style({ width: '64px' })),
state('1', style({ width: '240px' })),
- transition('0 <=> 1', animate('150ms ease-out'))]);
+ transition('0 <=> 1', animate('200ms cubic-bezier(0.4, 0, 0.2, 1)'))]);
+}
+
+export function toggleNavbarIcItemAnimation(): AnimationTriggerMetadata {
+ return trigger('toggle', [
+ state('expanded', style({ opacity: 1, marginLeft: 0 })),
+ state('collapsed', style({ opacity: 0, marginLeft: '-8px' })),
+ transition('collapsed => expanded', animate('200ms 150ms cubic-bezier(0.4, 0, 0.2, 1)')),
+ transition('expanded => collapsed', animate('200ms cubic-bezier(0.4, 0, 0.2, 1)'))]);
}
diff --git a/packages/components/navbar-ic/navbar-ic.ts b/packages/components/navbar-ic/navbar-ic.ts
index cb1cb68398..bdd23e960f 100644
--- a/packages/components/navbar-ic/navbar-ic.ts
+++ b/packages/components/navbar-ic/navbar-ic.ts
@@ -28,8 +28,8 @@ import { KBQ_LOCALE_SERVICE, KbqRectangleItem, ruRULocaleData } from '@koobiq/co
import { KbqDropdownTrigger } from '@koobiq/components/dropdown';
import { KbqNotificationCenterTrigger } from '@koobiq/components/notification-center';
import { KbqPopoverTrigger } from '@koobiq/components/popover';
-import { BehaviorSubject, combineLatest, merge, Observable, Subject, Subscription } from 'rxjs';
-import { startWith } from 'rxjs/operators';
+import { BehaviorSubject, EMPTY, filter, merge, Observable, Subject, Subscription, timer } from 'rxjs';
+import { delay, startWith, switchMap, takeUntil, tap } from 'rxjs/operators';
import {
KbqNavbarFocusableItemEvent,
KbqNavbarIcFocusableItem,
@@ -43,6 +43,12 @@ export enum KbqExpandEvents {
hoverOrFocus
}
+/** minimum timeout to use KBQ_ENTER_DELAY */
+export const KBQ_MIN_TIMEOUT_FOR_ENTER_DELAY = 2000;
+
+/** delay before open navbar-ic */
+export const KBQ_ENTER_DELAY = 400;
+
/** default configuration of navbar-ic */
/** @docs-private */
export const KBQ_NAVBAR_IC_DEFAULT_CONFIGURATION = ruRULocaleData.navbarIc;
@@ -218,6 +224,8 @@ export class KbqNavbarIc extends KbqFocusable implements AfterContentInit {
readonly externalConfiguration = inject(KBQ_NAVBAR_IC_CONFIGURATION, { optional: true });
+ readonly state = new BehaviorSubject<'expanded' | 'collapsed' | null>(null);
+
configuration;
/** @docs-private */
@@ -276,6 +284,8 @@ export class KbqNavbarIc extends KbqFocusable implements AfterContentInit {
set expanded(value: boolean) {
this._expanded = value;
+ this.state.next(value || this.pinned || this.hasOpenedPopUp ? 'expanded' : 'collapsed');
+
this.updateExpandedStateForItems();
}
@@ -299,24 +309,55 @@ export class KbqNavbarIc extends KbqFocusable implements AfterContentInit {
return this.expanded ? this.expandedWidth : this.collapsedWidth;
}
+ private lastOpeningTime: number;
+
constructor() {
super();
- this.animationDone.pipe(takeUntilDestroyed()).subscribe(this.updateTooltipForItems);
-
effect(this.updateExpandedStateForItems);
- combineLatest([this.hovered, this.focused])
- .pipe(takeUntilDestroyed())
- .subscribe(([hovered, focused]) => {
- if (this.pinned) return;
+ this.focused
+ .pipe(
+ delay(0),
+ filter(() => !this.pinned || !this.hasOpenedPopUp),
+ takeUntilDestroyed()
+ )
+ .subscribe((focused) => {
+ if (this.hovered.value) return;
this.expandEvent = KbqExpandEvents.hoverOrFocus;
- this.expanded = hovered || focused;
+ this.expanded = focused;
this.changeDetectorRef.markForCheck();
});
+ this.hovered
+ .pipe(
+ filter(() => !this.pinned),
+ switchMap((hovered) => {
+ if (!hovered) {
+ if (this.expanded) {
+ this.lastOpeningTime = Date.now();
+ }
+
+ this.expanded = false;
+
+ return EMPTY;
+ }
+
+ return timer(this.getExpandDelay()).pipe(
+ tap(() => {
+ this.expandEvent = KbqExpandEvents.hoverOrFocus;
+ this.expanded = true;
+ this.changeDetectorRef.markForCheck();
+ }),
+ takeUntil(this.hovered.pipe(filter((h) => !h)))
+ );
+ }),
+ takeUntilDestroyed()
+ )
+ .subscribe();
+
this.focusMonitor.monitor(this.elementRef, true).subscribe((focusOrigin) => {
this.focused.next(focusOrigin === 'keyboard');
});
@@ -366,6 +407,10 @@ export class KbqNavbarIc extends KbqFocusable implements AfterContentInit {
}
}
+ private getExpandDelay() {
+ return Date.now() - this.lastOpeningTime < KBQ_MIN_TIMEOUT_FOR_ENTER_DELAY ? 0 : KBQ_ENTER_DELAY;
+ }
+
protected updateTooltipForItems = () => this.items().forEach((item) => item.updateTooltip());
protected updateExpandedStateForItems = () => this.rectangleElements().forEach(this.updateItemExpandedState);
diff --git a/tools/public_api_guard/components/navbar-ic.api.md b/tools/public_api_guard/components/navbar-ic.api.md
index 251dd061de..bf2c4245e7 100644
--- a/tools/public_api_guard/components/navbar-ic.api.md
+++ b/tools/public_api_guard/components/navbar-ic.api.md
@@ -36,6 +36,12 @@ import { Signal } from '@angular/core';
import { Subject } from 'rxjs';
import { TooltipModifier } from '@koobiq/components/tooltip';
+// @public
+export const KBQ_ENTER_DELAY = 400;
+
+// @public
+export const KBQ_MIN_TIMEOUT_FOR_ENTER_DELAY = 2000;
+
// @public
export const KBQ_NAVBAR_IC_CONFIGURATION: InjectionToken;
@@ -162,6 +168,8 @@ export class KbqNavbarIc extends KbqFocusable implements AfterContentInit {
popoverTrigger: Signal;
rectangleElements: Signal;
// (undocumented)
+ readonly state: BehaviorSubject<"expanded" | "collapsed" | null>;
+ // (undocumented)
toggle(): void;
toggleElement: Signal;
// (undocumented)
@@ -242,6 +250,8 @@ export class KbqNavbarIcFocusableItem implements AfterContentInit, AfterViewInit
export class KbqNavbarIcHeader extends KbqTooltipTrigger implements AfterContentInit {
constructor();
// (undocumented)
+ protected readonly changeDetectorRef: ChangeDetectorRef;
+ // (undocumented)
protected readonly destroyRef: DestroyRef;
// (undocumented)
get isLink(): boolean;
@@ -258,7 +268,7 @@ export class KbqNavbarIcHeader extends KbqTooltipTrigger implements AfterContent
// (undocumented)
get titleText(): string;
// (undocumented)
- static ɵcmp: i0.ɵɵComponentDeclaration;
+ static ɵcmp: i0.ɵɵComponentDeclaration;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration;
}
@@ -280,6 +290,8 @@ export class KbqNavbarIcItem extends KbqTooltipTrigger implements AfterContentIn
get hasDropDownTrigger(): boolean;
icon: KbqIcon;
// (undocumented)
+ readonly navbar: KbqNavbarIc;
+ // (undocumented)
readonly navbarFocusableItem: KbqNavbarIcFocusableItem;
// (undocumented)
ngAfterContentInit(): void;