diff options
Diffstat (limited to 'apps/web/src/lib/commands/timeline/element/split-elements.ts')
| -rw-r--r-- | apps/web/src/lib/commands/timeline/element/split-elements.ts | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/apps/web/src/lib/commands/timeline/element/split-elements.ts b/apps/web/src/lib/commands/timeline/element/split-elements.ts new file mode 100644 index 00000000..13d4d730 --- /dev/null +++ b/apps/web/src/lib/commands/timeline/element/split-elements.ts @@ -0,0 +1,133 @@ +import { Command } from "@/lib/commands/base-command"; +import type { TimelineTrack } from "@/types/timeline"; +import { generateUUID } from "@/utils/id"; +import { EditorCore } from "@/core"; + +export class SplitElementsCommand extends Command { + private savedState: TimelineTrack[] | null = null; + private rightSideElements: { trackId: string; elementId: string }[] = []; + private previousSelection: { trackId: string; elementId: string }[] = []; + + constructor( + private elements: { trackId: string; elementId: string }[], + private splitTime: number, + private retainSide: "both" | "left" | "right" = "both", + ) { + super(); + } + + getRightSideElements(): { trackId: string; elementId: string }[] { + return this.rightSideElements; + } + + execute(): void { + const editor = EditorCore.getInstance(); + this.savedState = editor.timeline.getTracks(); + this.previousSelection = editor.selection.getSelectedElements(); + this.rightSideElements = []; + + const updatedTracks = this.savedState.map((track) => { + const elementsToSplit = this.elements.filter( + (el) => el.trackId === track.id, + ); + + if (elementsToSplit.length === 0) { + return track; + } + + return { + ...track, + elements: track.elements.flatMap((element) => { + const shouldSplit = elementsToSplit.some( + (el) => el.elementId === element.id, + ); + + if (!shouldSplit) { + return [element]; + } + + const effectiveStart = element.startTime; + const effectiveEnd = element.startTime + element.duration; + + if ( + this.splitTime <= effectiveStart || + this.splitTime >= effectiveEnd + ) { + return [element]; + } + + const relativeTime = this.splitTime - element.startTime; + const leftVisibleDuration = relativeTime; + const rightVisibleDuration = element.duration - relativeTime; + + if (this.retainSide === "left") { + return [ + { + ...element, + duration: leftVisibleDuration, + trimEnd: element.trimEnd + rightVisibleDuration, + name: `${element.name} (left)`, + }, + ]; + } + + if (this.retainSide === "right") { + const newId = generateUUID(); + this.rightSideElements.push({ + trackId: track.id, + elementId: newId, + }); + return [ + { + ...element, + id: newId, + startTime: this.splitTime, + duration: rightVisibleDuration, + trimStart: element.trimStart + leftVisibleDuration, + name: `${element.name} (right)`, + }, + ]; + } + + // "both" - split into two pieces + const secondElementId = generateUUID(); + this.rightSideElements.push({ + trackId: track.id, + elementId: secondElementId, + }); + + return [ + { + ...element, + duration: leftVisibleDuration, + trimEnd: element.trimEnd + rightVisibleDuration, + name: `${element.name} (left)`, + }, + { + ...element, + id: secondElementId, + startTime: this.splitTime, + duration: rightVisibleDuration, + trimStart: element.trimStart + leftVisibleDuration, + name: `${element.name} (right)`, + }, + ]; + }), + } as typeof track; + }); + + editor.timeline.updateTracks(updatedTracks); + + if (this.rightSideElements.length > 0) { + editor.selection.setSelectedElements({ elements: this.rightSideElements }); + } + } + + undo(): void { + if (this.savedState) { + const editor = EditorCore.getInstance(); + editor.timeline.updateTracks(this.savedState); + editor.selection.setSelectedElements({ elements: this.previousSelection }); + } + } +} |
