mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Allow Shift-click to select a range of items in the file tree (#15147)
GitOrigin-RevId: 4651b5d094c45fb8a7447d1f3439c2f6b3578403
This commit is contained in:
parent
f67e19f6d2
commit
bbbda35d63
1 changed files with 59 additions and 1 deletions
|
@ -19,6 +19,7 @@ import { useLayoutContext } from '../../../shared/context/layout-context'
|
|||
import usePersistedState from '../../../shared/hooks/use-persisted-state'
|
||||
import usePreviousValue from '../../../shared/hooks/use-previous-value'
|
||||
import { useFileTreeMainContext } from '@/features/file-tree/contexts/file-tree-main'
|
||||
import { fileCollator } from '@/features/file-tree/util/file-collator'
|
||||
|
||||
const FileTreeSelectableContext = createContext()
|
||||
|
||||
|
@ -223,6 +224,38 @@ export function useSelectableEntity(id, type) {
|
|||
|
||||
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(() => {
|
||||
for (const id of selectedEntityIds) {
|
||||
const selectedEntity = findInTree(fileTreeData, id)
|
||||
|
@ -251,7 +284,13 @@ export function useSelectableEntity(id, type) {
|
|||
const multiSelect =
|
||||
!isRootFolderSelected && (isMac ? ev.metaKey : ev.ctrlKey)
|
||||
setIsRootFolderSelected(false)
|
||||
selectOrMultiSelectEntity(id, multiSelect)
|
||||
|
||||
if (ev.shiftKey) {
|
||||
// use Shift to select a range of items
|
||||
selectOrMultiSelectEntity(buildSelectedRange(id))
|
||||
} else {
|
||||
selectOrMultiSelectEntity(id, multiSelect)
|
||||
}
|
||||
|
||||
if (type === 'file') {
|
||||
setView('file')
|
||||
|
@ -268,6 +307,7 @@ export function useSelectableEntity(id, type) {
|
|||
selectOrMultiSelectEntity,
|
||||
setView,
|
||||
type,
|
||||
buildSelectedRange,
|
||||
chooseView,
|
||||
]
|
||||
)
|
||||
|
@ -328,3 +368,21 @@ export function useFileTreeSelectable() {
|
|||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue