From ae2b34e1145085e8c84734f542200e55d1b27eb3 Mon Sep 17 00:00:00 2001 From: oguzhancttnky Date: Sun, 17 May 2026 17:47:57 +0300 Subject: [PATCH] refactor: refine timeline shift-range selection --- .../element-interaction-controller.ts | 10 +++- .../hooks/element/use-element-selection.ts | 58 ++++++++----------- 2 files changed, 33 insertions(+), 35 deletions(-) diff --git a/apps/web/src/timeline/controllers/element-interaction-controller.ts b/apps/web/src/timeline/controllers/element-interaction-controller.ts index e1d2274f..79b5c819 100644 --- a/apps/web/src/timeline/controllers/element-interaction-controller.ts +++ b/apps/web/src/timeline/controllers/element-interaction-controller.ts @@ -379,10 +379,18 @@ export class ElementInteractionController { let rangeSelection: readonly ElementRef[] | null = null; if (event.shiftKey) { + const anchor = + this.elementSelectionAnchor?.trackId === ref.trackId + ? this.elementSelectionAnchor + : null; rangeSelection = this.deps.selection.selectRange({ - anchor: this.elementSelectionAnchor, + anchor: anchor ?? ref, target: ref, }); + + if (!anchor) { + this.elementSelectionAnchor = ref; + } } else if (event.metaKey || event.ctrlKey) { this.deps.selection.handleClick({ ...ref, isMultiKey: true }); } diff --git a/apps/web/src/timeline/hooks/element/use-element-selection.ts b/apps/web/src/timeline/hooks/element/use-element-selection.ts index bfdf38b1..cc34409e 100644 --- a/apps/web/src/timeline/hooks/element/use-element-selection.ts +++ b/apps/web/src/timeline/hooks/element/use-element-selection.ts @@ -1,5 +1,6 @@ import { useCallback } from "react"; import { useEditor } from "@/editor/use-editor"; +import { findTrackInSceneTracks } from "@/timeline/track-element-update"; import type { ElementRef } from "@/timeline/types"; export function useElementSelection() { @@ -102,8 +103,8 @@ export function useElementSelection() { ); /** - * Selects every element between the anchor and target on the same timeline row. - * Falls back to selecting only the target when no same-row anchor exists. + * Handles Shift-click range selection. Selects all clips between the anchor + * and target when both are on the same track; otherwise selects only target. */ const selectElementRange = useCallback( ({ @@ -113,58 +114,47 @@ export function useElementSelection() { anchor?: ElementRef | null; target: ElementRef; }) => { - const tracks = editor.scenes.getActiveScene().tracks; - const track = [...tracks.overlay, tracks.main, ...tracks.audio].find( - (candidate) => candidate.id === target.trackId, - ); - const rangeAnchor = - anchor?.trackId === target.trackId - ? anchor - : selectedElements - .slice() - .reverse() - .find((element) => element.trackId === target.trackId); - - if (!track || !rangeAnchor) { - const elements = [target]; + const setSelection = (elements: ElementRef[]) => { editor.selection.setSelectedElements({ elements }); return elements; + }; + + const track = findTrackInSceneTracks({ + tracks: editor.scenes.getActiveScene().tracks, + trackId: target.trackId, + }); + if (!track || anchor?.trackId !== target.trackId) { + return setSelection([target]); } const orderedElements = track.elements .map((element, index) => ({ element, index })) - .sort((a, b) => { - if (a.element.startTime !== b.element.startTime) { - return a.element.startTime - b.element.startTime; - } - return a.index - b.index; - }); + .sort((a, b) => + a.element.startTime === b.element.startTime + ? a.index - b.index + : a.element.startTime - b.element.startTime, + ); const anchorIndex = orderedElements.findIndex( - ({ element }) => element.id === rangeAnchor.elementId, + ({ element }) => element.id === anchor.elementId, ); const targetIndex = orderedElements.findIndex( ({ element }) => element.id === target.elementId, ); if (anchorIndex === -1 || targetIndex === -1) { - const elements = [target]; - editor.selection.setSelectedElements({ elements }); - return elements; + return setSelection([target]); } const start = Math.min(anchorIndex, targetIndex); const end = Math.max(anchorIndex, targetIndex); - const elements = orderedElements - .slice(start, end + 1) - .map(({ element }) => ({ + return setSelection( + orderedElements.slice(start, end + 1).map(({ element }) => ({ trackId: target.trackId, elementId: element.id, - })); - - editor.selection.setSelectedElements({ elements }); - return elements; + })), + ); }, - [selectedElements, editor], + [editor], ); /**