Files
Joey Perrott bbbe477f47 refactor: migrate animations to prettier formatting (#53977)
Migrate formatting to prettier for animations from clang-format

PR Close #53977
2024-01-19 20:08:57 +01:00

143 lines
4.8 KiB
TypeScript

/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {AnimationTriggerMetadata} from '@angular/animations';
import type {NgZone, Renderer2, RendererFactory2, RendererType2} from '@angular/core';
import {AnimationEngine} from './animation_engine_next';
import {AnimationRenderer, BaseAnimationRenderer} from './renderer';
// Define a recursive type to allow for nested arrays of `AnimationTriggerMetadata`. Note that an
// interface declaration is used as TypeScript prior to 3.7 does not support recursive type
// references, see https://github.com/microsoft/TypeScript/pull/33050 for details.
type NestedAnimationTriggerMetadata = AnimationTriggerMetadata | RecursiveAnimationTriggerMetadata;
interface RecursiveAnimationTriggerMetadata extends Array<NestedAnimationTriggerMetadata> {}
export class AnimationRendererFactory implements RendererFactory2 {
private _currentId: number = 0;
private _microtaskId: number = 1;
private _animationCallbacksBuffer: [(e: any) => any, any][] = [];
private _rendererCache = new Map<Renderer2, BaseAnimationRenderer>();
private _cdRecurDepth = 0;
constructor(
private delegate: RendererFactory2,
private engine: AnimationEngine,
private _zone: NgZone,
) {
engine.onRemovalComplete = (element: any, delegate: Renderer2) => {
// Note: if a component element has a leave animation, and a host leave animation,
// the view engine will call `removeChild` for the parent
// component renderer as well as for the child component renderer.
// Therefore, we need to check if we already removed the element.
const parentNode = delegate?.parentNode(element);
if (parentNode) {
delegate.removeChild(parentNode, element);
}
};
}
createRenderer(hostElement: any, type: RendererType2): BaseAnimationRenderer {
const EMPTY_NAMESPACE_ID = '';
// cache the delegates to find out which cached delegate can
// be used by which cached renderer
const delegate = this.delegate.createRenderer(hostElement, type);
if (!hostElement || !type?.data?.['animation']) {
const cache = this._rendererCache;
let renderer: BaseAnimationRenderer | undefined = cache.get(delegate);
if (!renderer) {
// Ensure that the renderer is removed from the cache on destroy
// since it may contain references to detached DOM nodes.
const onRendererDestroy = () => cache.delete(delegate);
renderer = new BaseAnimationRenderer(
EMPTY_NAMESPACE_ID,
delegate,
this.engine,
onRendererDestroy,
);
// only cache this result when the base renderer is used
cache.set(delegate, renderer);
}
return renderer;
}
const componentId = type.id;
const namespaceId = type.id + '-' + this._currentId;
this._currentId++;
this.engine.register(namespaceId, hostElement);
const registerTrigger = (trigger: NestedAnimationTriggerMetadata) => {
if (Array.isArray(trigger)) {
trigger.forEach(registerTrigger);
} else {
this.engine.registerTrigger(componentId, namespaceId, hostElement, trigger.name, trigger);
}
};
const animationTriggers = type.data['animation'] as NestedAnimationTriggerMetadata[];
animationTriggers.forEach(registerTrigger);
return new AnimationRenderer(this, namespaceId, delegate, this.engine);
}
begin() {
this._cdRecurDepth++;
if (this.delegate.begin) {
this.delegate.begin();
}
}
private _scheduleCountTask() {
queueMicrotask(() => {
this._microtaskId++;
});
}
/** @internal */
scheduleListenerCallback(count: number, fn: (e: any) => any, data: any) {
if (count >= 0 && count < this._microtaskId) {
this._zone.run(() => fn(data));
return;
}
const animationCallbacksBuffer = this._animationCallbacksBuffer;
if (animationCallbacksBuffer.length == 0) {
queueMicrotask(() => {
this._zone.run(() => {
animationCallbacksBuffer.forEach((tuple) => {
const [fn, data] = tuple;
fn(data);
});
this._animationCallbacksBuffer = [];
});
});
}
animationCallbacksBuffer.push([fn, data]);
}
end() {
this._cdRecurDepth--;
// this is to prevent animations from running twice when an inner
// component does CD when a parent component instead has inserted it
if (this._cdRecurDepth == 0) {
this._zone.runOutsideAngular(() => {
this._scheduleCountTask();
this.engine.flush(this._microtaskId);
});
}
if (this.delegate.end) {
this.delegate.end();
}
}
whenRenderingDone(): Promise<any> {
return this.engine.whenRenderingDone();
}
}