Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(core): rely on resize observer instead of window resize event #6940

Merged
merged 3 commits into from
Oct 21, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,17 @@ <h2>Mid Column</h2>
<div>
<button class="fd-button" (click)="changeLayout('ThreeColumnsMidExpanded')">Open Column 3</button>
</div>
<fd-tab-list>
<fd-tab *ngFor="let tab of [1, 2, 3, 4]" [title]="'Tab ' + tab">
<div class="fd-dynamic-page-section-example">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in
voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</div>
</fd-tab>
</fd-tab-list>
</div>
</ng-template>
<ng-template #endColumn>
Expand Down
46 changes: 27 additions & 19 deletions libs/core/src/lib/tabs/tab-list.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
} from '@angular/core';
import { fromEvent, merge, Observable, Subject, Subscription } from 'rxjs';
import { debounceTime, delay, filter, first, map, startWith, switchMap, takeUntil } from 'rxjs/operators';
import { getElementCapacity, getElementWidth, KeyUtil } from '@fundamental-ngx/core/utils';
import { getElementCapacity, getElementWidth, KeyUtil, resizeObservable } from '@fundamental-ngx/core/utils';
import { TabItemExpandComponent } from './tab-item-expand/tab-item-expand.component';
import { TabLinkDirective } from './tab-link/tab-link.directive';
import { TabItemDirective } from './tab-item/tab-item.directive';
Expand Down Expand Up @@ -148,6 +148,7 @@ export class TabListComponent implements AfterContentInit, AfterViewInit, OnDest
constructor(
private _tabsService: TabsService,
private _changeDetectorRef: ChangeDetectorRef,
private _elRef: ElementRef,
@Optional() private _contentDensityService: ContentDensityService
) {}

Expand Down Expand Up @@ -228,8 +229,7 @@ export class TabListComponent implements AfterContentInit, AfterViewInit, OnDest
private get _tabPanelsChange$(): Observable<TabPanelComponent[]> {
return this.tabPanels.changes.pipe(
startWith(this.tabPanels),
takeUntil(this._onDestroy$),
map((tabPanels) => tabPanels.toArray())
map((tabPanels) => tabPanels.toArray(), takeUntil(this._onDestroy$))
);
}

Expand Down Expand Up @@ -264,7 +264,7 @@ export class TabListComponent implements AfterContentInit, AfterViewInit, OnDest
private _listenOnTabPanelsAndSetupStackedContent(): void {
if (this.stackContent) {
this._tabPanelsChange$
.pipe(delay(0))
.pipe(delay(0), takeUntil(this._onDestroy$))
.subscribe(() =>
this._tabArray.filter((tab) => !tab.panel.disabled).forEach((tab) => tab.panel._expand(true))
);
Expand All @@ -273,19 +273,25 @@ export class TabListComponent implements AfterContentInit, AfterViewInit, OnDest

/** @hidden */
private _listenOnTabPanelsAndUpdateStorageStructures(): void {
this._tabPanelsChange$.pipe(map((tabPanels) => tabPanels.map((el) => new TabInfo(el)))).subscribe((tabs) => {
this._tabArray = tabs;
this._numbOfVisibleTabs = tabs.length;
this._resetVisualOrder();
});
this._tabPanelsChange$
.pipe(
map((tabPanels) => tabPanels.map((el) => new TabInfo(el))),
takeUntil(this._onDestroy$)
)
.subscribe((tabs) => {
this._tabArray = tabs;
this._numbOfVisibleTabs = tabs.length;
this._resetVisualOrder();
});
}

/** @hidden */
private _listenOnTabPanelsExpandedChange(): void {
this._tabPanelsChange$
.pipe(
map((tabPanels) => tabPanels.map((el) => el._expandedStateChange.asObservable())),
switchMap((tabPanels) => merge(...tabPanels))
switchMap((tabPanels) => merge(...tabPanels)),
takeUntil(this._onDestroy$)
)
.subscribe((event) => this._expandTab(event.target, event.state));
}
Expand All @@ -297,7 +303,8 @@ export class TabListComponent implements AfterContentInit, AfterViewInit, OnDest
filter((_) => !this._tabArray.some((tab) => tab.active)),
map((_) => this._tabArray.find((tab) => !tab.disabled)),
filter((tab) => !!tab),
delay(0)
delay(0),
takeUntil(this._onDestroy$)
)
.subscribe((tab) => this._expandTab(tab.panel, true));
}
Expand All @@ -313,16 +320,16 @@ export class TabListComponent implements AfterContentInit, AfterViewInit, OnDest
private _listenOnKeyboardTabSelect(): void {
this._tabsService.tabSelected
.pipe(
takeUntil(this._onDestroy$),
map((index) => this._visualOrder.visible[index].panel)
map((index) => this._visualOrder.visible[index].panel),
takeUntil(this._onDestroy$)
)
.subscribe((tabPanel) => this._expandTab(tabPanel, !tabPanel.expanded));
}

/** @hidden */
private _listenOnResizeAndHideItems(): void {
fromEvent(window, 'resize')
.pipe(debounceTime(100), takeUntil(this._onDestroy$))
resizeObservable(this._elRef.nativeElement)
.pipe(debounceTime(20), takeUntil(this._onDestroy$))
.subscribe((_) => {
this.refreshOverflow();
this._detectChanges();
Expand All @@ -333,13 +340,14 @@ export class TabListComponent implements AfterContentInit, AfterViewInit, OnDest
private _listenOnTabPanelsChangeAndCollapse(): void {
const $tabHeadersSource = this.tabHeaders.changes.pipe(
map((tabHeaders) => tabHeaders.toArray()),
first()
first(),
takeUntil(this._onDestroy$)
);

this.tabPanels.changes
.pipe(
takeUntil(this._onDestroy$),
switchMap(() => $tabHeadersSource)
switchMap(() => $tabHeadersSource),
takeUntil(this._onDestroy$)
)
.subscribe((tabHeaders) => {
this._cacheTabsWidth(tabHeaders);
Expand Down Expand Up @@ -480,7 +488,7 @@ export class TabListComponent implements AfterContentInit, AfterViewInit, OnDest
if (!(currentScrollPosition === maximumScrollTop && distanceToScroll > maximumScrollTop)) {
!this._init ? (this._disableScrollSpy = true) : (this._init = false);
fromEvent(containerElement, 'scroll')
.pipe(takeUntil(this._onDestroy$), debounceTime(100), first())
.pipe(debounceTime(100), first(), takeUntil(this._onDestroy$))
.subscribe(() => (this._disableScrollSpy = false));
scrollTop(containerElement, distanceToScroll);
}
Expand Down
1 change: 1 addition & 0 deletions libs/core/src/lib/utils/functions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export * from './parser-file-size';
export * from './scroll';
export * from './random-color-accent';
export * from './clone-deep';
export * from './resize-observable';
51 changes: 51 additions & 0 deletions libs/core/src/lib/utils/functions/resize-observable.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Component, ElementRef } from '@angular/core';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { resizeObservable } from './resize-observable';

const ELEMENT_DIMENSIONS = { width: 100 };

@Component({
template: '',
host: {
'[style.display]': '"block"',
'[style.box-sizing]': '"border-box"',
'[style.width]': `elementDimensions.width + 'px'`
}
})
class TestComponent {
elementDimensions = ELEMENT_DIMENSIONS;
constructor(public elementRef: ElementRef) {}
}

describe('Resize Observable utils', () => {
let elementRef: ElementRef;
let fixture: ComponentFixture<TestComponent>;

beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [TestComponent]
}).compileComponents();
})
);

beforeEach(() => {
fixture = TestBed.createComponent(TestComponent);
elementRef = fixture.componentInstance.elementRef;
fixture.detectChanges();
});

it('should create', () => {
expect(elementRef).toBeTruthy();
});

it('should spy on element resize', () => {
resizeObservable(elementRef.nativeElement).subscribe((entries) => {
console.log(entries[0]);
const entry = entries[0];
expect(entry.contentRect.width).toEqual(200);
});
fixture.componentInstance.elementDimensions.width = 200;
fixture.detectChanges();
});
});
27 changes: 27 additions & 0 deletions libs/core/src/lib/utils/functions/resize-observable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { fromEvent, Observable } from 'rxjs';
import { map } from 'rxjs/operators';

/**
* RxJS wrapper for ResizeObserver class.
* @param target HTML element to spy on.
* @param options @see {ResizeObserverOptions}
* @returns {Observable} with observer entries.
*/
export function resizeObservable(target: Element, options?: ResizeObserverOptions): Observable<ResizeObserverEntry[]> {
if ('ResizeObserver' in window) {
return new Observable((subscriber) => {
const ro = new ResizeObserver((entries) => {
subscriber.next(entries);
});

ro.observe(target, options);

return function unsubscribe(): void {
ro.disconnect();
};
});
} else {
// If current browser does not support resizeObserver, rely on window resize and return empty array of items.
return fromEvent(window, 'resize').pipe(map((_) => []));
}
}