Files
hyperframes/packages/studio/src/components/editor/manualEditsDomPatches.ts
T
Miguel Ángel ea4d920589 refactor(studio): split oversized files and raise line limit to 600
Split PlayerControls.tsx into focused sub-components (SeekBar,
WorkAreaOverlay, MuteButton, LoopButton, FullscreenButton,
ShortcutsPanel, SpeedMenu) and extracted seek bar drag/progress
tracking into useSeekBarDrag hook.

Split manualEditsDom.ts patch-builder functions into
manualEditsDomPatches.ts with data-driven helpers to reduce
duplication and complexity.

Extracted per-type reapply helpers from reapplyPositionEditsAfterSeek
and factored out identity-matrix check from
stripGsapTranslateFromTransform.

Raised file-size limit from 500 to 600 lines, removed
.filesize-allowlist.
2026-05-22 22:57:12 -04:00

238 lines
9.0 KiB
TypeScript

import type { PatchOperation } from "../../utils/sourcePatcher";
import {
STUDIO_OFFSET_X_PROP,
STUDIO_OFFSET_Y_PROP,
STUDIO_WIDTH_PROP,
STUDIO_HEIGHT_PROP,
STUDIO_ROTATION_PROP,
STUDIO_PATH_OFFSET_ATTR,
STUDIO_BOX_SIZE_ATTR,
STUDIO_ROTATION_ATTR,
STUDIO_ROTATION_DRAFT_ATTR,
STUDIO_ORIGINAL_TRANSLATE_ATTR,
STUDIO_ORIGINAL_INLINE_TRANSLATE_ATTR,
STUDIO_ORIGINAL_WIDTH_ATTR,
STUDIO_ORIGINAL_HEIGHT_ATTR,
STUDIO_ORIGINAL_MIN_WIDTH_ATTR,
STUDIO_ORIGINAL_MIN_HEIGHT_ATTR,
STUDIO_ORIGINAL_MAX_WIDTH_ATTR,
STUDIO_ORIGINAL_MAX_HEIGHT_ATTR,
STUDIO_ORIGINAL_FLEX_BASIS_ATTR,
STUDIO_ORIGINAL_FLEX_GROW_ATTR,
STUDIO_ORIGINAL_FLEX_SHRINK_ATTR,
STUDIO_ORIGINAL_BOX_SIZING_ATTR,
STUDIO_ORIGINAL_SCALE_ATTR,
STUDIO_ORIGINAL_TRANSFORM_ORIGIN_ATTR,
STUDIO_ORIGINAL_DISPLAY_ATTR,
STUDIO_ORIGINAL_ROTATE_ATTR,
STUDIO_ORIGINAL_INLINE_ROTATE_ATTR,
STUDIO_ORIGINAL_ROTATION_TRANSFORM_ORIGIN_ATTR,
STUDIO_ORIGINAL_TRANSFORM_DISPLAY_ATTR,
} from "./manualEditsTypes";
import {
STUDIO_MOTION_ATTR,
STUDIO_MOTION_ORIGINAL_TRANSFORM_ATTR,
STUDIO_MOTION_ORIGINAL_OPACITY_ATTR,
STUDIO_MOTION_ORIGINAL_VISIBILITY_ATTR,
} from "./studioMotionTypes";
/* ── Shared helpers ──────────────────────────────────────────────── */
function collectInlineStyleOps(
element: HTMLElement,
properties: readonly string[],
ops: PatchOperation[],
): void {
for (const prop of properties) {
const val = element.style.getPropertyValue(prop);
if (val) ops.push({ type: "inline-style", property: prop, value: val });
}
}
function collectAttributeOps(
element: HTMLElement,
attrNames: readonly string[],
ops: PatchOperation[],
): void {
for (const attr of attrNames) {
const val = element.getAttribute(attr);
if (val !== null) ops.push({ type: "attribute", property: attr, value: val });
}
}
function appendTransformDisplayOps(element: HTMLElement, ops: PatchOperation[]): void {
const val = element.getAttribute(STUDIO_ORIGINAL_TRANSFORM_DISPLAY_ATTR);
if (val !== null) {
ops.push({ type: "inline-style", property: "display", value: val || null });
ops.push({ type: "attribute", property: STUDIO_ORIGINAL_TRANSFORM_DISPLAY_ATTR, value: null });
}
}
/* ── Path offset patches ─────────────────────────────────────────── */
export function buildPathOffsetPatches(element: HTMLElement): PatchOperation[] {
const ops: PatchOperation[] = [];
collectInlineStyleOps(element, [STUDIO_OFFSET_X_PROP, STUDIO_OFFSET_Y_PROP, "translate"], ops);
ops.push({ type: "attribute", property: STUDIO_PATH_OFFSET_ATTR, value: "true" });
collectAttributeOps(
element,
[STUDIO_ORIGINAL_TRANSLATE_ATTR, STUDIO_ORIGINAL_INLINE_TRANSLATE_ATTR],
ops,
);
collectInlineStyleOps(element, ["display"], ops);
collectAttributeOps(element, [STUDIO_ORIGINAL_TRANSFORM_DISPLAY_ATTR], ops);
return ops;
}
export function buildClearPathOffsetPatches(element: HTMLElement): PatchOperation[] {
const originalInlineTranslate = element.getAttribute(STUDIO_ORIGINAL_INLINE_TRANSLATE_ATTR);
const ops: PatchOperation[] = [
{ type: "inline-style", property: STUDIO_OFFSET_X_PROP, value: null },
{ type: "inline-style", property: STUDIO_OFFSET_Y_PROP, value: null },
{ type: "inline-style", property: "translate", value: originalInlineTranslate || null },
{ type: "attribute", property: STUDIO_PATH_OFFSET_ATTR, value: null },
{ type: "attribute", property: STUDIO_ORIGINAL_TRANSLATE_ATTR, value: null },
{ type: "attribute", property: STUDIO_ORIGINAL_INLINE_TRANSLATE_ATTR, value: null },
];
appendTransformDisplayOps(element, ops);
return ops;
}
/* ── Box size patches ────────────────────────────────────────────── */
const BOX_SIZE_STYLE_PROPS = [
"width",
"height",
"min-width",
"min-height",
"max-width",
"max-height",
"flex-basis",
"flex-grow",
"flex-shrink",
"box-sizing",
"scale",
"transform-origin",
"display",
] as const;
const BOX_SIZE_ORIG_ATTRS: ReadonlyArray<[string, string]> = [
[STUDIO_ORIGINAL_WIDTH_ATTR, "width"],
[STUDIO_ORIGINAL_HEIGHT_ATTR, "height"],
[STUDIO_ORIGINAL_MIN_WIDTH_ATTR, "min-width"],
[STUDIO_ORIGINAL_MIN_HEIGHT_ATTR, "min-height"],
[STUDIO_ORIGINAL_MAX_WIDTH_ATTR, "max-width"],
[STUDIO_ORIGINAL_MAX_HEIGHT_ATTR, "max-height"],
[STUDIO_ORIGINAL_FLEX_BASIS_ATTR, "flex-basis"],
[STUDIO_ORIGINAL_FLEX_GROW_ATTR, "flex-grow"],
[STUDIO_ORIGINAL_FLEX_SHRINK_ATTR, "flex-shrink"],
[STUDIO_ORIGINAL_BOX_SIZING_ATTR, "box-sizing"],
[STUDIO_ORIGINAL_SCALE_ATTR, "scale"],
[STUDIO_ORIGINAL_TRANSFORM_ORIGIN_ATTR, "transform-origin"],
[STUDIO_ORIGINAL_DISPLAY_ATTR, "display"],
[STUDIO_ORIGINAL_TRANSFORM_DISPLAY_ATTR, ""],
];
export function buildBoxSizePatches(element: HTMLElement): PatchOperation[] {
const ops: PatchOperation[] = [];
collectInlineStyleOps(element, [STUDIO_WIDTH_PROP, STUDIO_HEIGHT_PROP], ops);
collectInlineStyleOps(element, BOX_SIZE_STYLE_PROPS, ops);
ops.push({ type: "attribute", property: STUDIO_BOX_SIZE_ATTR, value: "true" });
collectAttributeOps(
element,
BOX_SIZE_ORIG_ATTRS.map(([attr]) => attr),
ops,
);
return ops;
}
export function buildClearBoxSizePatches(element: HTMLElement): PatchOperation[] {
const ops: PatchOperation[] = [
{ type: "inline-style", property: STUDIO_WIDTH_PROP, value: null },
{ type: "inline-style", property: STUDIO_HEIGHT_PROP, value: null },
{ type: "attribute", property: STUDIO_BOX_SIZE_ATTR, value: null },
];
for (const [attrName, styleProp] of BOX_SIZE_ORIG_ATTRS) {
const origVal = element.getAttribute(attrName);
if (origVal !== null && styleProp) {
ops.push({ type: "inline-style", property: styleProp, value: origVal || null });
}
ops.push({ type: "attribute", property: attrName, value: null });
}
return ops;
}
/* ── Rotation patches ────────────────────────────────────────────── */
const ROTATION_STYLE_PROPS = [
STUDIO_ROTATION_PROP,
"rotate",
"transform-origin",
"display",
] as const;
const ROTATION_ORIG_ATTRS = [
STUDIO_ORIGINAL_ROTATE_ATTR,
STUDIO_ORIGINAL_INLINE_ROTATE_ATTR,
STUDIO_ORIGINAL_ROTATION_TRANSFORM_ORIGIN_ATTR,
STUDIO_ORIGINAL_TRANSFORM_DISPLAY_ATTR,
] as const;
export function buildRotationPatches(element: HTMLElement): PatchOperation[] {
const ops: PatchOperation[] = [];
collectInlineStyleOps(element, ROTATION_STYLE_PROPS, ops);
ops.push({ type: "attribute", property: STUDIO_ROTATION_ATTR, value: "true" });
collectAttributeOps(element, ROTATION_ORIG_ATTRS, ops);
return ops;
}
export function buildClearRotationPatches(element: HTMLElement): PatchOperation[] {
const origInlineRotate = element.getAttribute(STUDIO_ORIGINAL_INLINE_ROTATE_ATTR);
const origRotationTransformOrigin = element.getAttribute(
STUDIO_ORIGINAL_ROTATION_TRANSFORM_ORIGIN_ATTR,
);
const ops: PatchOperation[] = [
{ type: "inline-style", property: STUDIO_ROTATION_PROP, value: null },
{ type: "inline-style", property: "rotate", value: origInlineRotate || null },
{
type: "inline-style",
property: "transform-origin",
value: origRotationTransformOrigin !== null ? origRotationTransformOrigin || null : null,
},
{ type: "attribute", property: STUDIO_ROTATION_ATTR, value: null },
{ type: "attribute", property: STUDIO_ROTATION_DRAFT_ATTR, value: null },
{ type: "attribute", property: STUDIO_ORIGINAL_ROTATE_ATTR, value: null },
{ type: "attribute", property: STUDIO_ORIGINAL_INLINE_ROTATE_ATTR, value: null },
{ type: "attribute", property: STUDIO_ORIGINAL_ROTATION_TRANSFORM_ORIGIN_ATTR, value: null },
];
appendTransformDisplayOps(element, ops);
return ops;
}
/* ── Motion patches ──────────────────────────────────────────────── */
const MOTION_ORIG_ATTRS = [
STUDIO_MOTION_ORIGINAL_TRANSFORM_ATTR,
STUDIO_MOTION_ORIGINAL_OPACITY_ATTR,
STUDIO_MOTION_ORIGINAL_VISIBILITY_ATTR,
] as const;
export function buildMotionPatches(element: HTMLElement): PatchOperation[] {
const motionJson = element.getAttribute(STUDIO_MOTION_ATTR);
if (!motionJson) return [];
const ops: PatchOperation[] = [
{ type: "attribute", property: STUDIO_MOTION_ATTR, value: motionJson },
];
collectAttributeOps(element, MOTION_ORIG_ATTRS, ops);
return ops;
}
export function buildClearMotionPatches(_element: HTMLElement): PatchOperation[] {
return [
{ type: "attribute", property: STUDIO_MOTION_ATTR, value: null },
{ type: "attribute", property: STUDIO_MOTION_ORIGINAL_TRANSFORM_ATTR, value: null },
{ type: "attribute", property: STUDIO_MOTION_ORIGINAL_OPACITY_ATTR, value: null },
{ type: "attribute", property: STUDIO_MOTION_ORIGINAL_VISIBILITY_ATTR, value: null },
];
}