From d7aa1051ebdff00fa7943c56691705b7f392b97a Mon Sep 17 00:00:00 2001 From: Rene Haas Date: Tue, 8 Nov 2022 01:07:11 +0100 Subject: [PATCH] add angular directive --- .../src/overlayscrollbars.component.ts | 107 ++++++++---------- .../src/overlayscrollbars.directive.ts | 65 +++++++++++ .../src/overlayscrollbars.module.ts | 5 +- .../overlayscrollbars-ngx/src/public-api.ts | 1 + .../test/overlayscrollbars.component.spec.ts | 12 +- 5 files changed, 123 insertions(+), 67 deletions(-) create mode 100644 packages/overlayscrollbars-ngx/src/overlayscrollbars.directive.ts diff --git a/packages/overlayscrollbars-ngx/src/overlayscrollbars.component.ts b/packages/overlayscrollbars-ngx/src/overlayscrollbars.component.ts index d05d79d..8e2cad3 100644 --- a/packages/overlayscrollbars-ngx/src/overlayscrollbars.component.ts +++ b/packages/overlayscrollbars-ngx/src/overlayscrollbars.component.ts @@ -7,12 +7,10 @@ import { ViewChild, ElementRef, OnDestroy, - OnChanges, AfterViewInit, - SimpleChanges, - NgZone, } from '@angular/core'; import { OverlayScrollbars } from 'overlayscrollbars'; +import { OverlayScrollbarsDirective } from '~/overlayscrollbars.directive'; import type { PartialOptions, EventListeners, EventListenerMap } from 'overlayscrollbars'; const mergeEventListeners = (emits: EventListeners, events: EventListeners) => @@ -31,17 +29,19 @@ const mergeEventListeners = (emits: EventListeners, events: EventListeners) => ); @Component({ - selector: '[overlay-scrollbars]', // https://angular.io/guide/styleguide#component-selectors + selector: '[overlay-scrollbars-component]', // https://angular.io/guide/styleguide#component-selectors exportAs: 'overlayScrollbars', host: { 'data-overlayscrollbars': '' }, - template: `
`, + template: `
+ +
`, }) -export class OverlayScrollbarsComponent implements OnDestroy, OnChanges, AfterViewInit { - private instanceRef: OverlayScrollbars | null = null; - - @ViewChild('content') - private contentRef?: ElementRef; - +export class OverlayScrollbarsComponent implements OnDestroy, AfterViewInit { @Input('options') options?: PartialOptions | false | null; @Input('events') @@ -56,9 +56,42 @@ export class OverlayScrollbarsComponent implements OnDestroy, OnChanges, AfterVi @Output('osScroll') onScroll = new EventEmitter(); - constructor(private targetRef: ElementRef, private ngZone: NgZone) {} + @ViewChild('content') + private contentRef?: ElementRef; + @ViewChild('content', { read: OverlayScrollbarsDirective }) + private osDirective?: OverlayScrollbarsDirective; - private mergedEvents(originalEvents: OverlayScrollbarsComponent['events']) { + constructor(private targetRef: ElementRef) {} + + instance(): OverlayScrollbars | null { + return this.osDirective!.instance(); + } + + element(): HTMLElement { + return this.targetRef.nativeElement; + } + + ngAfterViewInit() { + const targetElm = this.element(); + const contentElm = this.contentRef!.nativeElement; + + /* istanbul ignore else */ + if (targetElm && contentElm) { + this.osDirective!.initialize({ + target: targetElm, + elements: { + viewport: contentElm, + content: contentElm, + }, + }); + } + } + + ngOnDestroy() { + this.osDirective?.instance()!.destroy(); + } + + private mergeEvents(originalEvents: OverlayScrollbarsComponent['events']) { return mergeEventListeners( { initialized: (...args) => this.onInitialized.emit(args), @@ -69,52 +102,4 @@ export class OverlayScrollbarsComponent implements OnDestroy, OnChanges, AfterVi originalEvents || {} ); } - - instance(): OverlayScrollbars | null { - return this.instanceRef; - } - - element(): HTMLElement { - return this.targetRef.nativeElement; - } - - ngAfterViewInit() { - this.ngZone.runOutsideAngular(() => { - const targetElm = this.element(); - const contentElm = this.contentRef!.nativeElement; - - /* istanbul ignore else */ - if (targetElm && contentElm) { - this.instanceRef = OverlayScrollbars( - { - target: targetElm, - elements: { - viewport: contentElm, - content: contentElm, - }, - }, - this.options || {}, - this.mergedEvents(this.events) - ); - } - }); - } - - ngOnDestroy() { - this.instanceRef?.destroy(); - } - - ngOnChanges(changes: SimpleChanges) { - const optionsChange = changes.options; - const eventsChange = changes.events; - - if (OverlayScrollbars.valid(this.instanceRef)) { - if (optionsChange) { - this.instanceRef.options(optionsChange.currentValue || {}, true); - } - if (eventsChange) { - this.instanceRef.on(this.mergedEvents(eventsChange.currentValue), true); - } - } - } } diff --git a/packages/overlayscrollbars-ngx/src/overlayscrollbars.directive.ts b/packages/overlayscrollbars-ngx/src/overlayscrollbars.directive.ts new file mode 100644 index 0000000..c8ed30e --- /dev/null +++ b/packages/overlayscrollbars-ngx/src/overlayscrollbars.directive.ts @@ -0,0 +1,65 @@ +/* eslint-disable @typescript-eslint/consistent-type-imports */ +import { Directive, Input, OnChanges, SimpleChanges, NgZone } from '@angular/core'; +import { OverlayScrollbars } from 'overlayscrollbars'; +import type { InitializationTarget } from 'overlayscrollbars'; +import type { OverlayScrollbarsComponent } from '~/overlayscrollbars.component'; + +@Directive({ + selector: '[overlayScrollbarsDirective]', // https://angular.io/guide/styleguide#component-selectors + exportAs: 'overlayScrollbars', +}) +export class OverlayScrollbarsDirective implements OnChanges { + private instanceRef: OverlayScrollbars | null = null; + + @Input('options') + options?: OverlayScrollbarsComponent['options']; + @Input('events') + events?: OverlayScrollbarsComponent['events']; + + constructor(private ngZone: NgZone) {} + + initialize(target: InitializationTarget): OverlayScrollbars { + this.ngZone.runOutsideAngular(() => { + this.instanceRef = OverlayScrollbars( + target, + this.options || {}, + /* istanbul ignore next */ + this.events || {} + ); + }); + return this.instanceRef!; + } + + instance(): OverlayScrollbars | null { + return this.instanceRef; + } + + ngOnChanges(changes: SimpleChanges) { + const optionsChange = changes.options; + const eventsChange = changes.events; + + if (optionsChange) { + const curr = optionsChange.currentValue; + + this.options = curr; + + if (OverlayScrollbars.valid(this.instanceRef)) { + this.instanceRef.options(curr || {}, true); + } + } + /* istanbul ignore else */ + if (eventsChange) { + const curr = eventsChange.currentValue; + + this.events = curr; + + if (OverlayScrollbars.valid(this.instanceRef)) { + this.instanceRef.on( + /* istanbul ignore next */ + curr || {}, + true + ); + } + } + } +} diff --git a/packages/overlayscrollbars-ngx/src/overlayscrollbars.module.ts b/packages/overlayscrollbars-ngx/src/overlayscrollbars.module.ts index 2b86978..e9a20aa 100644 --- a/packages/overlayscrollbars-ngx/src/overlayscrollbars.module.ts +++ b/packages/overlayscrollbars-ngx/src/overlayscrollbars.module.ts @@ -1,8 +1,9 @@ import { NgModule } from '@angular/core'; +import { OverlayScrollbarsDirective } from './overlayscrollbars.directive'; import { OverlayScrollbarsComponent } from './overlayscrollbars.component'; @NgModule({ - declarations: [OverlayScrollbarsComponent], - exports: [OverlayScrollbarsComponent], + declarations: [OverlayScrollbarsComponent, OverlayScrollbarsDirective], + exports: [OverlayScrollbarsComponent, OverlayScrollbarsDirective], }) export class OverlayscrollbarsModule {} diff --git a/packages/overlayscrollbars-ngx/src/public-api.ts b/packages/overlayscrollbars-ngx/src/public-api.ts index 4657260..735197d 100644 --- a/packages/overlayscrollbars-ngx/src/public-api.ts +++ b/packages/overlayscrollbars-ngx/src/public-api.ts @@ -1,2 +1,3 @@ export * from './overlayscrollbars.component'; +export * from './overlayscrollbars.directive'; export * from './overlayscrollbars.module'; diff --git a/packages/overlayscrollbars-ngx/test/overlayscrollbars.component.spec.ts b/packages/overlayscrollbars-ngx/test/overlayscrollbars.component.spec.ts index 0e2bf05..9f70eee 100644 --- a/packages/overlayscrollbars-ngx/test/overlayscrollbars.component.spec.ts +++ b/packages/overlayscrollbars-ngx/test/overlayscrollbars.component.spec.ts @@ -1,14 +1,18 @@ import { Component, ViewChild } from '@angular/core'; import { TestBed } from '@angular/core/testing'; import { OverlayScrollbars } from 'overlayscrollbars'; -import { OverlayScrollbarsComponent, OverlayscrollbarsModule } from '~/public-api'; +import { + OverlayScrollbarsComponent, + OverlayScrollbarsDirective, + OverlayscrollbarsModule, +} from '~/public-api'; import type { ComponentFixture } from '@angular/core/testing'; import type { EventListenerMap } from 'overlayscrollbars'; @Component({ template: `
hello angular
empty
@@ -73,7 +77,7 @@ describe('OverlayscrollbarsNgxComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ ...new OverlayscrollbarsModule(), - declarations: [OverlayScrollbarsComponent, Test], + declarations: [OverlayScrollbarsComponent, OverlayScrollbarsDirective, Test], }).compileComponents(); fixture = TestBed.createComponent(OverlayScrollbarsComponent);