Skip to content

Instantly share code, notes, and snippets.

@sod
Last active November 24, 2023 12:11
Show Gist options
  • Save sod/26431ae994311e226829d57b7da827c9 to your computer and use it in GitHub Desktop.
Save sod/26431ae994311e226829d57b7da827c9 to your computer and use it in GitHub Desktop.
import {Directive, ElementRef, Input} from '@angular/core';
import {Observable, Subscription, of} from 'rxjs';
import {share} from 'rxjs/operators';
/**
* Adds the css class `active-animate` to the element, if given expression is truthy, but after a setTimeout without triggering change detection
* This allows to animate an element entering the view, as you can't have an animation on an element that was just added to the DOM
*
* Example:
*
* <div class="example" class-active-animate>Hello World</div>
* <div class="example" [class-active-animate]="active">Hello World</div>
*
* <style>
* .example {
* // initial animation state of the element
* will-change: opacity;
* opacity: 0;
* transition: opacity 1s linear;
*
* .active-animate {
* // applies after a setTimeout
* opacity: 1;
* }
* }
* </style>
*/
let environment_ssr = false;
@Directive({
selector: '[class-active-animate]',
})
export class ClassActiveAnimateDirective {
private static className = 'active-animate';
private active = false;
private timeout?: Subscription;
private immediateForceReflow$ = environment_ssr
? of(undefined)
: new Observable<void>((subscriber) => {
const timeout = setTimeout(() => {
ClassActiveAnimateDirective.forceReflow(document.body);
subscriber.next(undefined);
subscriber.complete();
}, 1);
return () => {
clearTimeout(timeout);
};
}).pipe(share());
@Input('class-active-animate')
set activate(value: boolean | undefined) {
if (environment_ssr || value === this.active || (!this.active && value === undefined)) {
return;
}
this.timeout?.unsubscribe();
this.timeout = undefined;
this.active = !this.active;
if (this.active) {
this.timeout = this.immediateForceReflow$.subscribe(() => {
this.timeout = undefined;
this.elementRef.nativeElement.className += ' ' + ClassActiveAnimateDirective.className;
});
}
if (!this.active) {
this.elementRef.nativeElement.classList.remove(ClassActiveAnimateDirective.className);
}
}
constructor(private elementRef: ElementRef) {}
private static forceReflow(element: HTMLElement): number {
return element.offsetWidth;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment