Skip to content

Commit

Permalink
NAS-131092 / 25.04 / Remove ngx-layout (#10648)
Browse files Browse the repository at this point in the history
  • Loading branch information
undsoft authored Sep 16, 2024
1 parent dca5277 commit 515e04a
Show file tree
Hide file tree
Showing 11 changed files with 115 additions and 86 deletions.
4 changes: 0 additions & 4 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -303,10 +303,6 @@ module.exports = {
"importNames": ["CommonModule"],
"message": "Import individual constituents instead."
},
{
"name": "@ngbracket/ngx-layout",
"message": "Do not use this package. Use native CSS instead."
}
],
"patterns": [{
"group": [ "../**"],
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@
"@lezer/lr": "~1.4.2",
"@material-design-icons/font": "~0.14.13",
"@mdi/font": "~7.4.47",
"@ngbracket/ngx-layout": "~17.0.1",
"@ngneat/reactive-forms": "~5.0.2",
"@ngneat/spectator": "~19.0.0",
"@ngneat/until-destroy": "~10.0.0",
Expand Down
21 changes: 7 additions & 14 deletions src/app/directives/details-height/details-height.directive.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import {
Directive, ElementRef, Inject, OnChanges, OnDestroy, OnInit,
Directive, ElementRef, HostListener, Inject, OnChanges, OnDestroy, OnInit,
} from '@angular/core';
// eslint-disable-next-line no-restricted-imports
import { MediaObserver } from '@ngbracket/ngx-layout';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { Store } from '@ngrx/store';
import { WINDOW } from 'app/helpers/window.helper';
Expand Down Expand Up @@ -39,12 +37,10 @@ export class IxDetailsHeightDirective implements OnInit, OnDestroy, OnChanges {
private element: ElementRef<HTMLElement>,
private layoutService: LayoutService,
private store$: Store<AppsState>,
private mediaObserver: MediaObserver,
) {}

ngOnInit(): void {
this.listenForConsoleFooterChanges();
this.listenForScreenSizeChanges();

this.element.nativeElement.style.height = this.heightCssValue;
this.window.addEventListener('scroll', this.onScrollHandler, true);
Expand All @@ -61,6 +57,12 @@ export class IxDetailsHeightDirective implements OnInit, OnDestroy, OnChanges {
this.window.removeEventListener('scroll', this.onScrollHandler, true);
}

@HostListener('window:resize')
listenForScreenSizeChanges(): void {
this.heightBaseOffset = this.getBaseOffset();
this.scrollBreakingPoint = this.getScrollBreakingPoint();
}

onScroll(): void {
const parentElement = this.layoutService.getContentContainer();

Expand Down Expand Up @@ -120,13 +122,4 @@ export class IxDetailsHeightDirective implements OnInit, OnDestroy, OnChanges {
this.hasConsoleFooter = advancedConfig.consolemsg;
});
}

private listenForScreenSizeChanges(): void {
this.mediaObserver.asObservable()
.pipe(untilDestroyed(this))
.subscribe(() => {
this.heightBaseOffset = this.getBaseOffset();
this.scrollBreakingPoint = this.getScrollBreakingPoint();
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
cdkTrapFocus
class="search-box-wrapper"
[class.sidenav-collapsed]="sidenavService.isMenuCollapsed"
[class.sidenav-mobile]="sidenavService.isMobile"
[class.sidenav-mobile]="sidenavService.isMobile()"
[cdkTrapFocusAutoCapture]="true"
>
<div class="search-box">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ describe('GlobalSearchComponent', () => {
closeAll: jest.fn(),
}),
mockProvider(SidenavService, {
isMobile: () => false,
closeSecondaryMenu: jest.fn(),
}),
mockProvider(DialogService, {
Expand Down
3 changes: 2 additions & 1 deletion src/app/modules/layout/topbar/ix-logo/ix-logo.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
ixTest="ixsystems"
>
<ix-icon
[class]="['ix-logo', screenSize$ | async, logoIcon$ | async]"
[class]="['ix-logo', logoIcon$ | async]"
[class.xs]="isXsScreen$ | async"
[name]="logoIcon$ | async"
></ix-icon>
</a>
11 changes: 5 additions & 6 deletions src/app/modules/layout/topbar/ix-logo/ix-logo.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// eslint-disable-next-line no-restricted-imports
import { MediaObserver } from '@ngbracket/ngx-layout';
import { BreakpointObserver } from '@angular/cdk/layout';
import { createComponentFactory, mockProvider, Spectator } from '@ngneat/spectator/jest';
import { MockComponent } from 'ng-mocks';
import { of } from 'rxjs';
Expand All @@ -18,10 +17,10 @@ describe('IxLogoComponent', () => {
mockProvider(ThemeService, {
activeTheme$: of('ix-dark'),
}),
mockProvider(MediaObserver, {
asObservable: () => of([{
mqAlias: 'sm',
}]),
mockProvider(BreakpointObserver, {
observe: () => of({
matches: false,
}),
}),
],
});
Expand Down
19 changes: 9 additions & 10 deletions src/app/modules/layout/topbar/ix-logo/ix-logo.component.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { AsyncPipe } from '@angular/common';
import { ChangeDetectionStrategy, Component } from '@angular/core';
// eslint-disable-next-line no-restricted-imports
import { MediaObserver } from '@ngbracket/ngx-layout';
import { combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';
import { IxIconModule } from 'app/modules/ix-icon/ix-icon.module';
Expand All @@ -22,27 +21,27 @@ import { ThemeService } from 'app/services/theme/theme.service';
})
export class IxLogoComponent {
constructor(
private mediaObserver: MediaObserver,
private themeService: ThemeService,
private breakpointObserver: BreakpointObserver,
) {}

screenSize$ = this.mediaObserver.asObservable().pipe(
map((changes) => changes[0].mqAlias),
readonly isXsScreen$ = this.breakpointObserver.observe(Breakpoints.XSmall).pipe(
map((result) => result.matches),
);

logoIcon$ = combineLatest([
this.themeService.activeTheme$,
this.screenSize$,
this.isXsScreen$,
]).pipe(
map(([activeTheme, screenSize]) => {
map(([activeTheme, isXsScreen]) => {
const isBlueTheme = activeTheme === 'ix-blue' || activeTheme === 'midnight';
if (isBlueTheme && screenSize === 'xs') {
if (isBlueTheme && isXsScreen) {
return 'ix:logo_mark';
}
if (!isBlueTheme && screenSize === 'xs') {
if (!isBlueTheme && isXsScreen) {
return 'ix:logo_mark_rgb';
}
if (isBlueTheme && screenSize !== 'xs') {
if (isBlueTheme && !isXsScreen) {
return 'ix:logo_full';
}
return 'ix:logo_full_rgb';
Expand Down
54 changes: 54 additions & 0 deletions src/app/services/sidenav.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { BreakpointObserver } from '@angular/cdk/layout';
import { Router } from '@angular/router';
import {
createServiceFactory,
mockProvider,
SpectatorService,
} from '@ngneat/spectator/jest';
import { provideMockActions } from '@ngrx/effects/testing';
import { provideMockStore } from '@ngrx/store/testing';
import { BehaviorSubject, of } from 'rxjs';
import { SidenavService } from 'app/services/sidenav.service';
import { selectPreferences } from 'app/store/preferences/preferences.selectors';

describe('SidenavService', () => {
let spectator: SpectatorService<SidenavService>;
const breakpointObserve$ = new BehaviorSubject({ matches: true });
const createService = createServiceFactory({
service: SidenavService,
providers: [
mockProvider(Router, {
events: of(),
}),
mockProvider(BreakpointObserver, {
observe: jest.fn(() => breakpointObserve$),
}),
provideMockActions(of()),
provideMockStore({
selectors: [
{
selector: selectPreferences,
value: {
sidenavStatus: {
isCollapsed: true,
},
},
},
],
}),
],
});

beforeEach(() => {
spectator = createService();
});

describe('listenForScreenSizeChanges', () => {
it('listens for screen size changes and sets isMobile accordingly', () => {
expect(spectator.service.isMobile()).toBe(true);

breakpointObserve$.next({ matches: false });
expect(spectator.service.isMobile()).toBe(false);
});
});
});
73 changes: 36 additions & 37 deletions src/app/services/sidenav.service.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { Inject, Injectable } from '@angular/core';
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { Injectable, signal } from '@angular/core';
import { MatDrawerMode, MatSidenav } from '@angular/material/sidenav';
import { Router, NavigationEnd } from '@angular/router';
// eslint-disable-next-line no-restricted-imports
import { MediaObserver } from '@ngbracket/ngx-layout';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { Actions, ofType } from '@ngrx/effects';
import { Store } from '@ngrx/store';
import { take, filter, distinctUntilChanged } from 'rxjs';
import { WINDOW } from 'app/helpers/window.helper';
import { SidenavStatusData } from 'app/interfaces/events/sidenav-status-event.interface';
import { SubMenuItem } from 'app/interfaces/menu-item.interface';
import { AppsState } from 'app/store';
Expand All @@ -15,12 +14,14 @@ import { sidenavIndicatorPressed, sidenavUpdated } from 'app/store/topbar/topbar

export const collapsedMenuClass = 'collapsed-menu';

@UntilDestroy()
@Injectable({
providedIn: 'root',
})
export class SidenavService {
sidenav: MatSidenav;
isOpen = true;
// TODO: How is this different from isMenuCollapsed?
isCollapsed = false;
mode: MatDrawerMode = 'over';
isOpenSecondaryMenu = false;
Expand All @@ -38,9 +39,7 @@ export class SidenavService {
return '0px';
}

get isMobile(): boolean {
return this.window.innerWidth < 960;
}
readonly isMobile = signal(false);

get isMenuCollapsed(): boolean {
return document.getElementsByClassName(collapsedMenuClass).length === 1;
Expand All @@ -62,10 +61,9 @@ export class SidenavService {

constructor(
private router: Router,
private mediaService: MediaObserver,
private breakpointObserver: BreakpointObserver,
private store$: Store<AppsState>,
private actions$: Actions,
@Inject(WINDOW) private window: Window,
) {
this.listenForScreenSizeChanges();
this.listenForRouteChanges();
Expand All @@ -76,10 +74,6 @@ export class SidenavService {
this.sidenav = sidenav;
}

getSidenav(): MatSidenav {
return this.sidenav;
}

setSidenavStatus(sidenav: SidenavStatusData): void {
this.isOpen = sidenav.isOpen;
this.mode = sidenav.mode;
Expand All @@ -103,27 +97,32 @@ export class SidenavService {
}

private listenForScreenSizeChanges(): void {
this.mediaService.asObservable().subscribe(() => {
this.isOpen = !this.isMobile;
this.mode = this.isMobile ? 'over' : 'side';
if (!this.isMobile) {
// TODO: This is hack to resolve issue described here: https://ixsystems.atlassian.net/browse/NAS-110404
setTimeout(() => {
this.sidenav?.open();
});
this.store$.pipe(
waitForPreferences,
take(1),
filter((preferences) => Boolean(preferences.sidenavStatus)),
).subscribe(({ sidenavStatus }) => {
this.isMenuCollapsed = sidenavStatus.isCollapsed;
this.isCollapsed = sidenavStatus.isCollapsed;
});
} else {
this.isMenuCollapsed = false;
this.isOpen = false;
}
});
this.breakpointObserver
.observe([Breakpoints.XSmall, Breakpoints.Small])
.pipe(untilDestroyed(this))
.subscribe((state) => {
const isMobile = state.matches;
this.isMobile.set(isMobile);
this.isOpen = !isMobile;
this.mode = isMobile ? 'over' : 'side';
if (!isMobile) {
// TODO: This is hack to resolve issue described here: https://ixsystems.atlassian.net/browse/NAS-110404
setTimeout(() => {
this.sidenav?.open();
});
this.store$.pipe(
waitForPreferences,
take(1),
filter((preferences) => Boolean(preferences.sidenavStatus)),
).subscribe(({ sidenavStatus }) => {
this.isMenuCollapsed = sidenavStatus.isCollapsed;
this.isCollapsed = sidenavStatus.isCollapsed;
});
} else {
this.isMenuCollapsed = false;
this.isOpen = false;
}
});
}

private listenForSidenavIndicatorPressed(): void {
Expand All @@ -137,7 +136,7 @@ export class SidenavService {
}

private toggleSidenav(): void {
if (this.isMobile) {
if (this.isMobile()) {
this.sidenav?.toggle();
} else {
this.sidenav?.open();
Expand All @@ -150,7 +149,7 @@ export class SidenavService {
isCollapsed: this.isMenuCollapsed,
};

if (!this.isMobile) {
if (!this.isMobile()) {
this.store$.dispatch(sidenavUpdated(data));
}

Expand All @@ -159,7 +158,7 @@ export class SidenavService {

private listenForRouteChanges(): void {
this.router.events.pipe(
filter((routeChange) => routeChange instanceof NavigationEnd && this.isMobile),
filter((routeChange) => routeChange instanceof NavigationEnd && this.isMobile()),
).subscribe(() => {
this.sidenav?.close();
});
Expand Down
12 changes: 0 additions & 12 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2327,13 +2327,6 @@
"@emnapi/runtime" "^1.1.0"
"@tybys/wasm-util" "^0.9.0"

"@ngbracket/ngx-layout@~17.0.1":
version "17.0.1"
resolved "https://registry.yarnpkg.com/@ngbracket/ngx-layout/-/ngx-layout-17.0.1.tgz#acbc418e4f7ee4fec33d68fbee341c2787a31a34"
integrity sha512-HHPQXEIDpdb0FuJPWuy3noS7n49F6vr8e0kJ6lusmOgjFpRxUQeNua1eXWyPSnzw1QcWIJK6XXxKK4OidOHrPA==
dependencies:
tslib "^2.5.0"

"@ngneat/reactive-forms@~5.0.2":
version "5.0.2"
resolved "https://registry.yarnpkg.com/@ngneat/reactive-forms/-/reactive-forms-5.0.2.tgz#d362983b2064ee00ae8d5765bcf1faaee2a386a8"
Expand Down Expand Up @@ -11522,11 +11515,6 @@ tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==

tslib@^2.5.0:
version "2.6.3"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0"
integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==

tsutils-etc@^1.4.1:
version "1.4.2"
resolved "https://registry.yarnpkg.com/tsutils-etc/-/tsutils-etc-1.4.2.tgz#6d6a9f33aa61867d832e4a455b2cebb6b104ebfa"
Expand Down

0 comments on commit 515e04a

Please sign in to comment.