diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-03-25 16:47:22 +0100 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-03-29 15:48:22 +0200 |
| commit | 8db3bead13441a5882d8689dfc6a9f09c82375d9 (patch) | |
| tree | e9d0cafa93904ffcbec3188c37961bcc16c6422d /legacy/use-assets-selection.ts | |
| parent | 39ea298a9ccba3104c549c0ca079c231981811f5 (diff) | |
| download | opencut-8db3bead13441a5882d8689dfc6a9f09c82375d9.tar.gz | |
feat: masks, properties refactor, shaders, storage migrations, and more
Diffstat (limited to 'legacy/use-assets-selection.ts')
| -rw-r--r-- | legacy/use-assets-selection.ts | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/legacy/use-assets-selection.ts b/legacy/use-assets-selection.ts new file mode 100644 index 00000000..89d245e6 --- /dev/null +++ b/legacy/use-assets-selection.ts @@ -0,0 +1,209 @@ +import { useCallback, useEffect, useMemo } from "react"; +import { useAssetsPanelStore } from "@/stores/assets-panel-store"; + +function mergeUniqueIds({ ids }: { ids: string[] }) { + return [...new Set(ids)]; +} + +function getRangeIds({ + orderedIds, + anchorId, + targetId, +}: { + orderedIds: string[]; + anchorId: string; + targetId: string; +}) { + const anchorIndex = orderedIds.indexOf(anchorId); + const targetIndex = orderedIds.indexOf(targetId); + + if (anchorIndex === -1 || targetIndex === -1) { + return [targetId]; + } + + const rangeStart = Math.min(anchorIndex, targetIndex); + const rangeEnd = Math.max(anchorIndex, targetIndex); + return orderedIds.slice(rangeStart, rangeEnd + 1); +} + +export function useAssetsSelection({ + orderedIds, +}: { + orderedIds: string[]; +}) { + const { + selectedMediaIds, + selectionAnchorId, + setMediaSelection, + clearMediaSelection, + } = useAssetsPanelStore(); + const selectedIdSet = useMemo(() => { + return new Set(selectedMediaIds); + }, [selectedMediaIds]); + + useEffect(() => { + const validIds = new Set(orderedIds); + const nextSelectedIds = selectedMediaIds.filter((id) => validIds.has(id)); + const nextAnchorId = + selectionAnchorId && validIds.has(selectionAnchorId) + ? selectionAnchorId + : nextSelectedIds[nextSelectedIds.length - 1] ?? null; + const isSelectionUnchanged = + nextSelectedIds.length === selectedMediaIds.length && + nextAnchorId === selectionAnchorId; + + if (isSelectionUnchanged) { + return; + } + + setMediaSelection({ + ids: nextSelectedIds, + anchorId: nextAnchorId, + }); + }, [orderedIds, selectedMediaIds, selectionAnchorId, setMediaSelection]); + + const replaceSelection = useCallback( + (ids: string[], anchorId?: string | null) => { + setMediaSelection({ + ids: mergeUniqueIds({ ids }), + anchorId, + }); + }, + [setMediaSelection], + ); + + const isSelected = useCallback( + (id: string) => { + return selectedIdSet.has(id); + }, + [selectedIdSet], + ); + + const toggleSelection = useCallback( + (id: string) => { + if (selectedIdSet.has(id)) { + const nextSelectedIds = selectedMediaIds.filter( + (selectedId) => selectedId !== id, + ); + const nextAnchorId = + selectionAnchorId === id + ? nextSelectedIds[nextSelectedIds.length - 1] ?? null + : selectionAnchorId; + replaceSelection(nextSelectedIds, nextAnchorId); + return; + } + + replaceSelection([...selectedMediaIds, id], id); + }, + [ + selectedIdSet, + selectedMediaIds, + selectionAnchorId, + replaceSelection, + ], + ); + + const selectRange = useCallback( + ({ + targetId, + isAdditive, + }: { + targetId: string; + isAdditive: boolean; + }) => { + const anchorId = + selectionAnchorId ?? + selectedMediaIds[selectedMediaIds.length - 1] ?? + targetId; + const rangeIds = getRangeIds({ + orderedIds, + anchorId, + targetId, + }); + const nextSelectedIds = isAdditive + ? mergeUniqueIds({ + ids: [...selectedMediaIds, ...rangeIds], + }) + : rangeIds; + + replaceSelection(nextSelectedIds, anchorId); + }, + [orderedIds, replaceSelection, selectedMediaIds, selectionAnchorId], + ); + + const handleItemClick = useCallback( + ({ + event, + id, + }: { + event: React.MouseEvent; + id: string; + }) => { + const isToggleSelection = event.ctrlKey || event.metaKey; + + if (event.shiftKey) { + selectRange({ + targetId: id, + isAdditive: isToggleSelection, + }); + return; + } + + if (isToggleSelection) { + toggleSelection(id); + return; + } + + replaceSelection([id], id); + }, + [replaceSelection, selectRange, toggleSelection], + ); + + const handleItemContextMenu = useCallback( + (id: string) => { + if (selectedIdSet.has(id)) { + return; + } + + replaceSelection([id], id); + }, + [selectedIdSet, replaceSelection], + ); + + const applyBoxSelection = useCallback( + ({ + intersectedIds, + initialSelectedIds, + initialAnchorId, + isAdditive, + }: { + intersectedIds: string[]; + initialSelectedIds: string[]; + initialAnchorId: string | null; + isAdditive: boolean; + }) => { + const nextSelectedIds = isAdditive + ? mergeUniqueIds({ + ids: [...initialSelectedIds, ...intersectedIds], + }) + : intersectedIds; + const nextAnchorId = isAdditive + ? initialAnchorId ?? intersectedIds[intersectedIds.length - 1] ?? null + : intersectedIds[intersectedIds.length - 1] ?? null; + + replaceSelection(nextSelectedIds, nextAnchorId); + }, + [replaceSelection], + ); + + return { + selectedMediaIds, + selectionAnchorId, + isSelected, + replaceSelection, + clearSelection: clearMediaSelection, + handleItemClick, + handleItemContextMenu, + applyBoxSelection, + }; +} |
