Last active
June 6, 2019 11:13
-
-
Save m-abs/d7cd87ac539e441ebcc8025b836c29a6 to your computer and use it in GitHub Desktop.
Nativescript angular dropshow directive
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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