Skip to content

Commit

Permalink
feat(rx): add rx-observe-visibility directive
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelbe812 committed Jan 17, 2023
1 parent 75d6b51 commit a832922
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 2 deletions.
4 changes: 2 additions & 2 deletions libs/rx/platform/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export * from './lib/create-resize-observer';
export * from './lib/create-intersection-observer';
export * from './lib/create-mutation-observer';

export * from './lib/directives/observe-resize.directive';
export * from './lib/directives/rx-observe-resize.directive';
export * from './lib/directives/observe-intersection.directive';


export * from './lib/directives/rx-observe-visibility.directive';
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {RxObserveVisibilityDirective, RxObserveVisibilityDirectiveModule} from './rx-observe-visibility.directive';
import {Component, ViewChild} from '@angular/core';
import {mockIntersectionObserver} from '@angular-kit/test-helpers';
import {fakeAsync, TestBed, tick} from '@angular/core/testing';
import {subscribeSpyTo} from '@hirez_io/observer-spy';

describe('RxObserveVisibilityDirective', () => {
it('should create an instance', async () => {
const { testComponent } = await create();
expect(testComponent).toBeTruthy();
});

it('should emit on intersection', fakeAsync(async () => {
const { testComponent, fixture } = await create();
const result = subscribeSpyTo(testComponent.directive.intersectStatusChange);

fixture.nativeElement.dispatchEvent(new Event('intersect'));
tick(1000);
expect(result.getValues()).toEqual(['Visible']);
}));
});

async function create() {
@Component({
template: `
<section style="position: relative; height: 200px; overflow: auto;">
<h1
id="resize_elem"
style="position: absolute; top: 200px; height: 200px;"
rxObserveVisibility
(intersectStatusChange)="onChange()"
>
I'm being observed
</h1>
</section>
`,
})
class TestComponent {
@ViewChild(RxObserveVisibilityDirective, { static: true }) directive!: RxObserveVisibilityDirective;
onChange = jest.fn();
observe = true;
}

TestBed.configureTestingModule({
imports: [RxObserveVisibilityDirectiveModule],
declarations: [TestComponent],
});
mockIntersectionObserver();
const fixture = TestBed.createComponent(TestComponent);
const testComponent = fixture.componentInstance;
fixture.detectChanges();

return { fixture, testComponent };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import {Directive, ElementRef, EventEmitter, Input, NgModule, Output} from '@angular/core';
import {CommonModule} from '@angular/common';
import {combineLatest, mergeMap, SchedulerLike, startWith, Subscription} from "rxjs";
import {createSignal} from '@angular-kit/rx/signal';
import {createIntersectionObserver} from "../create-intersection-observer";


export type IntersectionStatus = 'Visible' | 'Hidden';

@Directive({
selector: '[rxObserveVisibility]',
})
export class RxObserveVisibilityDirective {
private sub = new Subscription();
private rxObserveVisibilityDebounceSignal = createSignal(0);
private rxObserveVisibilityRootMarginSignal = createSignal('0px');
private rxObserveVisibilityRootSignal = createSignal<HTMLElement | undefined>(undefined);
private rxObserveVisibilityThresholdSignal = createSignal<number | number[]>(0);
private rxObserveVisibilitySchedulerSignal = createSignal<SchedulerLike | undefined>(undefined);

@Input() set rxObserveVisibilityDebounce(debounceInMs: number){
this.rxObserveVisibilityDebounceSignal.send(debounceInMs);
}
@Input() set rxObserveVisibilityRootMargin(rootMargin: string) {
this.rxObserveVisibilityRootMarginSignal.send(rootMargin);
}
@Input() set rxObserveVisibilityRoot(root: HTMLElement | undefined) {
this.rxObserveVisibilityRootSignal.send(root);
}
@Input() set rxObserveVisibilityThreshold(threshold: number | number[]) {
this.rxObserveVisibilityThresholdSignal.send(threshold);
}
@Input() set rxObserveVisibilityScheduler(scheduler: SchedulerLike) {
this.rxObserveVisibilitySchedulerSignal.send(scheduler);
}

@Output() intersectStatusChange = new EventEmitter<IntersectionStatus>();

constructor(private element: ElementRef) {
this.sub = combineLatest([
this.rxObserveVisibilityDebounceSignal.$.pipe(startWith(0)),
this.rxObserveVisibilityRootMarginSignal.$.pipe(startWith('0px')),
this.rxObserveVisibilityRootSignal.$.pipe(startWith(undefined)),
this.rxObserveVisibilityThresholdSignal.$.pipe(startWith(0)),
this.rxObserveVisibilitySchedulerSignal.$.pipe(startWith(undefined)),
]).pipe(
mergeMap(([debounceInMs, rootMargin, root, threshold, scheduler]) => createIntersectionObserver(this.element, {
root: root,
rootMargin: rootMargin,
threshold: threshold,
}, {
throttleMs: debounceInMs,
scheduler: scheduler
}))
).subscribe((entries) => {
const entry = entries[0];
if (entry.isIntersecting) {
this.intersectStatusChange.emit('Visible');
}

/* if (entry.intersectionRatio > 0 && entry.intersectionRatio < 1) {
this.visibilityChange.emit('Pending');
}*/

if(!entry.isIntersecting){
this.intersectStatusChange.emit('Hidden');
}
})
}
}

@NgModule({
imports: [CommonModule],
declarations: [RxObserveVisibilityDirective],
exports: [RxObserveVisibilityDirective],
})
export class RxObserveVisibilityDirectiveModule {}

0 comments on commit a832922

Please sign in to comment.