mirror of
https://github.com/OpenCut-app/OpenCut.git
synced 2026-07-13 21:52:53 +02:00
138 lines
3.5 KiB
TypeScript
138 lines
3.5 KiB
TypeScript
import { useCallback } from "react";
|
|
import { useEditor } from "@/hooks/use-editor";
|
|
import type { ElementRef } from "@/lib/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,
|
|
};
|
|
}
|