From 061f10a059aca9a445e79bcaeb33a4a52d580d70 Mon Sep 17 00:00:00 2001 From: David <33458145+davidmcpowell@users.noreply.github.com> Date: Wed, 21 May 2025 09:47:25 +0100 Subject: [PATCH] Merge pull request #25752 from overleaf/dp-delete-multiple-files Add bulk delete button to file tree toolbar GitOrigin-RevId: c857d8f5027eddb29b1ca324efe1a0e94ef4c28b --- ...alSymbolsRoundedUnfilledPartialSlice.woff2 | Bin 4392 -> 4384 bytes .../material-symbols/unfilled-symbols.mjs | 1 + .../file-tree/components/file-tree-root.tsx | 2 +- .../contexts/file-tree-actionable.tsx | 5 +- .../components/file-tree-toolbar.tsx | 149 ------------------ .../file-tree/file-tree-action-button.tsx | 33 ++++ .../file-tree/file-tree-action-buttons.tsx | 107 +++++++++++++ .../file-tree-outline-panel.tsx | 2 +- .../file-tree/file-tree-toolbar.tsx | 32 ++++ .../features/ide-redesign/components/rail.tsx | 2 +- 10 files changed, 180 insertions(+), 153 deletions(-) delete mode 100644 services/web/frontend/js/features/ide-redesign/components/file-tree-toolbar.tsx create mode 100644 services/web/frontend/js/features/ide-redesign/components/file-tree/file-tree-action-button.tsx create mode 100644 services/web/frontend/js/features/ide-redesign/components/file-tree/file-tree-action-buttons.tsx rename services/web/frontend/js/features/ide-redesign/components/{ => file-tree}/file-tree-outline-panel.tsx (95%) create mode 100644 services/web/frontend/js/features/ide-redesign/components/file-tree/file-tree-toolbar.tsx diff --git a/services/web/frontend/fonts/material-symbols/MaterialSymbolsRoundedUnfilledPartialSlice.woff2 b/services/web/frontend/fonts/material-symbols/MaterialSymbolsRoundedUnfilledPartialSlice.woff2 index 14a5ef1e2bb15e116072c39297e63a01e822b47c..df942df17688781db3c188df5a20253919ec02fc 100644 GIT binary patch literal 4384 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 awAp~2GYtG2#iPx+myzd%{|nE^D**u2R#deB literal 4392 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 = ({ 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 = ({ isDuplicate, isRootFolderSelected, parentFolderId, - selectedEntityIds.size, selectedFileName, + selectedEntityIds.size, startCreatingDocOrFile, startCreatingFile, startCreatingFolder, diff --git a/services/web/frontend/js/features/ide-redesign/components/file-tree-toolbar.tsx b/services/web/frontend/js/features/ide-redesign/components/file-tree-toolbar.tsx deleted file mode 100644 index f1d72941f5..0000000000 --- a/services/web/frontend/js/features/ide-redesign/components/file-tree-toolbar.tsx +++ /dev/null @@ -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 ( -
- - -
- ) -} - -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 ( -
- - - -
- ) -} - -function FileTreeActionButton({ - id, - description, - onClick, - iconType, -}: { - id: string - description: string - onClick: () => void - iconType: AvailableUnfilledIcon -}) { - return ( - - - - ) -} - -export default FileTreeToolbar diff --git a/services/web/frontend/js/features/ide-redesign/components/file-tree/file-tree-action-button.tsx b/services/web/frontend/js/features/ide-redesign/components/file-tree/file-tree-action-button.tsx new file mode 100644 index 0000000000..5678db58c8 --- /dev/null +++ b/services/web/frontend/js/features/ide-redesign/components/file-tree/file-tree-action-button.tsx @@ -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 ( + + + + ) +} diff --git a/services/web/frontend/js/features/ide-redesign/components/file-tree/file-tree-action-buttons.tsx b/services/web/frontend/js/features/ide-redesign/components/file-tree/file-tree-action-buttons.tsx new file mode 100644 index 0000000000..93760b97ab --- /dev/null +++ b/services/web/frontend/js/features/ide-redesign/components/file-tree/file-tree-action-buttons.tsx @@ -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 ( +
+ {canCreate && ( + + )} + {canCreate && ( + + )} + {canCreate && ( + + )} + {canBulkDelete && ( + + )} +
+ ) +} diff --git a/services/web/frontend/js/features/ide-redesign/components/file-tree-outline-panel.tsx b/services/web/frontend/js/features/ide-redesign/components/file-tree/file-tree-outline-panel.tsx similarity index 95% rename from services/web/frontend/js/features/ide-redesign/components/file-tree-outline-panel.tsx rename to services/web/frontend/js/features/ide-redesign/components/file-tree/file-tree-outline-panel.tsx index bf69d40e59..62c228973e 100644 --- a/services/web/frontend/js/features/ide-redesign/components/file-tree-outline-panel.tsx +++ b/services/web/frontend/js/features/ide-redesign/components/file-tree/file-tree-outline-panel.tsx @@ -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() { diff --git a/services/web/frontend/js/features/ide-redesign/components/file-tree/file-tree-toolbar.tsx b/services/web/frontend/js/features/ide-redesign/components/file-tree/file-tree-toolbar.tsx new file mode 100644 index 0000000000..231f98e7d7 --- /dev/null +++ b/services/web/frontend/js/features/ide-redesign/components/file-tree/file-tree-toolbar.tsx @@ -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 ( +
+ + +
+ ) +} + +export default FileTreeToolbar diff --git a/services/web/frontend/js/features/ide-redesign/components/rail.tsx b/services/web/frontend/js/features/ide-redesign/components/rail.tsx index 8f54331630..249b2b1891 100644 --- a/services/web/frontend/js/features/ide-redesign/components/rail.tsx +++ b/services/web/frontend/js/features/ide-redesign/components/rail.tsx @@ -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'