angular/packages/animations/browser/src/render/shared.ts
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

229 lines
6.4 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 {
AnimationEvent,
AnimationPlayer,
AUTO_STYLE,
NoopAnimationPlayer,
ɵAnimationGroupPlayer,
ɵPRE_STYLE as PRE_STYLE,
ɵStyleDataMap,
} from '@angular/animations';
import {AnimationStyleNormalizer} from '../../src/dsl/style_normalization/animation_style_normalizer';
import {animationFailed} from '../error_helpers';
import {ANIMATABLE_PROP_SET} from './web_animations/animatable_props_set';
export function optimizeGroupPlayer(players: AnimationPlayer[]): AnimationPlayer {
switch (players.length) {
case 0:
return new NoopAnimationPlayer();
case 1:
return players[0];
default:
return new ɵAnimationGroupPlayer(players);
}
}
export function normalizeKeyframes(
normalizer: AnimationStyleNormalizer,
keyframes: Array<ɵStyleDataMap>,
preStyles: ɵStyleDataMap = new Map(),
postStyles: ɵStyleDataMap = new Map(),
): Array<ɵStyleDataMap> {
const errors: Error[] = [];
const normalizedKeyframes: Array<ɵStyleDataMap> = [];
let previousOffset = -1;
let previousKeyframe: ɵStyleDataMap | null = null;
keyframes.forEach((kf) => {
const offset = kf.get('offset') as number;
const isSameOffset = offset == previousOffset;
const normalizedKeyframe: ɵStyleDataMap = (isSameOffset && previousKeyframe) || new Map();
kf.forEach((val, prop) => {
let normalizedProp = prop;
let normalizedValue = val;
if (prop !== 'offset') {
normalizedProp = normalizer.normalizePropertyName(normalizedProp, errors);
switch (normalizedValue) {
case PRE_STYLE:
normalizedValue = preStyles.get(prop)!;
break;
case AUTO_STYLE:
normalizedValue = postStyles.get(prop)!;
break;
default:
normalizedValue = normalizer.normalizeStyleValue(
prop,
normalizedProp,
normalizedValue,
errors,
);
break;
}
}
normalizedKeyframe.set(normalizedProp, normalizedValue);
});
if (!isSameOffset) {
normalizedKeyframes.push(normalizedKeyframe);
}
previousKeyframe = normalizedKeyframe;
previousOffset = offset;
});
if (errors.length) {
throw animationFailed(errors);
}
return normalizedKeyframes;
}
export function listenOnPlayer(
player: AnimationPlayer,
eventName: string,
event: AnimationEvent | undefined,
callback: (event: any) => any,
) {
switch (eventName) {
case 'start':
player.onStart(() => callback(event && copyAnimationEvent(event, 'start', player)));
break;
case 'done':
player.onDone(() => callback(event && copyAnimationEvent(event, 'done', player)));
break;
case 'destroy':
player.onDestroy(() => callback(event && copyAnimationEvent(event, 'destroy', player)));
break;
}
}
export function copyAnimationEvent(
e: AnimationEvent,
phaseName: string,
player: AnimationPlayer,
): AnimationEvent {
const totalTime = player.totalTime;
const disabled = (player as any).disabled ? true : false;
const event = makeAnimationEvent(
e.element,
e.triggerName,
e.fromState,
e.toState,
phaseName || e.phaseName,
totalTime == undefined ? e.totalTime : totalTime,
disabled,
);
const data = (e as any)['_data'];
if (data != null) {
(event as any)['_data'] = data;
}
return event;
}
export function makeAnimationEvent(
element: any,
triggerName: string,
fromState: string,
toState: string,
phaseName: string = '',
totalTime: number = 0,
disabled?: boolean,
): AnimationEvent {
return {element, triggerName, fromState, toState, phaseName, totalTime, disabled: !!disabled};
}
export function getOrSetDefaultValue<T, V>(map: Map<T, V>, key: T, defaultValue: V) {
let value = map.get(key);
if (!value) {
map.set(key, (value = defaultValue));
}
return value;
}
export function parseTimelineCommand(command: string): [string, string] {
const separatorPos = command.indexOf(':');
const id = command.substring(1, separatorPos);
const action = command.slice(separatorPos + 1);
return [id, action];
}
const documentElement: HTMLElement | null = /* @__PURE__ */ (() =>
typeof document === 'undefined' ? null : document.documentElement)();
export function getParentElement(element: any): unknown | null {
const parent = element.parentNode || element.host || null; // consider host to support shadow DOM
if (parent === documentElement) {
return null;
}
return parent;
}
function containsVendorPrefix(prop: string): boolean {
// Webkit is the only real popular vendor prefix nowadays
// cc: http://shouldiprefix.com/
return prop.substring(1, 6) == 'ebkit'; // webkit or Webkit
}
let _CACHED_BODY: {style: any} | null = null;
let _IS_WEBKIT = false;
export function validateStyleProperty(prop: string): boolean {
if (!_CACHED_BODY) {
_CACHED_BODY = getBodyNode() || {};
_IS_WEBKIT = _CACHED_BODY!.style ? 'WebkitAppearance' in _CACHED_BODY!.style : false;
}
let result = true;
if (_CACHED_BODY!.style && !containsVendorPrefix(prop)) {
result = prop in _CACHED_BODY!.style;
if (!result && _IS_WEBKIT) {
const camelProp = 'Webkit' + prop.charAt(0).toUpperCase() + prop.slice(1);
result = camelProp in _CACHED_BODY!.style;
}
}
return result;
}
export function validateWebAnimatableStyleProperty(prop: string): boolean {
return ANIMATABLE_PROP_SET.has(prop);
}
export function getBodyNode(): any | null {
if (typeof document != 'undefined') {
return document.body;
}
return null;
}
export function containsElement(elm1: any, elm2: any): boolean {
while (elm2) {
if (elm2 === elm1) {
return true;
}
elm2 = getParentElement(elm2);
}
return false;
}
export function invokeQuery(element: any, selector: string, multi: boolean): any[] {
if (multi) {
return Array.from(element.querySelectorAll(selector));
}
const elem = element.querySelector(selector);
return elem ? [elem] : [];
}
export function hypenatePropsKeys(original: ɵStyleDataMap): ɵStyleDataMap {
const newMap: ɵStyleDataMap = new Map();
original.forEach((val, prop) => {
const newProp = prop.replace(/([a-z])([A-Z])/g, '$1-$2');
newMap.set(newProp, val);
});
return newMap;
}