Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions packages/components/navbar-ic/navbar-ic-header.scss
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,6 @@
&.kbq-navbar-ic-header_interactive {
cursor: pointer;
}

&.kbq-collapsed .kbq-navbar-ic-title {
display: none;
}
}

a.kbq-navbar-ic-header {
Expand Down
16 changes: 15 additions & 1 deletion packages/components/navbar-ic/navbar-ic-header.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
AfterContentInit,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ContentChild,
DestroyRef,
Expand All @@ -10,15 +11,22 @@ 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,
selector: 'kbq-navbar-ic-header, [kbq-navbar-ic-header]',
exportAs: 'kbqNavbarIcHeader',
template: `
<div class="kbq-navbar-ic-header__inner">
<ng-content select="[kbqNavbarIcLogo]" />

<div [@toggle]="navbar.state.value">
<ng-content select="[kbqNavbarIcTitle]" />
</div>
<ng-content />
</div>
`,
Expand All @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion packages/components/navbar-ic/navbar-ic-item.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<ng-content select="[kbq-icon]" />

@if (title && !collapsed) {
<div class="kbq-navbar-ic-item__title-container">
<div class="kbq-navbar-ic-item__title-container" [@toggle]="navbar.state.value">
<ng-content select="[kbqNavbarIcTitle]" />

@if (showDropDownAngle) {
Expand Down
4 changes: 0 additions & 4 deletions packages/components/navbar-ic/navbar-ic-item.scss
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,6 @@
top: 0;
right: 0;
}

& .kbq-navbar-ic-item__title-container {
display: none;
}
}

& > .kbq-icon {
Expand Down
13 changes: 6 additions & 7 deletions packages/components/navbar-ic/navbar-ic-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand All @@ -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();
});
Expand Down
10 changes: 9 additions & 1 deletion packages/components/navbar-ic/navbar-ic.animation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)'))]);
}
61 changes: 52 additions & 9 deletions packages/components/navbar-ic/navbar-ic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, filter, merge, Observable, Subject, Subscription } from 'rxjs';
import { delay, startWith } from 'rxjs/operators';
import {
KbqNavbarFocusableItemEvent,
KbqNavbarIcFocusableItem,
Expand All @@ -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;
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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();
}

Expand All @@ -299,24 +309,57 @@ export class KbqNavbarIc extends KbqFocusable implements AfterContentInit {
return this.expanded ? this.expandedWidth : this.collapsedWidth;
}

private lastOpeningTime: number;
private hideTimeoutId: number | null;

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),
takeUntilDestroyed()
)
.subscribe((hovered) => {
if (hovered) {
const delay =
Date.now() - this.lastOpeningTime < KBQ_MIN_TIMEOUT_FOR_ENTER_DELAY ? 0 : KBQ_ENTER_DELAY;

this.hideTimeoutId = setTimeout(() => {
this.hideTimeoutId = null;

this.expandEvent = KbqExpandEvents.hoverOrFocus;
this.expanded = hovered;
this.changeDetectorRef.markForCheck();
}, delay) as unknown as number;
} else {
clearTimeout(this.hideTimeoutId!);

@artembelik artembelik Nov 1, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

необходимо очищать таймаут при дестрое компонента


if (this.expanded) {
this.lastOpeningTime = Date.now();
}

this.expanded = hovered;
}
});

this.focusMonitor.monitor(this.elementRef, true).subscribe((focusOrigin) => {
this.focused.next(focusOrigin === 'keyboard');
});
Expand Down
14 changes: 13 additions & 1 deletion tools/public_api_guard/components/navbar-ic.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<unknown>;

Expand Down Expand Up @@ -162,6 +168,8 @@ export class KbqNavbarIc extends KbqFocusable implements AfterContentInit {
popoverTrigger: Signal<readonly any[]>;
rectangleElements: Signal<readonly any[]>;
// (undocumented)
readonly state: BehaviorSubject<"expanded" | "collapsed" | null>;
// (undocumented)
toggle(): void;
toggleElement: Signal<readonly any[]>;
// (undocumented)
Expand Down Expand Up @@ -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;
Expand All @@ -258,7 +268,7 @@ export class KbqNavbarIcHeader extends KbqTooltipTrigger implements AfterContent
// (undocumented)
get titleText(): string;
// (undocumented)
static ɵcmp: i0.ɵɵComponentDeclaration<KbqNavbarIcHeader, "kbq-navbar-ic-header, [kbq-navbar-ic-header]", ["kbqNavbarIcHeader"], {}, {}, ["logo", "title"], ["*"], true, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<KbqNavbarIcHeader, "kbq-navbar-ic-header, [kbq-navbar-ic-header]", ["kbqNavbarIcHeader"], {}, {}, ["logo", "title"], ["[kbqNavbarIcLogo]", "[kbqNavbarIcTitle]", "*"], true, never>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<KbqNavbarIcHeader, never>;
}
Expand All @@ -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;
Expand Down