summaryrefslogtreecommitdiff
path: root/apps/web/src/components/editor/timeline-track.tsx
diff options
context:
space:
mode:
authorMaze <167211895+mazeincoding@users.noreply.github.com>2025-07-15 14:17:50 +0200
committerGitHub <noreply@github.com>2025-07-15 14:17:50 +0200
commitf5ed3e196a8b9c48941646f5cd8fd7676816623a (patch)
tree18bb88c1e850f8da0e66c0381220f8a04b8fefda /apps/web/src/components/editor/timeline-track.tsx
parentcb877769fba83ddb7131e103581edb14f4cf2e92 (diff)
parent1fdf2a051529e40cf7a0857d31a83057b878f08d (diff)
downloadopencut-f5ed3e196a8b9c48941646f5cd8fd7676816623a.tar.gz
Merge pull request #266 from anwarulislam/feat/snapping
feature: add timeline snapping functionality
Diffstat (limited to 'apps/web/src/components/editor/timeline-track.tsx')
-rw-r--r--apps/web/src/components/editor/timeline-track.tsx50
1 files changed, 45 insertions, 5 deletions
diff --git a/apps/web/src/components/editor/timeline-track.tsx b/apps/web/src/components/editor/timeline-track.tsx
index 033b79a6..2be143eb 100644
--- a/apps/web/src/components/editor/timeline-track.tsx
+++ b/apps/web/src/components/editor/timeline-track.tsx
@@ -22,13 +22,16 @@ import {
TIMELINE_CONSTANTS,
} from "@/constants/timeline-constants";
import { useProjectStore } from "@/stores/project-store";
+import { useTimelineSnapping, SnapPoint } from "@/hooks/use-timeline-snapping";
export function TimelineTrackContent({
track,
zoomLevel,
+ onSnapPointChange,
}: {
track: TimelineTrack;
zoomLevel: number;
+ onSnapPointChange?: (snapPoint: SnapPoint | null) => void;
}) {
const { mediaItems } = useMediaStore();
const {
@@ -45,8 +48,18 @@ export function TimelineTrackContent({
endDrag: endDragAction,
clearSelectedElements,
insertTrackAt,
+ snappingEnabled,
} = useTimelineStore();
+ const { currentTime } = usePlaybackStore();
+
+ // Initialize snapping hook
+ const { snapElementPosition } = useTimelineSnapping({
+ snapThreshold: 10,
+ enableElementSnapping: snappingEnabled,
+ enablePlayheadSnapping: snappingEnabled,
+ });
+
const timelineRef = useRef<HTMLDivElement>(null);
const [isDropping, setIsDropping] = useState(false);
const [dropPosition, setDropPosition] = useState<number | null>(null);
@@ -85,12 +98,34 @@ export function TimelineTrackContent({
mouseX / (TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel)
);
const adjustedTime = Math.max(0, mouseTime - dragState.clickOffsetTime);
- // Use frame snapping if project has FPS, otherwise use decimal snapping
- const projectStore = useProjectStore.getState();
- const projectFps = projectStore.activeProject?.fps || 30;
- const snappedTime = snapTimeToFrame(adjustedTime, projectFps);
- updateDragTime(snappedTime);
+ // Apply snapping if enabled
+ let finalTime = adjustedTime;
+ let snapPoint = null;
+ if (snappingEnabled) {
+ const snapResult = snapElementPosition(
+ adjustedTime,
+ tracks,
+ currentTime,
+ zoomLevel,
+ dragState.elementId || undefined
+ );
+ finalTime = snapResult.snappedTime;
+ snapPoint = snapResult.snapPoint;
+
+ // Notify parent component about snap point change
+ onSnapPointChange?.(snapPoint);
+ } else {
+ // Use frame snapping if project has FPS, otherwise use decimal snapping
+ const projectStore = useProjectStore.getState();
+ const projectFps = projectStore.activeProject?.fps || 30;
+ finalTime = snapTimeToFrame(adjustedTime, projectFps);
+
+ // Clear snap point when not snapping
+ onSnapPointChange?.(null);
+ }
+
+ updateDragTime(finalTime);
};
const handleMouseUp = (e: MouseEvent) => {
@@ -108,6 +143,8 @@ export function TimelineTrackContent({
dragState.currentTime
);
endDragAction();
+ // Clear snap point when drag ends
+ onSnapPointChange?.(null);
}
return;
}
@@ -204,6 +241,8 @@ export function TimelineTrackContent({
if (isTrackThatStartedDrag) {
endDragAction();
+ // Clear snap point when drag ends
+ onSnapPointChange?.(null);
}
};
@@ -229,6 +268,7 @@ export function TimelineTrackContent({
endDragAction,
selectedElements,
selectElement,
+ onSnapPointChange,
]);
const handleElementMouseDown = (