Skip to content

Commit

Permalink
refactor: minor tab/tabs component improvements (#889)
Browse files Browse the repository at this point in the history
* refactor: minor tab/tabs component improvements

* refactor: do not store elRef,cdRef and index entries

* refactor: sort methods and functions by feature
  • Loading branch information
davidlj95 authored Nov 28, 2024
1 parent ef6663b commit 0012c86
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 41 deletions.
1 change: 1 addition & 0 deletions src/app/header/tab/tab.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Component, ElementRef, Input } from '@angular/core'
},
})
export class TabComponent {
// Can't be migrated yet: https://github.com/davidlj95/website/pull/886
@Input() isSelected = false

constructor(
Expand Down
1 change: 1 addition & 0 deletions src/app/header/tabs/tabs.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
[disabled]="_prevButtonDisabled()"
(click)="_scrollABit(-1)"
></button>
<!--suppress JSUnusedGlobalSymbols - Used in component -->
<div role="tablist" #tabList>
<ng-content></ng-content>
</div>
Expand Down
83 changes: 42 additions & 41 deletions src/app/header/tabs/tabs.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,10 @@ export class TabsComponent implements OnDestroy {
KeyboardDoubleArrowRight,
}

private _tabs = contentChildren(TabComponent, {
// Selected management
private readonly _tabs = contentChildren(TabComponent, {
descendants: false,
})

// Pagination
private _tabList = viewChild.required<ElementRef<HTMLElement>>('tabList')
private _firstTab?: ElementRef<HTMLElement>
private _lastTab?: ElementRef<HTMLElement>
protected _prevButtonDisabled = signal(true)
protected _nextButtonDisabled = signal(true)
private _intersectionObserver!: IntersectionObserver

// Selected management
private _currentTabs: readonly TabComponent[] = []
private _indexToSelect?: number
private _selectedIndex?: number
Expand All @@ -57,25 +48,39 @@ export class TabsComponent implements OnDestroy {
/// Scroll to selected
private _indexToScrollTo?: number

constructor(
private _elRef: ElementRef,
private _cdRef: ChangeDetectorRef,
) {
afterNextRender({
// Pagination
private readonly _tabList =
viewChild.required<ElementRef<HTMLElement>>('tabList')
private _firstTab?: ElementRef<HTMLElement>
private _lastTab?: ElementRef<HTMLElement>
protected readonly _prevButtonDisabled = signal(true)
protected readonly _nextButtonDisabled = signal(true)
private _intersectionObserver?: IntersectionObserver

constructor(elRef: ElementRef<Element>, cdRef: ChangeDetectorRef) {
effect(this._onTabsChanged.bind(this))
afterRender({
read: () => {
this._setupIntersectionObserver()
this._updateSelectedIfNeeded(cdRef)
this._scrollToTabIfNeeded()
},
})
afterRender({
afterNextRender({
read: () => {
this._updateSelectedIfNeeded()
this._scrollToTabIfNeeded()
this._setupIntersectionObserver(elRef)
},
})
effect(this._onTabsChanged.bind(this))
}

private _updateSelectedIfNeeded(): void {
// Selected management
private _onTabsChanged() {
const tabs = this._tabs()
this._currentTabs = tabs
;[this._firstTab, this._lastTab] = [tabs.at(0)?.elRef, tabs.at(-1)?.elRef]
this._resetIntersectionObserverTargets()
}

private _updateSelectedIfNeeded(cdRef: ChangeDetectorRef): void {
if (
this._indexToSelect === undefined ||
this._currentTabs.length === 0 ||
Expand All @@ -89,7 +94,7 @@ export class TabsComponent implements OnDestroy {
this._selectedIndex = this._indexToSelect
this._indexToScrollTo = this._indexToSelect
this._indexToSelect = undefined
this._cdRef.markForCheck()
cdRef.markForCheck()
}

private _scrollToTabIfNeeded(): void {
Expand All @@ -106,35 +111,27 @@ export class TabsComponent implements OnDestroy {
this._indexToScrollTo = undefined
}

ngOnDestroy(): void {
this._intersectionObserver?.disconnect()
}

private _onTabsChanged() {
const tabs = this._tabs()
this._currentTabs = tabs
;[this._firstTab, this._lastTab] = [tabs.at(0)?.elRef, tabs.at(-1)?.elRef]
this._resetIntersectionObserverTargets()
}

private _setupIntersectionObserver() {
// Pagination
private _setupIntersectionObserver(elRef: ElementRef<Element>) {
this._intersectionObserver = new IntersectionObserver(
(entries) => {
const entryByTarget = new Map<Element, IntersectionObserverEntry>(
entries.map((entry) => [entry.target, entry] as const),
)
;(
[
[this._firstTab!, this._prevButtonDisabled],
[this._lastTab!, this._nextButtonDisabled],
] as const
).forEach(([tabElement, signalToUpdate]) => {
const entry = entries.find(
(entry) => entry.target === tabElement.nativeElement,
)
if (!entry) return
signalToUpdate.set(entry.isIntersecting)
const entry = entryByTarget.get(tabElement.nativeElement)
if (entry) {
signalToUpdate.set(entry.isIntersecting)
}
})
},
{
root: this._elRef.nativeElement as Element,
root: elRef.nativeElement,
threshold: [0.8],
},
)
Expand All @@ -149,6 +146,10 @@ export class TabsComponent implements OnDestroy {
}
}

ngOnDestroy(): void {
this._intersectionObserver?.disconnect()
}

protected _scrollABit(scrollDirection: -1 | 1) {
const tabListContainer = this._tabList().nativeElement
//👇 Amount to scroll extracted from MatTab
Expand Down

0 comments on commit 0012c86

Please sign in to comment.