mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-06-21 01:50:36 +03:00
add angular directive
This commit is contained in:
@@ -7,12 +7,10 @@ import {
|
|||||||
ViewChild,
|
ViewChild,
|
||||||
ElementRef,
|
ElementRef,
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
OnChanges,
|
|
||||||
AfterViewInit,
|
AfterViewInit,
|
||||||
SimpleChanges,
|
|
||||||
NgZone,
|
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { OverlayScrollbars } from 'overlayscrollbars';
|
import { OverlayScrollbars } from 'overlayscrollbars';
|
||||||
|
import { OverlayScrollbarsDirective } from '~/overlayscrollbars.directive';
|
||||||
import type { PartialOptions, EventListeners, EventListenerMap } from 'overlayscrollbars';
|
import type { PartialOptions, EventListeners, EventListenerMap } from 'overlayscrollbars';
|
||||||
|
|
||||||
const mergeEventListeners = (emits: EventListeners, events: EventListeners) =>
|
const mergeEventListeners = (emits: EventListeners, events: EventListeners) =>
|
||||||
@@ -31,17 +29,19 @@ const mergeEventListeners = (emits: EventListeners, events: EventListeners) =>
|
|||||||
);
|
);
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: '[overlay-scrollbars]', // https://angular.io/guide/styleguide#component-selectors
|
selector: '[overlay-scrollbars-component]', // https://angular.io/guide/styleguide#component-selectors
|
||||||
exportAs: 'overlayScrollbars',
|
exportAs: 'overlayScrollbars',
|
||||||
host: { 'data-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 {
|
export class OverlayScrollbarsComponent implements OnDestroy, AfterViewInit {
|
||||||
private instanceRef: OverlayScrollbars | null = null;
|
|
||||||
|
|
||||||
@ViewChild('content')
|
|
||||||
private contentRef?: ElementRef<HTMLDivElement>;
|
|
||||||
|
|
||||||
@Input('options')
|
@Input('options')
|
||||||
options?: PartialOptions | false | null;
|
options?: PartialOptions | false | null;
|
||||||
@Input('events')
|
@Input('events')
|
||||||
@@ -56,9 +56,42 @@ export class OverlayScrollbarsComponent implements OnDestroy, OnChanges, AfterVi
|
|||||||
@Output('osScroll')
|
@Output('osScroll')
|
||||||
onScroll = new EventEmitter<EventListenerMap['scroll']>();
|
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(
|
return mergeEventListeners(
|
||||||
{
|
{
|
||||||
initialized: (...args) => this.onInitialized.emit(args),
|
initialized: (...args) => this.onInitialized.emit(args),
|
||||||
@@ -69,52 +102,4 @@ export class OverlayScrollbarsComponent implements OnDestroy, OnChanges, AfterVi
|
|||||||
originalEvents || {}
|
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 { NgModule } from '@angular/core';
|
||||||
|
import { OverlayScrollbarsDirective } from './overlayscrollbars.directive';
|
||||||
import { OverlayScrollbarsComponent } from './overlayscrollbars.component';
|
import { OverlayScrollbarsComponent } from './overlayscrollbars.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [OverlayScrollbarsComponent],
|
declarations: [OverlayScrollbarsComponent, OverlayScrollbarsDirective],
|
||||||
exports: [OverlayScrollbarsComponent],
|
exports: [OverlayScrollbarsComponent, OverlayScrollbarsDirective],
|
||||||
})
|
})
|
||||||
export class OverlayscrollbarsModule {}
|
export class OverlayscrollbarsModule {}
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
export * from './overlayscrollbars.component';
|
export * from './overlayscrollbars.component';
|
||||||
|
export * from './overlayscrollbars.directive';
|
||||||
export * from './overlayscrollbars.module';
|
export * from './overlayscrollbars.module';
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
import { Component, ViewChild } from '@angular/core';
|
import { Component, ViewChild } from '@angular/core';
|
||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
import { OverlayScrollbars } from 'overlayscrollbars';
|
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 { ComponentFixture } from '@angular/core/testing';
|
||||||
import type { EventListenerMap } from 'overlayscrollbars';
|
import type { EventListenerMap } from 'overlayscrollbars';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
<div
|
<div
|
||||||
[overlay-scrollbars]
|
[overlay-scrollbars-component]
|
||||||
[options]="options"
|
[options]="options"
|
||||||
[events]="events"
|
[events]="events"
|
||||||
(osInitialized)="onInitialized($event)"
|
(osInitialized)="onInitialized($event)"
|
||||||
@@ -17,7 +21,7 @@ import type { EventListenerMap } from 'overlayscrollbars';
|
|||||||
(osScroll)="onScroll($event)"
|
(osScroll)="onScroll($event)"
|
||||||
[ngClass]="clazz"
|
[ngClass]="clazz"
|
||||||
[ngStyle]="style"
|
[ngStyle]="style"
|
||||||
#ref="overlayScrollbars"
|
#ref
|
||||||
>
|
>
|
||||||
hello <span>angular</span>
|
hello <span>angular</span>
|
||||||
<div *ngIf="children === 0" id="empty">empty</div>
|
<div *ngIf="children === 0" id="empty">empty</div>
|
||||||
@@ -73,7 +77,7 @@ describe('OverlayscrollbarsNgxComponent', () => {
|
|||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
...new OverlayscrollbarsModule(),
|
...new OverlayscrollbarsModule(),
|
||||||
declarations: [OverlayScrollbarsComponent, Test],
|
declarations: [OverlayScrollbarsComponent, OverlayScrollbarsDirective, Test],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
||||||
fixture = TestBed.createComponent(OverlayScrollbarsComponent);
|
fixture = TestBed.createComponent(OverlayScrollbarsComponent);
|
||||||
|
|||||||
Reference in New Issue
Block a user