chore: cleanup
This commit is contained in:
parent
2ee0a44735
commit
b8d08df244
|
|
@ -1,200 +0,0 @@
|
|||
import type {
|
||||
AnimationBindingKind,
|
||||
AnimationInterpolation,
|
||||
AnimationPropertyPath,
|
||||
AnimationValue,
|
||||
NumericSpec,
|
||||
} from "@/animation/types";
|
||||
import {
|
||||
coerceParamValue,
|
||||
getParamDefaultInterpolation,
|
||||
getParamNumericRange,
|
||||
getParamValueKind,
|
||||
type ParamDefinition,
|
||||
} from "@/params";
|
||||
import {
|
||||
getBuiltInElementParams,
|
||||
getElementParam,
|
||||
readElementParamValue,
|
||||
writeElementParamValue,
|
||||
} from "@/params/registry";
|
||||
import type { ElementType, TimelineElement } from "@/timeline";
|
||||
|
||||
export interface AnimationPropertyDefinition {
|
||||
kind: AnimationBindingKind;
|
||||
defaultInterpolation: AnimationInterpolation;
|
||||
numericRanges?: Partial<Record<string, NumericSpec>>;
|
||||
supportsElement: ({ element }: { element: TimelineElement }) => boolean;
|
||||
getValue: ({ element }: { element: TimelineElement }) => AnimationValue | null;
|
||||
coerceValue: ({ value }: { value: AnimationValue }) => AnimationValue | null;
|
||||
applyValue: ({
|
||||
element,
|
||||
value,
|
||||
}: {
|
||||
element: TimelineElement;
|
||||
value: AnimationValue;
|
||||
}) => TimelineElement;
|
||||
}
|
||||
|
||||
function getFallbackParam({
|
||||
propertyPath,
|
||||
}: {
|
||||
propertyPath: AnimationPropertyPath;
|
||||
}): ParamDefinition | null {
|
||||
const elementTypes: ElementType[] = [
|
||||
"video",
|
||||
"image",
|
||||
"text",
|
||||
"sticker",
|
||||
"graphic",
|
||||
"audio",
|
||||
];
|
||||
for (const type of elementTypes) {
|
||||
const param =
|
||||
getBuiltInElementParams({ type }).find(
|
||||
(candidate) => candidate.key === propertyPath,
|
||||
) ?? null;
|
||||
if (param) {
|
||||
return param;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function buildDefinition({
|
||||
propertyPath,
|
||||
element,
|
||||
}: {
|
||||
propertyPath: AnimationPropertyPath;
|
||||
element?: TimelineElement;
|
||||
}): AnimationPropertyDefinition | null {
|
||||
const param = element
|
||||
? getElementParam({ element, key: propertyPath })
|
||||
: getFallbackParam({ propertyPath });
|
||||
if (!param || param.keyframable === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const range = getParamNumericRange({ param });
|
||||
|
||||
return {
|
||||
kind: getParamValueKind({ param }),
|
||||
defaultInterpolation: getParamDefaultInterpolation({ param }),
|
||||
numericRanges: range ? { value: range } : undefined,
|
||||
supportsElement: ({ element: candidate }) =>
|
||||
getElementParam({ element: candidate, key: propertyPath }) !== null,
|
||||
getValue: ({ element: candidate }) =>
|
||||
readElementParamValue({ element: candidate, param }),
|
||||
coerceValue: ({ value }) => coerceParamValue({ param, value }),
|
||||
applyValue: ({ element: candidate, value }) => {
|
||||
const targetParam = getElementParam({
|
||||
element: candidate,
|
||||
key: propertyPath,
|
||||
});
|
||||
if (!targetParam) {
|
||||
return candidate;
|
||||
}
|
||||
const coercedValue = coerceParamValue({
|
||||
param: targetParam,
|
||||
value,
|
||||
});
|
||||
if (coercedValue === null) {
|
||||
return candidate;
|
||||
}
|
||||
return writeElementParamValue({
|
||||
element: candidate,
|
||||
param: targetParam,
|
||||
value: coercedValue,
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function isAnimationPropertyPath(
|
||||
propertyPath: string,
|
||||
): propertyPath is AnimationPropertyPath {
|
||||
return !propertyPath.startsWith("params.") && !propertyPath.startsWith("effects.");
|
||||
}
|
||||
|
||||
export function getAnimationPropertyDefinition({
|
||||
propertyPath,
|
||||
element,
|
||||
}: {
|
||||
propertyPath: AnimationPropertyPath;
|
||||
element?: TimelineElement;
|
||||
}): AnimationPropertyDefinition {
|
||||
const definition = buildDefinition({ propertyPath, element });
|
||||
if (!definition) {
|
||||
throw new Error(`Unknown animation property for element: ${propertyPath}`);
|
||||
}
|
||||
return definition;
|
||||
}
|
||||
|
||||
export function supportsAnimationProperty({
|
||||
element,
|
||||
propertyPath,
|
||||
}: {
|
||||
element: TimelineElement;
|
||||
propertyPath: AnimationPropertyPath;
|
||||
}): boolean {
|
||||
return getElementParam({ element, key: propertyPath }) !== null;
|
||||
}
|
||||
|
||||
export function getElementBaseValueForProperty({
|
||||
element,
|
||||
propertyPath,
|
||||
}: {
|
||||
element: TimelineElement;
|
||||
propertyPath: AnimationPropertyPath;
|
||||
}): AnimationValue | null {
|
||||
const param = getElementParam({ element, key: propertyPath });
|
||||
if (!param) {
|
||||
return null;
|
||||
}
|
||||
return readElementParamValue({ element, param });
|
||||
}
|
||||
|
||||
export function withElementBaseValueForProperty({
|
||||
element,
|
||||
propertyPath,
|
||||
value,
|
||||
}: {
|
||||
element: TimelineElement;
|
||||
propertyPath: AnimationPropertyPath;
|
||||
value: AnimationValue;
|
||||
}): TimelineElement {
|
||||
const param = getElementParam({ element, key: propertyPath });
|
||||
if (!param) {
|
||||
return element;
|
||||
}
|
||||
const coercedValue = coerceParamValue({ param, value });
|
||||
if (coercedValue === null) {
|
||||
return element;
|
||||
}
|
||||
return writeElementParamValue({ element, param, value: coercedValue });
|
||||
}
|
||||
|
||||
export function getDefaultInterpolationForProperty({
|
||||
propertyPath,
|
||||
element,
|
||||
}: {
|
||||
propertyPath: AnimationPropertyPath;
|
||||
element?: TimelineElement;
|
||||
}): AnimationInterpolation {
|
||||
return getAnimationPropertyDefinition({ propertyPath, element })
|
||||
.defaultInterpolation;
|
||||
}
|
||||
|
||||
export function coerceAnimationValueForProperty({
|
||||
propertyPath,
|
||||
value,
|
||||
element,
|
||||
}: {
|
||||
propertyPath: AnimationPropertyPath;
|
||||
value: AnimationValue;
|
||||
element?: TimelineElement;
|
||||
}): AnimationValue | null {
|
||||
return getAnimationPropertyDefinition({ propertyPath, element }).coerceValue({
|
||||
value,
|
||||
});
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ import { transformProjectV28ToV29 } from "../transformers/v28-to-v29";
|
|||
import { asRecord, asRecordArray } from "./helpers";
|
||||
|
||||
describe("V28 to V29 Migration", () => {
|
||||
test("moves built-in element fields into params", () => {
|
||||
test("copies built-in element fields into params without deleting source fields", () => {
|
||||
const result = transformProjectV28ToV29({
|
||||
project: {
|
||||
id: "project-v28-builtins",
|
||||
|
|
@ -83,10 +83,15 @@ describe("V28 to V29 Migration", () => {
|
|||
const tracks = asRecord(scene.tracks);
|
||||
const main = asRecord(tracks.main);
|
||||
const video = asRecordArray(main.elements)[0];
|
||||
expect(video.transform).toBeUndefined();
|
||||
expect(video.opacity).toBeUndefined();
|
||||
expect(video.volume).toBeUndefined();
|
||||
expect(video.muted).toBeUndefined();
|
||||
expect(video.transform).toEqual({
|
||||
position: { x: 12, y: -4 },
|
||||
scaleX: 1.25,
|
||||
scaleY: 0.75,
|
||||
rotate: 9,
|
||||
});
|
||||
expect(video.opacity).toBe(0.5);
|
||||
expect(video.volume).toBe(-6);
|
||||
expect(video.muted).toBe(true);
|
||||
expect(video.params).toEqual({
|
||||
"transform.positionX": 12,
|
||||
"transform.positionY": -4,
|
||||
|
|
@ -101,8 +106,13 @@ describe("V28 to V29 Migration", () => {
|
|||
|
||||
const overlay = asRecordArray(tracks.overlay)[0];
|
||||
const text = asRecordArray(asRecord(overlay).elements)[0];
|
||||
expect(text.content).toBeUndefined();
|
||||
expect(text.background).toBeUndefined();
|
||||
expect(text.content).toBe("Hello");
|
||||
expect(text.background).toEqual({
|
||||
enabled: true,
|
||||
color: "#111111",
|
||||
paddingX: 10,
|
||||
paddingY: 12,
|
||||
});
|
||||
expect(text.params).toMatchObject({
|
||||
content: "Hello",
|
||||
fontSize: 20,
|
||||
|
|
|
|||
|
|
@ -114,23 +114,6 @@ function migrateElement({ element }: { element: unknown }): unknown {
|
|||
}
|
||||
|
||||
nextElement.params = params;
|
||||
delete nextElement.transform;
|
||||
delete nextElement.opacity;
|
||||
delete nextElement.blendMode;
|
||||
delete nextElement.volume;
|
||||
delete nextElement.muted;
|
||||
delete nextElement.content;
|
||||
delete nextElement.fontSize;
|
||||
delete nextElement.fontFamily;
|
||||
delete nextElement.color;
|
||||
delete nextElement.background;
|
||||
delete nextElement.textAlign;
|
||||
delete nextElement.fontWeight;
|
||||
delete nextElement.fontStyle;
|
||||
delete nextElement.textDecoration;
|
||||
delete nextElement.letterSpacing;
|
||||
delete nextElement.lineHeight;
|
||||
|
||||
return nextElement;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ import {
|
|||
} from "@/text/layout";
|
||||
import { DEFAULTS } from "@/timeline/defaults";
|
||||
import { mediaTimeFromSeconds } from "@/wasm";
|
||||
import type { CreateTextElement, TextBackground } from "@/timeline";
|
||||
import type { CreateTextElement } from "@/timeline";
|
||||
import type { TextBackground } from "@/text/background";
|
||||
import type {
|
||||
TextAlign,
|
||||
TextDecoration,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { TextBackground } from "@/timeline";
|
||||
import type { TextBackground } from "@/text/background";
|
||||
import type {
|
||||
TextAlign,
|
||||
TextDecoration,
|
||||
|
|
|
|||
|
|
@ -1,2 +1,12 @@
|
|||
export const CORNER_RADIUS_MIN = 0;
|
||||
export const CORNER_RADIUS_MAX = 100;
|
||||
|
||||
export interface TextBackground {
|
||||
enabled: boolean;
|
||||
color: string;
|
||||
cornerRadius?: number;
|
||||
paddingX?: number;
|
||||
paddingY?: number;
|
||||
offsetX?: number;
|
||||
offsetY?: number;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { TextBackground } from "@/timeline";
|
||||
import type { TextBackground } from "@/text/background";
|
||||
import { DEFAULTS } from "@/timeline/defaults";
|
||||
import type { TextAlign } from "@/text/primitives";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { CORNER_RADIUS_MIN } from "@/text/background";
|
||||
import { DEFAULTS } from "@/timeline/defaults";
|
||||
import type { TextBackground, TextElement } from "@/timeline";
|
||||
import type { TextElement } from "@/timeline";
|
||||
import type { TextBackground } from "@/text/background";
|
||||
import { resolveNumberAtTime } from "@/animation/values";
|
||||
import {
|
||||
getTextVisualRect,
|
||||
|
|
|
|||
|
|
@ -23,9 +23,6 @@ import {
|
|||
} from "@/params";
|
||||
import {
|
||||
getElementParam,
|
||||
readElementParamValue,
|
||||
writeElementParamValue,
|
||||
type ElementParamDefinition,
|
||||
} from "@/params/registry";
|
||||
import type { TimelineElement } from "@/timeline";
|
||||
import { isVisualElement } from "@/timeline/element-utils";
|
||||
|
|
@ -96,35 +93,14 @@ function buildElementParamDescriptor({
|
|||
return null;
|
||||
}
|
||||
|
||||
return buildTimelineElementParamDescriptor({ element, param });
|
||||
}
|
||||
|
||||
function buildTimelineElementParamDescriptor({
|
||||
element,
|
||||
param,
|
||||
}: {
|
||||
element: TimelineElement;
|
||||
param: ElementParamDefinition;
|
||||
}): AnimationPathDescriptor | null {
|
||||
if (param.keyframable === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
kind: getParamValueKind({ param }),
|
||||
defaultInterpolation: getParamDefaultInterpolation({ param }),
|
||||
numericRanges: paramNumericRanges({ param }),
|
||||
coerceValue: ({ value }) => coerceParamValue({ param, value }),
|
||||
getBaseValue: () => readElementParamValue({ element, param }),
|
||||
setBaseValue: ({ value }) => {
|
||||
const coercedValue = coerceParamValue({ param, value });
|
||||
if (coercedValue === null) {
|
||||
return element;
|
||||
}
|
||||
|
||||
return writeElementParamValue({ element, param, value: coercedValue });
|
||||
},
|
||||
};
|
||||
return buildParamDescriptor({
|
||||
param,
|
||||
baseParams: element.params,
|
||||
setParams: (params) => ({
|
||||
...element,
|
||||
params,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
function buildGraphicParamDescriptor({
|
||||
|
|
|
|||
|
|
@ -117,8 +117,6 @@ interface BaseTimelineElement {
|
|||
export interface VideoElement extends BaseTimelineElement {
|
||||
type: "video";
|
||||
mediaId: string;
|
||||
volume?: number;
|
||||
muted?: boolean;
|
||||
isSourceAudioEnabled?: boolean;
|
||||
hidden?: boolean;
|
||||
retime?: RetimeConfig;
|
||||
|
|
@ -134,16 +132,6 @@ export interface ImageElement extends BaseTimelineElement {
|
|||
masks?: Mask[];
|
||||
}
|
||||
|
||||
export interface TextBackground {
|
||||
enabled: boolean;
|
||||
color: string;
|
||||
cornerRadius?: number;
|
||||
paddingX?: number;
|
||||
paddingY?: number;
|
||||
offsetX?: number;
|
||||
offsetY?: number;
|
||||
}
|
||||
|
||||
export interface TextElement extends BaseTimelineElement {
|
||||
type: "text";
|
||||
hidden?: boolean;
|
||||
|
|
|
|||
Loading…
Reference in New Issue