mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-05-27 17:44:08 +03:00
add angular directive
This commit is contained in:
@@ -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: `<div #content><ng-content></ng-content></div>`,
|
||||
template: `<div
|
||||
#content
|
||||
[overlayScrollbarsDirective]
|
||||
[options]="options"
|
||||
[events]="mergeEvents(events)"
|
||||
>
|
||||
<ng-content></ng-content>
|
||||
</div>`,
|
||||
})
|
||||
export class OverlayScrollbarsComponent implements OnDestroy, OnChanges, AfterViewInit {
|
||||
private instanceRef: OverlayScrollbars | null = null;
|
||||
|
||||
@ViewChild('content')
|
||||
private contentRef?: ElementRef<HTMLDivElement>;
|
||||
|
||||
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<EventListenerMap['scroll']>();
|
||||
|
||||
constructor(private targetRef: ElementRef<HTMLElement>, private ngZone: NgZone) {}
|
||||
@ViewChild('content')
|
||||
private contentRef?: ElementRef<HTMLDivElement>;
|
||||
@ViewChild('content', { read: OverlayScrollbarsDirective })
|
||||
private osDirective?: OverlayScrollbarsDirective;
|
||||
|
||||
private mergedEvents(originalEvents: OverlayScrollbarsComponent['events']) {
|
||||
constructor(private targetRef: ElementRef<HTMLElement>) {}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 {}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './overlayscrollbars.component';
|
||||
export * from './overlayscrollbars.directive';
|
||||
export * from './overlayscrollbars.module';
|
||||
|
||||
@@ -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: `
|
||||
<div
|
||||
[overlay-scrollbars]
|
||||
[overlay-scrollbars-component]
|
||||
[options]="options"
|
||||
[events]="events"
|
||||
(osInitialized)="onInitialized($event)"
|
||||
@@ -17,7 +21,7 @@ import type { EventListenerMap } from 'overlayscrollbars';
|
||||
(osScroll)="onScroll($event)"
|
||||
[ngClass]="clazz"
|
||||
[ngStyle]="style"
|
||||
#ref="overlayScrollbars"
|
||||
#ref
|
||||
>
|
||||
hello <span>angular</span>
|
||||
<div *ngIf="children === 0" id="empty">empty</div>
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user