diff --git a/apps/docs/src/app/core/component-docs/dynamic-page/dynamic-page-examples/dynamic-page-column-layout-example/dynamic-page-column-layout-example.component.html b/apps/docs/src/app/core/component-docs/dynamic-page/dynamic-page-examples/dynamic-page-column-layout-example/dynamic-page-column-layout-example.component.html
index 80abc0cedd8..2060243c08d 100644
--- a/apps/docs/src/app/core/component-docs/dynamic-page/dynamic-page-examples/dynamic-page-column-layout-example/dynamic-page-column-layout-example.component.html
+++ b/apps/docs/src/app/core/component-docs/dynamic-page/dynamic-page-examples/dynamic-page-column-layout-example/dynamic-page-column-layout-example.component.html
@@ -146,6 +146,17 @@
Mid Column
Open Column 3
+
+
+
+ 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.
+
+
+
diff --git a/libs/core/src/lib/tabs/tab-list.component.ts b/libs/core/src/lib/tabs/tab-list.component.ts
index ba0a3b16552..67fb87dcd91 100644
--- a/libs/core/src/lib/tabs/tab-list.component.ts
+++ b/libs/core/src/lib/tabs/tab-list.component.ts
@@ -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';
@@ -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
) {}
@@ -228,8 +229,7 @@ export class TabListComponent implements AfterContentInit, AfterViewInit, OnDest
private get _tabPanelsChange$(): Observable {
return this.tabPanels.changes.pipe(
startWith(this.tabPanels),
- takeUntil(this._onDestroy$),
- map((tabPanels) => tabPanels.toArray())
+ map((tabPanels) => tabPanels.toArray(), takeUntil(this._onDestroy$))
);
}
@@ -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))
);
@@ -273,11 +273,16 @@ 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 */
@@ -285,7 +290,8 @@ export class TabListComponent implements AfterContentInit, AfterViewInit, OnDest
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));
}
@@ -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));
}
@@ -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();
@@ -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);
@@ -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);
}
diff --git a/libs/core/src/lib/utils/functions/index.ts b/libs/core/src/lib/utils/functions/index.ts
index e0e527d1565..d3bea5c3571 100644
--- a/libs/core/src/lib/utils/functions/index.ts
+++ b/libs/core/src/lib/utils/functions/index.ts
@@ -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';
diff --git a/libs/core/src/lib/utils/functions/resize-observable.spec.ts b/libs/core/src/lib/utils/functions/resize-observable.spec.ts
new file mode 100644
index 00000000000..82503feabd7
--- /dev/null
+++ b/libs/core/src/lib/utils/functions/resize-observable.spec.ts
@@ -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;
+
+ 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();
+ });
+});
diff --git a/libs/core/src/lib/utils/functions/resize-observable.ts b/libs/core/src/lib/utils/functions/resize-observable.ts
new file mode 100644
index 00000000000..c418962bb1e
--- /dev/null
+++ b/libs/core/src/lib/utils/functions/resize-observable.ts
@@ -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 {
+ 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((_) => []));
+ }
+}