diff options
Diffstat (limited to 'legacy/sections/transform.tsx')
| -rw-r--r-- | legacy/sections/transform.tsx | 382 |
1 files changed, 0 insertions, 382 deletions
diff --git a/legacy/sections/transform.tsx b/legacy/sections/transform.tsx deleted file mode 100644 index f9d68065..00000000 --- a/legacy/sections/transform.tsx +++ /dev/null @@ -1,382 +0,0 @@ -import { NumberField } from "@/components/ui/number-field";
-import { useEditor } from "@/hooks/use-editor";
-import { clamp, isNearlyEqual } from "@/utils/math";
-import type { AnimationPropertyPath } from "@/types/animation";
-import type { VisualElement } from "@/types/timeline";
-import {
- Section,
- SectionContent,
- SectionField,
- SectionFields,
- SectionHeader,
- SectionTitle,
-} from "../section";
-import { Button } from "@/components/ui/button";
-import { HugeiconsIcon } from "@hugeicons/react";
-import {
- ArrowExpandIcon,
- Link05Icon,
- RotateClockwiseIcon,
-} from "@hugeicons/core-free-icons";
-import { useState } from "react";
-import { DEFAULT_TRANSFORM } from "@/constants/timeline-constants";
-import { TIME_EPSILON_SECONDS } from "@/constants/animation-constants";
-import { getElementLocalTime, resolveTransformAtTime } from "@/lib/animation";
-import { KeyframeToggle } from "../keyframe-toggle";
-import { useKeyframedNumberProperty } from "../hooks/use-keyframed-number-property";
-
-export function parseNumericInput({ input }: { input: string }): number | null {
- const parsed = parseFloat(input);
- return Number.isNaN(parsed) ? null : parsed;
-}
-
-export function isPropertyAtDefault({
- hasAnimatedKeyframes,
- isPlayheadWithinElementRange,
- resolvedValue,
- staticValue,
- defaultValue,
-}: {
- hasAnimatedKeyframes: boolean;
- isPlayheadWithinElementRange: boolean;
- resolvedValue: number;
- staticValue: number;
- defaultValue: number;
-}): boolean {
- if (hasAnimatedKeyframes && isPlayheadWithinElementRange) {
- return isNearlyEqual({
- leftValue: resolvedValue,
- rightValue: defaultValue,
- });
- }
-
- return staticValue === defaultValue;
-}
-
-export function TransformSection({
- element,
- trackId,
- showTopBorder = true,
-}: {
- element: VisualElement;
- trackId: string;
- showTopBorder?: boolean;
-}) {
- const editor = useEditor();
- const [isScaleLocked, setIsScaleLocked] = useState(false);
- const playheadTime = editor.playback.getCurrentTime();
- const localTime = getElementLocalTime({
- timelineTime: playheadTime,
- elementStartTime: element.startTime,
- elementDuration: element.duration,
- });
- const resolvedTransform = resolveTransformAtTime({
- baseTransform: element.transform,
- animations: element.animations,
- localTime,
- });
- const isPlayheadWithinElementRange =
- playheadTime >= element.startTime - TIME_EPSILON_SECONDS &&
- playheadTime <= element.startTime + element.duration + TIME_EPSILON_SECONDS;
-
- const positionX = useKeyframedNumberProperty({
- trackId,
- elementId: element.id,
- animations: element.animations,
- propertyPath: "transform.position.x",
- localTime,
- isPlayheadWithinElementRange,
- displayValue: Math.round(resolvedTransform.position.x).toString(),
- parse: (input) => parseNumericInput({ input }),
- valueAtPlayhead: resolvedTransform.position.x,
- buildBaseUpdates: ({ value }) => ({
- transform: {
- ...element.transform,
- position: {
- ...element.transform.position,
- x: value,
- },
- },
- }),
- });
-
- const positionY = useKeyframedNumberProperty({
- trackId,
- elementId: element.id,
- animations: element.animations,
- propertyPath: "transform.position.y",
- localTime,
- isPlayheadWithinElementRange,
- displayValue: Math.round(resolvedTransform.position.y).toString(),
- parse: (input) => parseNumericInput({ input }),
- valueAtPlayhead: resolvedTransform.position.y,
- buildBaseUpdates: ({ value }) => ({
- transform: {
- ...element.transform,
- position: {
- ...element.transform.position,
- y: value,
- },
- },
- }),
- });
-
- const scale = useKeyframedNumberProperty({
- trackId,
- elementId: element.id,
- animations: element.animations,
- propertyPath: "transform.scale",
- localTime,
- isPlayheadWithinElementRange,
- displayValue: Math.round(resolvedTransform.scale * 100).toString(),
- parse: (input) => {
- const parsed = parseNumericInput({ input });
- if (parsed === null) return null;
- return Math.max(parsed, 1) / 100;
- },
- valueAtPlayhead: resolvedTransform.scale,
- buildBaseUpdates: ({ value }) => ({
- transform: {
- ...element.transform,
- scale: value,
- },
- }),
- });
- const scaleFieldProps = {
- className: "flex-1",
- value: scale.displayValue,
- onFocus: scale.onFocus,
- onChange: scale.onChange,
- onBlur: scale.onBlur,
- dragSensitivity: "slow" as const,
- onScrub: scale.scrubTo,
- onScrubEnd: scale.commitScrub,
- onReset: () => scale.commitValue({ value: DEFAULT_TRANSFORM.scale }),
- isDefault: isPropertyAtDefault({
- hasAnimatedKeyframes: scale.hasAnimatedKeyframes,
- isPlayheadWithinElementRange,
- resolvedValue: resolvedTransform.scale,
- staticValue: element.transform.scale,
- defaultValue: DEFAULT_TRANSFORM.scale,
- }),
- };
-
- const rotation = useKeyframedNumberProperty({
- trackId,
- elementId: element.id,
- animations: element.animations,
- propertyPath: "transform.rotate",
- localTime,
- isPlayheadWithinElementRange,
- displayValue: Math.round(resolvedTransform.rotate).toString(),
- parse: (input) => {
- const parsed = parseNumericInput({ input });
- if (parsed === null) return null;
- return clamp({ value: parsed, min: -360, max: 360 });
- },
- valueAtPlayhead: resolvedTransform.rotate,
- buildBaseUpdates: ({ value }) => ({
- transform: {
- ...element.transform,
- rotate: value,
- },
- }),
- });
-
- const hasPositionKeyframe =
- positionX.isKeyframedAtTime || positionY.isKeyframedAtTime;
-
- const togglePositionKeyframe = () => {
- if (!isPlayheadWithinElementRange) {
- return;
- }
-
- if (positionX.keyframeIdAtTime || positionY.keyframeIdAtTime) {
- const keyframesToRemove: Array<{
- trackId: string;
- elementId: string;
- propertyPath: AnimationPropertyPath;
- keyframeId: string;
- }> = [];
- if (positionX.keyframeIdAtTime) {
- keyframesToRemove.push({
- trackId,
- elementId: element.id,
- propertyPath: "transform.position.x" as const,
- keyframeId: positionX.keyframeIdAtTime,
- });
- }
- if (positionY.keyframeIdAtTime) {
- keyframesToRemove.push({
- trackId,
- elementId: element.id,
- propertyPath: "transform.position.y" as const,
- keyframeId: positionY.keyframeIdAtTime,
- });
- }
-
- editor.timeline.removeKeyframes({
- keyframes: keyframesToRemove,
- });
- return;
- }
-
- editor.timeline.upsertKeyframes({
- keyframes: [
- {
- trackId,
- elementId: element.id,
- propertyPath: "transform.position.x",
- time: localTime,
- value: resolvedTransform.position.x,
- },
- {
- trackId,
- elementId: element.id,
- propertyPath: "transform.position.y",
- time: localTime,
- value: resolvedTransform.position.y,
- },
- ],
- });
- };
-
- return (
- <Section
- collapsible
- sectionKey={`${element.type}:transform`}
- showTopBorder={showTopBorder}
- >
- <SectionHeader><SectionTitle>Transform</SectionTitle></SectionHeader>
- <SectionContent>
- <SectionFields>
- <SectionField
- label="Scale"
- beforeLabel={
- <KeyframeToggle
- isActive={scale.isKeyframedAtTime}
- isDisabled={!isPlayheadWithinElementRange}
- title="Toggle scale keyframe"
- onToggle={scale.toggleKeyframe}
- />
- }
- >
- <div className="flex items-center gap-2">
- {isScaleLocked ? (
- <>
- <NumberField icon="W" {...scaleFieldProps} />
- <NumberField icon="H" {...scaleFieldProps} />
- </>
- ) : (
- <NumberField
- icon={<HugeiconsIcon icon={ArrowExpandIcon} />}
- {...scaleFieldProps}
- className="flex-1"
- />
- )}
- <Button
- type="button"
- variant={isScaleLocked ? "secondary" : "ghost"}
- size="icon"
- aria-pressed={isScaleLocked}
- onClick={() => setIsScaleLocked((isLocked) => !isLocked)}
- >
- <HugeiconsIcon icon={Link05Icon} />
- </Button>
- </div>
- </SectionField>
- <SectionField
- label="Position"
- beforeLabel={
- <KeyframeToggle
- isActive={hasPositionKeyframe}
- isDisabled={!isPlayheadWithinElementRange}
- title="Toggle position keyframe"
- onToggle={togglePositionKeyframe}
- />
- }
- >
- <div className="flex items-center gap-2">
- <NumberField
- icon="X"
- className="flex-1"
- value={positionX.displayValue}
- onFocus={positionX.onFocus}
- onChange={positionX.onChange}
- onBlur={positionX.onBlur}
- onScrub={positionX.scrubTo}
- onScrubEnd={positionX.commitScrub}
- onReset={() =>
- positionX.commitValue({ value: DEFAULT_TRANSFORM.position.x })
- }
- isDefault={isPropertyAtDefault({
- hasAnimatedKeyframes: positionX.hasAnimatedKeyframes,
- isPlayheadWithinElementRange,
- resolvedValue: resolvedTransform.position.x,
- staticValue: element.transform.position.x,
- defaultValue: DEFAULT_TRANSFORM.position.x,
- })}
- />
- <NumberField
- icon="Y"
- className="flex-1"
- value={positionY.displayValue}
- onFocus={positionY.onFocus}
- onChange={positionY.onChange}
- onBlur={positionY.onBlur}
- onScrub={positionY.scrubTo}
- onScrubEnd={positionY.commitScrub}
- onReset={() =>
- positionY.commitValue({ value: DEFAULT_TRANSFORM.position.y })
- }
- isDefault={isPropertyAtDefault({
- hasAnimatedKeyframes: positionY.hasAnimatedKeyframes,
- isPlayheadWithinElementRange,
- resolvedValue: resolvedTransform.position.y,
- staticValue: element.transform.position.y,
- defaultValue: DEFAULT_TRANSFORM.position.y,
- })}
- />
- </div>
- </SectionField>
-
- <SectionField
- label="Rotation"
- beforeLabel={
- <KeyframeToggle
- isActive={rotation.isKeyframedAtTime}
- isDisabled={!isPlayheadWithinElementRange}
- title="Toggle rotation keyframe"
- onToggle={rotation.toggleKeyframe}
- />
- }
- >
- <div className="flex items-center gap-2">
- <NumberField
- icon={<HugeiconsIcon icon={RotateClockwiseIcon} />}
- className="flex-none"
- value={rotation.displayValue}
- onFocus={rotation.onFocus}
- onChange={rotation.onChange}
- onBlur={rotation.onBlur}
- dragSensitivity="slow"
- onScrub={rotation.scrubTo}
- onScrubEnd={rotation.commitScrub}
- onReset={() =>
- rotation.commitValue({ value: DEFAULT_TRANSFORM.rotate })
- }
- isDefault={isPropertyAtDefault({
- hasAnimatedKeyframes: rotation.hasAnimatedKeyframes,
- isPlayheadWithinElementRange,
- resolvedValue: resolvedTransform.rotate,
- staticValue: element.transform.rotate,
- defaultValue: DEFAULT_TRANSFORM.rotate,
- })}
- />
- </div>
- </SectionField>
- </SectionFields>
- </SectionContent>
- </Section>
- );
-}
|
