Skip to content

Instantly share code, notes, and snippets.

@m-abs
Last active June 6, 2019 11:13
Show Gist options
  • Save m-abs/d7cd87ac539e441ebcc8025b836c29a6 to your computer and use it in GitHub Desktop.
Save m-abs/d7cd87ac539e441ebcc8025b836c29a6 to your computer and use it in GitHub Desktop.
Nativescript angular dropshow directive
import { Directive, ElementRef, Input, OnDestroy } from '@angular/core';
import { fromEvent, merge, Observable, ReplaySubject } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
import { Color } from 'tns-core-modules/color';
import * as knownColors from 'tns-core-modules/color/known-colors';
import { isAndroid, isIOS, screen } from 'tns-core-modules/platform';
import { View } from 'tns-core-modules/ui/core/view';
/**
* Directive for adding a drop shadow to an element.
*/
@Directive({
// tslint:disable-next-line:directive-selector
selector: '[dropshadow]',
})
export class DropShadowDirective implements OnDestroy {
// NOTE: opacity only works on iOS atm.
@Input()
public dropshadowOpacity = 0.5;
@Input()
public dropshadowSize = 3;
public get nativeView(): View {
return this.el.nativeElement;
}
private get baseColor(): Color {
const nativeView = this.nativeView;
if (!nativeView || !this.nativeView.backgroundColor) {
return new Color(knownColors.White);
}
if (typeof this.nativeView.backgroundColor === 'string') {
return new Color(this.nativeView.backgroundColor);
}
if (this.nativeView.backgroundColor instanceof Color) {
return this.nativeView.backgroundColor;
}
return new Color(knownColors.White);
}
public readonly destroy$ = new ReplaySubject<boolean>(1);
constructor(private readonly el: ElementRef) {
merge(fromEvent(this.nativeView, View.loadedEvent), fromEvent(this.nativeView, View.layoutChangedEvent))
.pipe(
this.takeUntilDestroy(),
debounceTime(20),
)
.subscribe(() => this.applyDropShadow());
}
public ngOnDestroy() {
this.destroy$.next(true);
}
public takeUntilDestroy<T>() {
const destroy$ = this.destroy$;
// This should have been a `takeUntil(this.destroy$)` but it kept emitting after destroy...
return function(source: Observable<T>) {
return new Observable<T>(function(subscriber) {
const sub = source.subscribe(subscriber);
const destroySub = destroy$.subscribe(function() {
// Extra check here because HMR-mode logged errors about them missing
if (sub) {
sub.unsubscribe();
}
if (subscriber) {
subscriber.complete();
}
if (destroySub) {
destroySub.unsubscribe();
}
});
return () => {
sub.unsubscribe();
destroySub.unsubscribe();
};
});
};
}
private applyDropShadow() {
if (isAndroid) {
// TODO: shadowOpacity on Android
// NOTE: Determined that x2 scale gives somewhat same result as on iOS
const androidView = this.nativeView && (this.nativeView.android as android.view.View);
if (!androidView) {
return;
}
const compat = android.support.v4.view.ViewCompat;
if (!compat) {
console.error(`Pipe.DropShadow: no compat library`);
return;
}
if (!androidView.getBackground()) {
androidView.setBackground(new android.graphics.drawable.ColorDrawable(this.baseColor.android));
}
compat.setElevation(androidView, this.dropshadowSize * screen.mainScreen.scale * 2);
return;
}
if (isIOS) {
const uiView = this.nativeView.ios as UIView;
if (!uiView) {
return;
}
uiView.layer.masksToBounds = false;
uiView.layer.shadowOpacity = this.dropshadowOpacity;
uiView.layer.shadowRadius = this.dropshadowSize;
uiView.layer.shadowColor = UIColor.blackColor.CGColor;
uiView.layer.shadowOffset = CGSizeMake(this.dropshadowSize, this.dropshadowSize);
return;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment