From e223348d47f07528754ba5ffe3c6b25b27e9f1e9 Mon Sep 17 00:00:00 2001 From: oguzhancttnky Date: Sun, 17 May 2026 15:19:21 +0300 Subject: [PATCH] chore: add comment for explain selectElementRange --- .../hooks/element/use-element-selection.ts | 162 +++++++++--------- 1 file changed, 83 insertions(+), 79 deletions(-) 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 83fd4806..bfdf38b1 100644 --- a/apps/web/src/timeline/hooks/element/use-element-selection.ts +++ b/apps/web/src/timeline/hooks/element/use-element-selection.ts @@ -83,10 +83,10 @@ export function useElementSelection() { * Merges elements into the current selection, deduplicating by identity. * Used for additive box-select where the pre-drag selection is preserved. */ - const mergeElementsIntoSelection = useCallback( - ({ elements }: { elements: ElementRef[] }) => { - const merged = [ - ...selectedElements.filter( + const mergeElementsIntoSelection = useCallback( + ({ elements }: { elements: ElementRef[] }) => { + const merged = [ + ...selectedElements.filter( (selectedElement) => !elements.some( (element) => @@ -98,74 +98,78 @@ export function useElementSelection() { ]; editor.selection.setSelectedElements({ elements: merged }); }, - [selectedElements, editor], - ); - - const selectElementRange = useCallback( - ({ - anchor, - target, - }: { - 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]; - editor.selection.setSelectedElements({ elements }); - return elements; - } - - 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; - }); - const anchorIndex = orderedElements.findIndex( - ({ element }) => element.id === rangeAnchor.elementId, - ); - const targetIndex = orderedElements.findIndex( - ({ element }) => element.id === target.elementId, - ); - - if (anchorIndex === -1 || targetIndex === -1) { - const elements = [target]; - editor.selection.setSelectedElements({ elements }); - return elements; - } - - const start = Math.min(anchorIndex, targetIndex); - const end = Math.max(anchorIndex, targetIndex); - const elements = orderedElements - .slice(start, end + 1) - .map(({ element }) => ({ - trackId: target.trackId, - elementId: element.id, - })); - - editor.selection.setSelectedElements({ elements }); - return elements; - }, - [selectedElements, editor], - ); - - /** - * Handles click interaction on an element. - * - Regular click: select only this element + [selectedElements, editor], + ); + + /** + * 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. + */ + const selectElementRange = useCallback( + ({ + anchor, + target, + }: { + 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]; + editor.selection.setSelectedElements({ elements }); + return elements; + } + + 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; + }); + const anchorIndex = orderedElements.findIndex( + ({ element }) => element.id === rangeAnchor.elementId, + ); + const targetIndex = orderedElements.findIndex( + ({ element }) => element.id === target.elementId, + ); + + if (anchorIndex === -1 || targetIndex === -1) { + const elements = [target]; + editor.selection.setSelectedElements({ elements }); + return elements; + } + + const start = Math.min(anchorIndex, targetIndex); + const end = Math.max(anchorIndex, targetIndex); + const elements = orderedElements + .slice(start, end + 1) + .map(({ element }) => ({ + trackId: target.trackId, + elementId: element.id, + })); + + editor.selection.setSelectedElements({ elements }); + return elements; + }, + [selectedElements, editor], + ); + + /** + * Handles click interaction on an element. + * - Regular click: select only this element * - Multi-key click (Ctrl/Cmd): toggle this element in selection */ const handleElementClick = useCallback( @@ -186,13 +190,13 @@ export function useElementSelection() { return { selectedElements, isElementSelected, - selectElement, - setElementSelection, - mergeElementsIntoSelection, - selectElementRange, - addElementToSelection, - removeElementFromSelection, - toggleElementSelection, + selectElement, + setElementSelection, + mergeElementsIntoSelection, + selectElementRange, + addElementToSelection, + removeElementFromSelection, + toggleElementSelection, clearElementSelection, handleElementClick, };