From ae4e5b30c1d5d98e1bddffc2bacfdd1886e41455 Mon Sep 17 00:00:00 2001 From: Mathias Jakobsen Date: Thu, 13 Mar 2025 09:46:48 +0000 Subject: [PATCH] [web] Editor redesign: Add actions to project name dropdown (#24220) GitOrigin-RevId: 04f4abdc529a0494c70b0e3d14847b0cf452b80d --- .../web/frontend/extracted-translations.json | 2 + .../components/toolbar/download-project.tsx | 69 ++++++++++++++++++ .../components/toolbar/editable-label.tsx | 70 +++++++++++++++++++ .../components/toolbar/project-title.tsx | 65 ++++++++++++++--- .../components/toolbar/toolbar.tsx | 12 ---- .../pages/editor/toolbar-redesign.scss | 7 ++ services/web/locales/en.json | 3 + 7 files changed, 208 insertions(+), 20 deletions(-) create mode 100644 services/web/frontend/js/features/ide-redesign/components/toolbar/download-project.tsx create mode 100644 services/web/frontend/js/features/ide-redesign/components/toolbar/editable-label.tsx diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index fd64863454..3e7f70ac8e 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -427,6 +427,8 @@ "dont_reload_or_close_this_tab": "", "download": "", "download_all": "", + "download_as_pdf": "", + "download_as_source_zip": "", "download_metadata": "", "download_pdf": "", "download_zip_file": "", diff --git a/services/web/frontend/js/features/ide-redesign/components/toolbar/download-project.tsx b/services/web/frontend/js/features/ide-redesign/components/toolbar/download-project.tsx new file mode 100644 index 0000000000..e40f385fa1 --- /dev/null +++ b/services/web/frontend/js/features/ide-redesign/components/toolbar/download-project.tsx @@ -0,0 +1,69 @@ +import OLDropdownMenuItem from '@/features/ui/components/ol/ol-dropdown-menu-item' +import OLTooltip from '@/features/ui/components/ol/ol-tooltip' +import { isSmallDevice, sendMB } from '@/infrastructure/event-tracking' +import { useDetachCompileContext as useCompileContext } from '@/shared/context/detach-compile-context' +import { useProjectContext } from '@/shared/context/project-context' +import { useCallback } from 'react' +import { useTranslation } from 'react-i18next' + +export const DownloadProjectZip = () => { + const { t } = useTranslation() + const { _id: projectId } = useProjectContext() + const sendDownloadEvent = useCallback(() => { + sendMB('download-zip-button-click', { + projectId, + location: 'project-name-dropdown', + isSmallDevice, + }) + }, [projectId]) + + return ( + + {t('download_as_source_zip')} + + ) +} + +export const DownloadProjectPDF = () => { + const { t } = useTranslation() + const { pdfDownloadUrl, pdfUrl } = useCompileContext() + const { _id: projectId } = useProjectContext() + const sendDownloadEvent = useCallback(() => { + sendMB('download-pdf-button-click', { + projectId, + location: 'project-name-dropdown', + isSmallDevice, + }) + }, [projectId]) + + const button = ( + + {t('download_as_pdf')} + + ) + + if (!pdfUrl) { + return ( + + {button} + + ) + } else { + return button + } +} diff --git a/services/web/frontend/js/features/ide-redesign/components/toolbar/editable-label.tsx b/services/web/frontend/js/features/ide-redesign/components/toolbar/editable-label.tsx new file mode 100644 index 0000000000..b223b95676 --- /dev/null +++ b/services/web/frontend/js/features/ide-redesign/components/toolbar/editable-label.tsx @@ -0,0 +1,70 @@ +import OLFormControl from '@/features/ui/components/ol/ol-form-control' +import { + ChangeEventHandler, + useCallback, + useEffect, + useRef, + useState, +} from 'react' + +type EditableLabelProps = { + initialText: string + className?: string + onChange: (name: string) => void + onCancel: () => void + maxLength?: number +} + +const EditableLabel = ({ + initialText, + className, + onChange, + onCancel, + maxLength, +}: EditableLabelProps) => { + const [name, setName] = useState(initialText) + + const inputRef = useRef(null) + useEffect(() => { + inputRef.current?.select() + }, []) + + const onInputChange: ChangeEventHandler = useCallback( + event => { + setName(event.target.value) + }, + [] + ) + + const finishRenaming = useCallback(() => { + onChange(name) + }, [onChange, name]) + + const onKeyDown = useCallback( + (event: React.KeyboardEvent) => { + if (event.key === 'Enter') { + event.preventDefault() + finishRenaming() + } + if (event.key === 'Escape') { + event.preventDefault() + onCancel() + } + }, + [finishRenaming, onCancel] + ) + + return ( + + ) +} +export default EditableLabel diff --git a/services/web/frontend/js/features/ide-redesign/components/toolbar/project-title.tsx b/services/web/frontend/js/features/ide-redesign/components/toolbar/project-title.tsx index 91743558e2..4cf10b37f3 100644 --- a/services/web/frontend/js/features/ide-redesign/components/toolbar/project-title.tsx +++ b/services/web/frontend/js/features/ide-redesign/components/toolbar/project-title.tsx @@ -4,14 +4,53 @@ import { DropdownMenu, DropdownToggle, } from '@/features/ui/components/bootstrap-5/dropdown-menu' -import OLDropdownMenuItem from '@/features/ui/components/ol/ol-dropdown-menu-item' import MaterialIcon from '@/shared/components/material-icon' import { useProjectContext } from '@/shared/context/project-context' import { useTranslation } from 'react-i18next' +import importOverleafModules from '../../../../../macros/import-overleaf-module.macro' +import { useEditorContext } from '@/shared/context/editor-context' +import { DownloadProjectPDF, DownloadProjectZip } from './download-project' +import { useCallback, useState } from 'react' +import OLDropdownMenuItem from '@/features/ui/components/ol/ol-dropdown-menu-item' +import EditableLabel from './editable-label' + +const [publishModalModules] = importOverleafModules('publishModal') +const SubmitProjectButton = publishModalModules?.import.NewPublishToolbarButton export const ToolbarProjectTitle = () => { - const { name } = useProjectContext() const { t } = useTranslation() + const { permissionsLevel, renameProject } = useEditorContext() + const { name } = useProjectContext() + const shouldDisplaySubmitButton = + (permissionsLevel === 'owner' || permissionsLevel === 'readAndWrite') && + SubmitProjectButton + const hasRenamePermissions = permissionsLevel === 'owner' + const [isRenaming, setIsRenaming] = useState(false) + const onRename = useCallback( + name => { + if (name) { + renameProject(name) + } + setIsRenaming(false) + }, + [renameProject] + ) + const onCancel = useCallback(() => { + setIsRenaming(false) + }, []) + + if (isRenaming) { + return ( + + ) + } + return ( { accessibilityLabel={t('project_title_options')} /> - - TODO: Export + + {shouldDisplaySubmitButton && ( + <> + + + + )} + + - {t('rename')} - {t('download')} - - {t('delete')} + { + setIsRenaming(true) + }} + disabled={!hasRenamePermissions} + > + {t('rename')} diff --git a/services/web/frontend/js/features/ide-redesign/components/toolbar/toolbar.tsx b/services/web/frontend/js/features/ide-redesign/components/toolbar/toolbar.tsx index eddcb3ef95..86f948038b 100644 --- a/services/web/frontend/js/features/ide-redesign/components/toolbar/toolbar.tsx +++ b/services/web/frontend/js/features/ide-redesign/components/toolbar/toolbar.tsx @@ -4,14 +4,9 @@ import { ToolbarMenuBar } from './menu-bar' import { ToolbarProjectTitle } from './project-title' import { OnlineUsers } from './online-users' import ShareProjectButton from './share-project-button' -import importOverleafModules from '../../../../../macros/import-overleaf-module.macro' -import { useEditorContext } from '@/shared/context/editor-context' import ChangeLayoutButton from './change-layout-button' import ShowHistoryButton from './show-history-button' -const [publishModalModules] = importOverleafModules('publishModal') -const SubmitProjectButton = publishModalModules?.import.NewPublishToolbarButton - export const Toolbar = () => { return (
@@ -38,18 +33,11 @@ const ToolbarMenus = () => { } const ToolbarButtons = () => { - const { permissionsLevel } = useEditorContext() - - const shouldDisplaySubmitButton = - (permissionsLevel === 'owner' || permissionsLevel === 'readAndWrite') && - SubmitProjectButton - return (
- {shouldDisplaySubmitButton && }
) diff --git a/services/web/frontend/stylesheets/bootstrap-5/pages/editor/toolbar-redesign.scss b/services/web/frontend/stylesheets/bootstrap-5/pages/editor/toolbar-redesign.scss index f0f5005ca3..9a9a7e271d 100644 --- a/services/web/frontend/stylesheets/bootstrap-5/pages/editor/toolbar-redesign.scss +++ b/services/web/frontend/stylesheets/bootstrap-5/pages/editor/toolbar-redesign.scss @@ -142,3 +142,10 @@ .ide-redesign-online-users { display: flex; } + +.ide-redesign-toolbar-editable-project-name { + text-align: center; + width: 100%; + max-width: 400px; + margin: var(--spacing-02) 0; +} diff --git a/services/web/locales/en.json b/services/web/locales/en.json index 82d9f65fd4..cf755629db 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -554,6 +554,8 @@ "dont_reload_or_close_this_tab": "Don’t reload or close this tab.", "download": "Download", "download_all": "Download all", + "download_as_pdf": "Download as PDF", + "download_as_source_zip": "Download as source (.zip)", "download_metadata": "Download Overleaf metadata", "download_pdf": "Download PDF", "download_zip_file": "Download .zip file", @@ -587,6 +589,7 @@ "dropbox_unlinked_premium_feature": "<0>Your Dropbox account has been unlinked because Dropbox Sync is a premium feature that you had through an institutional license.", "due_date": "Due __date__", "due_today": "Due today", + "duplicate": "Duplicate", "duplicate_file": "Duplicate File", "duplicate_projects": "This user has projects with duplicate names", "each_user_will_have_access_to": "Each user will have access to",