refactor: refine timeline shift-range selection

This commit is contained in:
oguzhancttnky 2026-05-17 17:47:57 +03:00
parent e223348d47
commit ae2b34e114
2 changed files with 33 additions and 35 deletions

View File

@ -379,10 +379,18 @@ export class ElementInteractionController {
let rangeSelection: readonly ElementRef[] | null = null; let rangeSelection: readonly ElementRef[] | null = null;
if (event.shiftKey) { if (event.shiftKey) {
const anchor =
this.elementSelectionAnchor?.trackId === ref.trackId
? this.elementSelectionAnchor
: null;
rangeSelection = this.deps.selection.selectRange({ rangeSelection = this.deps.selection.selectRange({
anchor: this.elementSelectionAnchor, anchor: anchor ?? ref,
target: ref, target: ref,
}); });
if (!anchor) {
this.elementSelectionAnchor = ref;
}
} else if (event.metaKey || event.ctrlKey) { } else if (event.metaKey || event.ctrlKey) {
this.deps.selection.handleClick({ ...ref, isMultiKey: true }); this.deps.selection.handleClick({ ...ref, isMultiKey: true });
} }

View File

@ -1,5 +1,6 @@
import { useCallback } from "react"; import { useCallback } from "react";
import { useEditor } from "@/editor/use-editor"; import { useEditor } from "@/editor/use-editor";
import { findTrackInSceneTracks } from "@/timeline/track-element-update";
import type { ElementRef } from "@/timeline/types"; import type { ElementRef } from "@/timeline/types";
export function useElementSelection() { export function useElementSelection() {
@ -102,8 +103,8 @@ export function useElementSelection() {
); );
/** /**
* Selects every element between the anchor and target on the same timeline row. * Handles Shift-click range selection. Selects all clips between the anchor
* Falls back to selecting only the target when no same-row anchor exists. * and target when both are on the same track; otherwise selects only target.
*/ */
const selectElementRange = useCallback( const selectElementRange = useCallback(
({ ({
@ -113,58 +114,47 @@ export function useElementSelection() {
anchor?: ElementRef | null; anchor?: ElementRef | null;
target: ElementRef; target: ElementRef;
}) => { }) => {
const tracks = editor.scenes.getActiveScene().tracks; const setSelection = (elements: ElementRef[]) => {
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];
editor.selection.setSelectedElements({ elements }); editor.selection.setSelectedElements({ elements });
return 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 const orderedElements = track.elements
.map((element, index) => ({ element, index })) .map((element, index) => ({ element, index }))
.sort((a, b) => { .sort((a, b) =>
if (a.element.startTime !== b.element.startTime) { a.element.startTime === b.element.startTime
return a.element.startTime - b.element.startTime; ? a.index - b.index
} : a.element.startTime - b.element.startTime,
return a.index - b.index; );
});
const anchorIndex = orderedElements.findIndex( const anchorIndex = orderedElements.findIndex(
({ element }) => element.id === rangeAnchor.elementId, ({ element }) => element.id === anchor.elementId,
); );
const targetIndex = orderedElements.findIndex( const targetIndex = orderedElements.findIndex(
({ element }) => element.id === target.elementId, ({ element }) => element.id === target.elementId,
); );
if (anchorIndex === -1 || targetIndex === -1) { if (anchorIndex === -1 || targetIndex === -1) {
const elements = [target]; return setSelection([target]);
editor.selection.setSelectedElements({ elements });
return elements;
} }
const start = Math.min(anchorIndex, targetIndex); const start = Math.min(anchorIndex, targetIndex);
const end = Math.max(anchorIndex, targetIndex); const end = Math.max(anchorIndex, targetIndex);
const elements = orderedElements return setSelection(
.slice(start, end + 1) orderedElements.slice(start, end + 1).map(({ element }) => ({
.map(({ element }) => ({
trackId: target.trackId, trackId: target.trackId,
elementId: element.id, elementId: element.id,
})); })),
);
editor.selection.setSelectedElements({ elements });
return elements;
}, },
[selectedElements, editor], [editor],
); );
/** /**