Files
tolaria/src/hooks/useMultiSelect.ts
Luca Rossi 480d124a0e fix: multiselect range includes anchor note and prevents text selection (#127)
- selectRange() now includes the currently open note as the anchor
- Added setAnchor() to track which note is the selection start
- Added select-none to NoteList to prevent browser text selection on Shift+click

Co-authored-by: Test <test@test.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 16:53:18 +01:00

74 lines
2.0 KiB
TypeScript

import { useState, useCallback, useRef } from 'react'
import type { VaultEntry } from '../types'
export interface MultiSelectState {
selectedPaths: Set<string>
isMultiSelecting: boolean
toggle: (path: string) => void
selectRange: (toPath: string) => void
clear: () => void
setAnchor: (path: string) => void
selectAll: () => void
}
export function useMultiSelect(visibleEntries: VaultEntry[], activePath: string | null = null): MultiSelectState {
const [selectedPaths, setSelectedPaths] = useState<Set<string>>(new Set())
const lastClickedRef = useRef<string | null>(null)
const toggle = useCallback((path: string) => {
setSelectedPaths((prev) => {
const next = new Set(prev)
if (next.has(path)) next.delete(path)
else next.add(path)
return next
})
lastClickedRef.current = path
}, [])
const selectRange = useCallback((toPath: string) => {
const fromPath = lastClickedRef.current ?? activePath
if (!fromPath) {
toggle(toPath)
return
}
const paths = visibleEntries.map((e) => e.path)
const fromIdx = paths.indexOf(fromPath)
const toIdx = paths.indexOf(toPath)
if (fromIdx === -1 || toIdx === -1) {
toggle(toPath)
return
}
const start = Math.min(fromIdx, toIdx)
const end = Math.max(fromIdx, toIdx)
setSelectedPaths((prev) => {
const next = new Set(prev)
for (let i = start; i <= end; i++) next.add(paths[i])
return next
})
lastClickedRef.current = toPath
}, [visibleEntries, activePath, toggle])
const clear = useCallback(() => {
setSelectedPaths(new Set())
lastClickedRef.current = null
}, [])
const setAnchor = useCallback((path: string) => {
lastClickedRef.current = path
}, [])
const selectAll = useCallback(() => {
setSelectedPaths(new Set(visibleEntries.map((e) => e.path)))
}, [visibleEntries])
return {
selectedPaths,
isMultiSelecting: selectedPaths.size > 0,
toggle,
selectRange,
clear,
setAnchor,
selectAll,
}
}