From 3e89d2998588f8e35c524e523ba2ee853f0f6475 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Mon, 20 Apr 2026 11:31:17 +0200 Subject: refactor: restructure files to their domains, new preview overlay system, and dep graph --- .../hooks/element/use-element-selection.ts | 137 +++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 apps/web/src/timeline/hooks/element/use-element-selection.ts (limited to 'apps/web/src/timeline/hooks/element/use-element-selection.ts') diff --git a/apps/web/src/timeline/hooks/element/use-element-selection.ts b/apps/web/src/timeline/hooks/element/use-element-selection.ts new file mode 100644 index 00000000..19037e10 --- /dev/null +++ b/apps/web/src/timeline/hooks/element/use-element-selection.ts @@ -0,0 +1,137 @@ +import { useCallback } from "react"; +import { useEditor } from "@/editor/use-editor"; +import type { ElementRef } from "@/timeline/types"; + +export function useElementSelection() { + const editor = useEditor(); + const selectedElements = useEditor((e) => e.selection.getSelectedElements()); + + const isElementSelected = useCallback( + ({ trackId, elementId }: ElementRef) => + selectedElements.some( + (element) => + element.trackId === trackId && element.elementId === elementId, + ), + [selectedElements], + ); + + const selectElement = useCallback( + ({ trackId, elementId }: ElementRef) => { + editor.selection.setSelectedElements({ + elements: [{ trackId, elementId }], + }); + }, + [editor], + ); + + const addElementToSelection = useCallback( + ({ trackId, elementId }: ElementRef) => { + const alreadySelected = selectedElements.some( + (element) => + element.trackId === trackId && element.elementId === elementId, + ); + if (alreadySelected) return; + + editor.selection.setSelectedElements({ + elements: [...selectedElements, { trackId, elementId }], + }); + }, + [selectedElements, editor], + ); + + const removeElementFromSelection = useCallback( + ({ trackId, elementId }: ElementRef) => { + editor.selection.setSelectedElements({ + elements: selectedElements.filter( + (element) => + !(element.trackId === trackId && element.elementId === elementId), + ), + }); + }, + [selectedElements, editor], + ); + + const toggleElementSelection = useCallback( + ({ trackId, elementId }: ElementRef) => { + const alreadySelected = selectedElements.some( + (element) => + element.trackId === trackId && element.elementId === elementId, + ); + + if (alreadySelected) { + removeElementFromSelection({ trackId, elementId }); + } else { + addElementToSelection({ trackId, elementId }); + } + }, + [selectedElements, addElementToSelection, removeElementFromSelection], + ); + + const clearElementSelection = useCallback(() => { + editor.selection.clearSelection(); + }, [editor]); + + const setElementSelection = useCallback( + ({ elements }: { elements: ElementRef[] }) => { + editor.selection.setSelectedElements({ elements }); + }, + [editor], + ); + + + /** + * 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( + (selectedElement) => + !elements.some( + (element) => + element.trackId === selectedElement.trackId && + element.elementId === selectedElement.elementId, + ), + ), + ...elements, + ]; + editor.selection.setSelectedElements({ elements: merged }); + }, + [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( + ({ + trackId, + elementId, + isMultiKey, + }: ElementRef & { isMultiKey: boolean }) => { + if (isMultiKey) { + toggleElementSelection({ trackId, elementId }); + } else { + selectElement({ trackId, elementId }); + } + }, + [toggleElementSelection, selectElement], + ); + + return { + selectedElements, + isElementSelected, + selectElement, + setElementSelection, + mergeElementsIntoSelection, + addElementToSelection, + removeElementFromSelection, + toggleElementSelection, + clearElementSelection, + handleElementClick, + }; +} -- cgit v1.3.1