mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-31 12:51:35 +02:00
Merge pull request #25752 from overleaf/dp-delete-multiple-files
Add bulk delete button to file tree toolbar GitOrigin-RevId: c857d8f5027eddb29b1ca324efe1a0e94ef4c28b
This commit is contained in:
Binary file not shown.
@@ -8,6 +8,7 @@ export default /** @type {const} */ ([
|
||||
'brush',
|
||||
'code',
|
||||
'create_new_folder',
|
||||
'delete',
|
||||
'description',
|
||||
'experiment',
|
||||
'forum',
|
||||
|
||||
@@ -6,7 +6,7 @@ import FileTreeContext from './file-tree-context'
|
||||
import FileTreeDraggablePreviewLayer from './file-tree-draggable-preview-layer'
|
||||
import FileTreeFolderList from './file-tree-folder-list'
|
||||
import FileTreeToolbar from './file-tree-toolbar'
|
||||
import FileTreeToolbarNew from '@/features/ide-redesign/components/file-tree-toolbar'
|
||||
import FileTreeToolbarNew from '@/features/ide-redesign/components/file-tree/file-tree-toolbar'
|
||||
import FileTreeModalDelete from './modals/file-tree-modal-delete'
|
||||
import FileTreeModalCreateFolder from './modals/file-tree-modal-create-folder'
|
||||
import FileTreeModalError from './modals/file-tree-modal-error'
|
||||
|
||||
@@ -57,6 +57,7 @@ const FileTreeActionableContext = createContext<
|
||||
newFileCreateMode: any | null
|
||||
error: any | null
|
||||
canDelete: boolean
|
||||
canBulkDelete: boolean
|
||||
canRename: boolean
|
||||
canCreate: boolean
|
||||
parentFolderId: string
|
||||
@@ -509,6 +510,8 @@ export const FileTreeActionableProvider: FC<React.PropsWithChildren> = ({
|
||||
const value = useMemo(
|
||||
() => ({
|
||||
canDelete: write && selectedEntityIds.size > 0 && !isRootFolderSelected,
|
||||
canBulkDelete:
|
||||
write && selectedEntityIds.size > 1 && !isRootFolderSelected,
|
||||
canRename: write && selectedEntityIds.size === 1 && !isRootFolderSelected,
|
||||
canCreate: write && selectedEntityIds.size < 2,
|
||||
...state,
|
||||
@@ -545,8 +548,8 @@ export const FileTreeActionableProvider: FC<React.PropsWithChildren> = ({
|
||||
isDuplicate,
|
||||
isRootFolderSelected,
|
||||
parentFolderId,
|
||||
selectedEntityIds.size,
|
||||
selectedFileName,
|
||||
selectedEntityIds.size,
|
||||
startCreatingDocOrFile,
|
||||
startCreatingFile,
|
||||
startCreatingFolder,
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import * as eventTracking from '../../../infrastructure/event-tracking'
|
||||
import { useFileTreeActionable } from '@/features/file-tree/contexts/file-tree-actionable'
|
||||
import { useFileTreeData } from '@/shared/context/file-tree-data-context'
|
||||
import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
|
||||
import MaterialIcon, {
|
||||
AvailableUnfilledIcon,
|
||||
} from '@/shared/components/material-icon'
|
||||
import React from 'react'
|
||||
import useCollapsibleFileTree from '../hooks/use-collapsible-file-tree'
|
||||
import { useCommandProvider } from '@/features/ide-react/hooks/use-command-provider'
|
||||
import { usePermissionsContext } from '@/features/ide-react/context/permissions-context'
|
||||
|
||||
function FileTreeToolbar() {
|
||||
const { t } = useTranslation()
|
||||
const { fileTreeExpanded, toggleFileTreeExpanded } = useCollapsibleFileTree()
|
||||
|
||||
return (
|
||||
<div className="file-tree-toolbar">
|
||||
<button
|
||||
className="file-tree-expand-collapse-button"
|
||||
onClick={toggleFileTreeExpanded}
|
||||
aria-label={
|
||||
fileTreeExpanded ? t('hide_file_tree') : t('show_file_tree')
|
||||
}
|
||||
>
|
||||
<MaterialIcon
|
||||
type={
|
||||
fileTreeExpanded ? 'keyboard_arrow_down' : 'keyboard_arrow_right'
|
||||
}
|
||||
/>
|
||||
<h4>{t('file_tree')}</h4>
|
||||
</button>
|
||||
<FileTreeActionButtons />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function FileTreeActionButtons() {
|
||||
const { t } = useTranslation()
|
||||
const { fileTreeReadOnly } = useFileTreeData()
|
||||
const { write } = usePermissionsContext()
|
||||
|
||||
const {
|
||||
canCreate,
|
||||
startCreatingFolder,
|
||||
startCreatingDocOrFile,
|
||||
startUploadingDocOrFile,
|
||||
} = useFileTreeActionable()
|
||||
useCommandProvider(() => {
|
||||
if (!canCreate || fileTreeReadOnly || !write) return
|
||||
return [
|
||||
{
|
||||
label: t('new_file'),
|
||||
id: 'new_file',
|
||||
handler: ({ location }) => {
|
||||
eventTracking.sendMB('new-file-click', { location })
|
||||
startCreatingDocOrFile()
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('new_folder'),
|
||||
id: 'new_folder',
|
||||
handler: startCreatingFolder,
|
||||
},
|
||||
{
|
||||
label: t('upload_file'),
|
||||
id: 'upload_file',
|
||||
handler: ({ location }) => {
|
||||
eventTracking.sendMB('upload-click', { location })
|
||||
startUploadingDocOrFile()
|
||||
},
|
||||
},
|
||||
]
|
||||
}, [
|
||||
canCreate,
|
||||
fileTreeReadOnly,
|
||||
startCreatingDocOrFile,
|
||||
t,
|
||||
startCreatingFolder,
|
||||
startUploadingDocOrFile,
|
||||
write,
|
||||
])
|
||||
|
||||
if (!canCreate || fileTreeReadOnly) return null
|
||||
|
||||
const createWithAnalytics = () => {
|
||||
eventTracking.sendMB('new-file-click', { location: 'toolbar' })
|
||||
startCreatingDocOrFile()
|
||||
}
|
||||
|
||||
const uploadWithAnalytics = () => {
|
||||
eventTracking.sendMB('upload-click', { location: 'toolbar' })
|
||||
startUploadingDocOrFile()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="file-tree-toolbar-action-buttons">
|
||||
<FileTreeActionButton
|
||||
id="new-file"
|
||||
description={t('new_file')}
|
||||
onClick={createWithAnalytics}
|
||||
iconType="note_add"
|
||||
/>
|
||||
<FileTreeActionButton
|
||||
id="new-folder"
|
||||
description={t('new_folder')}
|
||||
onClick={startCreatingFolder}
|
||||
iconType="create_new_folder"
|
||||
/>
|
||||
<FileTreeActionButton
|
||||
id="upload"
|
||||
description={t('upload')}
|
||||
onClick={uploadWithAnalytics}
|
||||
iconType="upload_file"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function FileTreeActionButton({
|
||||
id,
|
||||
description,
|
||||
onClick,
|
||||
iconType,
|
||||
}: {
|
||||
id: string
|
||||
description: string
|
||||
onClick: () => void
|
||||
iconType: AvailableUnfilledIcon
|
||||
}) {
|
||||
return (
|
||||
<OLTooltip
|
||||
id={id}
|
||||
description={description}
|
||||
overlayProps={{ placement: 'bottom' }}
|
||||
>
|
||||
<button className="btn file-tree-toolbar-action-button" onClick={onClick}>
|
||||
<MaterialIcon
|
||||
unfilled
|
||||
type={iconType}
|
||||
accessibilityLabel={description}
|
||||
/>
|
||||
</button>
|
||||
</OLTooltip>
|
||||
)
|
||||
}
|
||||
|
||||
export default FileTreeToolbar
|
||||
@@ -0,0 +1,33 @@
|
||||
import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
|
||||
import MaterialIcon, {
|
||||
AvailableUnfilledIcon,
|
||||
} from '@/shared/components/material-icon'
|
||||
import React from 'react'
|
||||
|
||||
export default function FileTreeActionButton({
|
||||
id,
|
||||
description,
|
||||
onClick,
|
||||
iconType,
|
||||
}: {
|
||||
id: string
|
||||
description: string
|
||||
onClick: () => void
|
||||
iconType: AvailableUnfilledIcon
|
||||
}) {
|
||||
return (
|
||||
<OLTooltip
|
||||
id={id}
|
||||
description={description}
|
||||
overlayProps={{ placement: 'bottom' }}
|
||||
>
|
||||
<button className="btn file-tree-toolbar-action-button" onClick={onClick}>
|
||||
<MaterialIcon
|
||||
unfilled
|
||||
type={iconType}
|
||||
accessibilityLabel={description}
|
||||
/>
|
||||
</button>
|
||||
</OLTooltip>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import * as eventTracking from '../../../../infrastructure/event-tracking'
|
||||
import { useFileTreeActionable } from '@/features/file-tree/contexts/file-tree-actionable'
|
||||
import { useFileTreeData } from '@/shared/context/file-tree-data-context'
|
||||
import React from 'react'
|
||||
import { useCommandProvider } from '@/features/ide-react/hooks/use-command-provider'
|
||||
import { usePermissionsContext } from '@/features/ide-react/context/permissions-context'
|
||||
import FileTreeActionButton from './file-tree-action-button'
|
||||
|
||||
export default function FileTreeActionButtons() {
|
||||
const { t } = useTranslation()
|
||||
const { fileTreeReadOnly } = useFileTreeData()
|
||||
const { write } = usePermissionsContext()
|
||||
|
||||
const {
|
||||
canCreate,
|
||||
canBulkDelete,
|
||||
startDeleting,
|
||||
startCreatingFolder,
|
||||
startCreatingDocOrFile,
|
||||
startUploadingDocOrFile,
|
||||
} = useFileTreeActionable()
|
||||
|
||||
useCommandProvider(() => {
|
||||
if (!canCreate || fileTreeReadOnly || !write) return
|
||||
return [
|
||||
{
|
||||
label: t('new_file'),
|
||||
id: 'new_file',
|
||||
handler: ({ location }) => {
|
||||
eventTracking.sendMB('new-file-click', { location })
|
||||
startCreatingDocOrFile()
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('new_folder'),
|
||||
id: 'new_folder',
|
||||
handler: startCreatingFolder,
|
||||
},
|
||||
{
|
||||
label: t('upload_file'),
|
||||
id: 'upload_file',
|
||||
handler: ({ location }) => {
|
||||
eventTracking.sendMB('upload-click', { location })
|
||||
startUploadingDocOrFile()
|
||||
},
|
||||
},
|
||||
]
|
||||
}, [
|
||||
canCreate,
|
||||
fileTreeReadOnly,
|
||||
startCreatingDocOrFile,
|
||||
t,
|
||||
startCreatingFolder,
|
||||
startUploadingDocOrFile,
|
||||
write,
|
||||
])
|
||||
|
||||
if (fileTreeReadOnly) return null
|
||||
|
||||
const createWithAnalytics = () => {
|
||||
eventTracking.sendMB('new-file-click', { location: 'toolbar' })
|
||||
startCreatingDocOrFile()
|
||||
}
|
||||
|
||||
const uploadWithAnalytics = () => {
|
||||
eventTracking.sendMB('upload-click', { location: 'toolbar' })
|
||||
startUploadingDocOrFile()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="file-tree-toolbar-action-buttons">
|
||||
{canCreate && (
|
||||
<FileTreeActionButton
|
||||
id="new-file"
|
||||
description={t('new_file')}
|
||||
onClick={createWithAnalytics}
|
||||
iconType="note_add"
|
||||
/>
|
||||
)}
|
||||
{canCreate && (
|
||||
<FileTreeActionButton
|
||||
id="new-folder"
|
||||
description={t('new_folder')}
|
||||
onClick={startCreatingFolder}
|
||||
iconType="create_new_folder"
|
||||
/>
|
||||
)}
|
||||
{canCreate && (
|
||||
<FileTreeActionButton
|
||||
id="upload"
|
||||
description={t('upload')}
|
||||
onClick={uploadWithAnalytics}
|
||||
iconType="upload_file"
|
||||
/>
|
||||
)}
|
||||
{canBulkDelete && (
|
||||
<FileTreeActionButton
|
||||
id="delete"
|
||||
description={t('delete')}
|
||||
onClick={startDeleting}
|
||||
iconType="delete"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import { FileTree } from '@/features/ide-react/components/file-tree'
|
||||
import { OutlineContainer } from '@/features/outline/components/outline-container'
|
||||
import { VerticalResizeHandle } from '@/features/ide-react/components/resize/vertical-resize-handle'
|
||||
import { useOutlinePane } from '@/features/ide-react/hooks/use-outline-pane'
|
||||
import useCollapsibleFileTree from '../hooks/use-collapsible-file-tree'
|
||||
import useCollapsibleFileTree from '../../hooks/use-collapsible-file-tree'
|
||||
import classNames from 'classnames'
|
||||
|
||||
function FileTreeOutlinePanel() {
|
||||
@@ -0,0 +1,32 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import MaterialIcon from '@/shared/components/material-icon'
|
||||
import React from 'react'
|
||||
import useCollapsibleFileTree from '../../hooks/use-collapsible-file-tree'
|
||||
import FileTreeActionButtons from './file-tree-action-buttons'
|
||||
|
||||
function FileTreeToolbar() {
|
||||
const { t } = useTranslation()
|
||||
const { fileTreeExpanded, toggleFileTreeExpanded } = useCollapsibleFileTree()
|
||||
|
||||
return (
|
||||
<div className="file-tree-toolbar">
|
||||
<button
|
||||
className="file-tree-expand-collapse-button"
|
||||
onClick={toggleFileTreeExpanded}
|
||||
aria-label={
|
||||
fileTreeExpanded ? t('hide_file_tree') : t('show_file_tree')
|
||||
}
|
||||
>
|
||||
<MaterialIcon
|
||||
type={
|
||||
fileTreeExpanded ? 'keyboard_arrow_down' : 'keyboard_arrow_right'
|
||||
}
|
||||
/>
|
||||
<h4>{t('file_tree')}</h4>
|
||||
</button>
|
||||
<FileTreeActionButtons />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default FileTreeToolbar
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
RailTabKey,
|
||||
useRailContext,
|
||||
} from '../contexts/rail-context'
|
||||
import FileTreeOutlinePanel from './file-tree-outline-panel'
|
||||
import FileTreeOutlinePanel from './file-tree/file-tree-outline-panel'
|
||||
import { ChatIndicator, ChatPane } from './chat/chat'
|
||||
import getMeta from '@/utils/meta'
|
||||
import { HorizontalResizeHandle } from '@/features/ide-react/components/resize/horizontal-resize-handle'
|
||||
|
||||
Reference in New Issue
Block a user