Skip to content

Commit

Permalink
chore: create new observer
Browse files Browse the repository at this point in the history
  • Loading branch information
hirsch88 committed Jan 12, 2025
1 parent c962509 commit bf800b5
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 28 deletions.
12 changes: 7 additions & 5 deletions packages/core/src/components/bal-carousel/bal-carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { BalMutationObserver, ListenToMutation } from '../../utils/mutation'
import { BalResizeObserver, ListenToResize } from '../../utils/resize'
import { BalConfigState, BalLanguage, ListenToConfig, defaultConfig } from '../../utils/config'
import { SwiperChildItem, SwiperInterface, SwiperUtil } from '../../utils/swiper'
import { isChildOfEventTarget, rIC, waitAfterIdleCallback, waitForRequestIdleCallback } from '../../utils/helpers'
import { BalVisibilityObserver, ListenToVisibility } from '../../utils/visibility'

@Component({
tag: 'bal-carousel',
Expand All @@ -35,6 +35,7 @@ export class Carousel
BalSwipeObserver,
BalMutationObserver,
BalResizeObserver,
BalVisibilityObserver,
SwiperInterface
{
swiper = new SwiperUtil()
Expand Down Expand Up @@ -164,10 +165,6 @@ export class Carousel
this.onValueChange()
}

componentDidLoad(): void {
this.swiper.componentDidLoad()
}

disconnectedCallback(): void {
this.swiper.disconnectedCallback()
}
Expand All @@ -191,6 +188,11 @@ export class Carousel
this.swiper.notifyChange()
}

@ListenToVisibility()
visibilityListener(): void {
this.swiper.notifyChange()
}

@ListenToSwipe()
swipeListener({ left, right }: BalSwipeInfo) {
if (left) {
Expand Down
8 changes: 7 additions & 1 deletion packages/core/src/components/bal-tabs/bal-tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { TabNav } from './components/tab-nav'
import { toKebabCase } from '../../utils/string'
import { SwiperChildItem, SwiperInterface, SwiperUtil } from '../../utils/swiper'
import { BalSwipeInfo, ListenToSwipe } from '../../utils/swipe'
import { BalVisibilityObserver, ListenToVisibility } from '../../utils/visibility'

@Component({
tag: 'bal-tabs',
Expand All @@ -54,6 +55,7 @@ export class Tabs
BalMutationObserver,
BalBreakpointObserver,
BalResizeObserver,
BalVisibilityObserver,
SwiperInterface
{
private tabsId = `bal-tabs-${TabsIds++}`
Expand Down Expand Up @@ -273,7 +275,6 @@ export class Tabs
}

componentDidLoad() {
this.swiper.componentDidLoad()
this.onOptionChange()
rOnLoad(() => {
this.enableLineRender = true
Expand All @@ -298,6 +299,11 @@ export class Tabs
this.swiper.notifyChange()
}

@ListenToVisibility()
visibilityListener(): void {
this.swiper.notifyChange()
}

@ListenToBreakpoints()
breakpointListener(breakpoints: BalBreakpoints): void {
this.isMobile = breakpoints.mobile
Expand Down
22 changes: 0 additions & 22 deletions packages/core/src/utils/swiper/swiper.util.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,28 +51,6 @@ export class SwiperUtil {
addEventListener(window, 'mousedown', this.pointerDown)
}

public componentDidLoad() {
const observer = new IntersectionObserver(
entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
this.notifyChange()
}
})
},
{
root: null, // Use the viewport as the container
threshold: 0.5, // Trigger when 50% of the element is visible
},
)

// Target the element you want to observe
const targetElement = this.component.el
if (targetElement) {
observer.observe(targetElement)
}
}

public disconnectedCallback() {
removeEventListener(window, 'keydown', this.listenToKeyDown)
removeEventListener(this.component.el, 'focusin', this.updateFocus)
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/utils/visibility/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './visibility.interfaces'
export * from './visibility.decorator'
36 changes: 36 additions & 0 deletions packages/core/src/utils/visibility/visibility.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { ComponentInterface } from '@stencil/core'
import { BalVisibilityObserver } from './visibility.interfaces'
import { BalVisibilitySubject } from './visibility.subject'

export function ListenToVisibility() {
return function (
target: ComponentInterface & BalVisibilityObserver,
_propertyKey: string,
_descriptor: PropertyDescriptor,
) {
const { connectedCallback, componentDidLoad, disconnectedCallback } = target

target.connectedCallback = function () {
if (!this._balVisibilitySubject) {
this._balVisibilitySubject = new BalVisibilitySubject()
}

return connectedCallback && connectedCallback.call(this)
}

target.componentDidLoad = function () {
this._balVisibilitySubject.attach(this)

return componentDidLoad && componentDidLoad.call(this)
}

target.disconnectedCallback = function () {
if (this._balVisibilitySubject) {
this._balVisibilitySubject.detach()
this._balVisibilitySubject = undefined
}

return disconnectedCallback && disconnectedCallback.call(this)
}
}
}
4 changes: 4 additions & 0 deletions packages/core/src/utils/visibility/visibility.interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface BalVisibilityObserver {
el: HTMLElement
visibilityListener(): void
}
43 changes: 43 additions & 0 deletions packages/core/src/utils/visibility/visibility.listener.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { deepReady, waitAfterFramePaint } from '../helpers'
import { ListenerAbstract } from '../types/listener'

export class BalVisibilityListener extends ListenerAbstract {
private waitAfterFramePrint = false
private intersectionObserver: IntersectionObserver | undefined = undefined

async connect(el: HTMLElement) {
super.connect(el)
if (typeof IntersectionObserver === 'undefined') {
return
}
if (this.waitAfterFramePrint) {
await deepReady(el)
await waitAfterFramePaint()
}
this.destroyMutationObserver()
this.intersectionObserver = new IntersectionObserver(this.intersectionCallback, {
root: null, // Use the viewport as the container
threshold: 0.5, // Trigger when 50% of the element is visible
})
this.intersectionObserver.observe(el)
}

disconnect(): void {
super.disconnect()
this.destroyMutationObserver()
}

private intersectionCallback = (records: IntersectionObserverEntry[]) => {
const isIntersecting = records.some(record => record.isIntersecting)
if (isIntersecting) {
this.notify()
}
}

private destroyMutationObserver() {
if (this.intersectionObserver !== undefined) {
this.intersectionObserver.disconnect()
this.intersectionObserver = undefined
}
}
}
27 changes: 27 additions & 0 deletions packages/core/src/utils/visibility/visibility.subject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { debounce } from '../helpers'
import { SingleSubject } from '../types/signal'
import { BalVisibilityObserver } from './visibility.interfaces'
import { BalVisibilityListener } from './visibility.listener'

export class BalVisibilitySubject extends SingleSubject<BalVisibilityObserver> {
private listener?: BalVisibilityListener
private debouncedNotify = debounce(() => this.notify(), 50)

constructor() {
super(observer => {
observer.visibilityListener()
})
this.listener = new BalVisibilityListener()
}

override attach(observer: BalVisibilityObserver): void {
super.attach(observer)
this.listener?.connect(observer.el)
this.listener?.add(() => this.debouncedNotify())
}

override detach(): void {
super.detach()
this.listener?.disconnect()
}
}

0 comments on commit bf800b5

Please sign in to comment.