Allow Shift-click to select a range of items in the file tree (#15147)

GitOrigin-RevId: 4651b5d094c45fb8a7447d1f3439c2f6b3578403
This commit is contained in:
Alf Eaton 2024-01-12 10:10:06 +00:00 committed by Copybot
parent f67e19f6d2
commit bbbda35d63

View file

@ -19,6 +19,7 @@ import { useLayoutContext } from '../../../shared/context/layout-context'
import usePersistedState from '../../../shared/hooks/use-persisted-state' import usePersistedState from '../../../shared/hooks/use-persisted-state'
import usePreviousValue from '../../../shared/hooks/use-previous-value' import usePreviousValue from '../../../shared/hooks/use-previous-value'
import { useFileTreeMainContext } from '@/features/file-tree/contexts/file-tree-main' import { useFileTreeMainContext } from '@/features/file-tree/contexts/file-tree-main'
import { fileCollator } from '@/features/file-tree/util/file-collator'
const FileTreeSelectableContext = createContext() const FileTreeSelectableContext = createContext()
@ -223,6 +224,38 @@ export function useSelectableEntity(id, type) {
const isSelected = selectedEntityIds.has(id) const isSelected = selectedEntityIds.has(id)
const buildSelectedRange = useCallback(
id => {
const selected = []
let started = false
for (const itemId of sortedItems(fileTreeData)) {
if (itemId === id) {
selected.push(itemId)
if (started) {
break
} else {
started = true
}
} else if (selectedEntityIds.has(itemId)) {
// TODO: should only look at latest ("main") selected item
selected.push(itemId)
if (started) {
break
} else {
started = true
}
} else if (started) {
selected.push(itemId)
}
}
return selected
},
[fileTreeData, selectedEntityIds]
)
const chooseView = useCallback(() => { const chooseView = useCallback(() => {
for (const id of selectedEntityIds) { for (const id of selectedEntityIds) {
const selectedEntity = findInTree(fileTreeData, id) const selectedEntity = findInTree(fileTreeData, id)
@ -251,7 +284,13 @@ export function useSelectableEntity(id, type) {
const multiSelect = const multiSelect =
!isRootFolderSelected && (isMac ? ev.metaKey : ev.ctrlKey) !isRootFolderSelected && (isMac ? ev.metaKey : ev.ctrlKey)
setIsRootFolderSelected(false) setIsRootFolderSelected(false)
if (ev.shiftKey) {
// use Shift to select a range of items
selectOrMultiSelectEntity(buildSelectedRange(id))
} else {
selectOrMultiSelectEntity(id, multiSelect) selectOrMultiSelectEntity(id, multiSelect)
}
if (type === 'file') { if (type === 'file') {
setView('file') setView('file')
@ -268,6 +307,7 @@ export function useSelectableEntity(id, type) {
selectOrMultiSelectEntity, selectOrMultiSelectEntity,
setView, setView,
type, type,
buildSelectedRange,
chooseView, chooseView,
] ]
) )
@ -328,3 +368,21 @@ export function useFileTreeSelectable() {
return context return context
} }
const alphabetical = (a, b) => fileCollator.compare(a.name, b.name)
function* sortedItems(folder) {
yield folder._id
const folders = [...folder.folders].sort(alphabetical)
for (const subfolder of folders) {
for (const id of sortedItems(subfolder)) {
yield id
}
}
const files = [...folder.docs, ...folder.fileRefs].sort(alphabetical)
for (const file of files) {
yield file._id
}
}