mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-14 22:23:42 +01:00
[web] Editor redesign: Add actions to project name dropdown (#24220)
GitOrigin-RevId: 04f4abdc529a0494c70b0e3d14847b0cf452b80d
This commit is contained in:
committed by
Copybot
parent
0a726ff003
commit
ae4e5b30c1
@@ -427,6 +427,8 @@
|
|||||||
"dont_reload_or_close_this_tab": "",
|
"dont_reload_or_close_this_tab": "",
|
||||||
"download": "",
|
"download": "",
|
||||||
"download_all": "",
|
"download_all": "",
|
||||||
|
"download_as_pdf": "",
|
||||||
|
"download_as_source_zip": "",
|
||||||
"download_metadata": "",
|
"download_metadata": "",
|
||||||
"download_pdf": "",
|
"download_pdf": "",
|
||||||
"download_zip_file": "",
|
"download_zip_file": "",
|
||||||
|
|||||||
+69
@@ -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 (
|
||||||
|
<OLDropdownMenuItem
|
||||||
|
href={`/project/${projectId}/download/zip`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
onClick={sendDownloadEvent}
|
||||||
|
>
|
||||||
|
{t('download_as_source_zip')}
|
||||||
|
</OLDropdownMenuItem>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = (
|
||||||
|
<OLDropdownMenuItem
|
||||||
|
href={pdfDownloadUrl || pdfUrl}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
onClick={sendDownloadEvent}
|
||||||
|
disabled={!pdfUrl}
|
||||||
|
>
|
||||||
|
{t('download_as_pdf')}
|
||||||
|
</OLDropdownMenuItem>
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!pdfUrl) {
|
||||||
|
return (
|
||||||
|
<OLTooltip
|
||||||
|
id="tooltip-download-pdf-unavailable"
|
||||||
|
description={t('please_compile_pdf_before_download')}
|
||||||
|
overlayProps={{ placement: 'right', delay: 0 }}
|
||||||
|
>
|
||||||
|
<span>{button}</span>
|
||||||
|
</OLTooltip>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return button
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<HTMLInputElement | null>(null)
|
||||||
|
useEffect(() => {
|
||||||
|
inputRef.current?.select()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const onInputChange: ChangeEventHandler<HTMLInputElement> = 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 (
|
||||||
|
<OLFormControl
|
||||||
|
className={className}
|
||||||
|
ref={inputRef}
|
||||||
|
type="text"
|
||||||
|
value={name}
|
||||||
|
onChange={onInputChange}
|
||||||
|
onKeyDown={onKeyDown}
|
||||||
|
onBlur={finishRenaming}
|
||||||
|
maxLength={maxLength}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default EditableLabel
|
||||||
+57
-8
@@ -4,14 +4,53 @@ import {
|
|||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownToggle,
|
DropdownToggle,
|
||||||
} from '@/features/ui/components/bootstrap-5/dropdown-menu'
|
} 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 MaterialIcon from '@/shared/components/material-icon'
|
||||||
import { useProjectContext } from '@/shared/context/project-context'
|
import { useProjectContext } from '@/shared/context/project-context'
|
||||||
import { useTranslation } from 'react-i18next'
|
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 = () => {
|
export const ToolbarProjectTitle = () => {
|
||||||
const { name } = useProjectContext()
|
|
||||||
const { t } = useTranslation()
|
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 (
|
||||||
|
<EditableLabel
|
||||||
|
onChange={onRename}
|
||||||
|
onCancel={onCancel}
|
||||||
|
initialText={name}
|
||||||
|
maxLength={150}
|
||||||
|
className="ide-redesign-toolbar-editable-project-name"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown align="start">
|
<Dropdown align="start">
|
||||||
<DropdownToggle
|
<DropdownToggle
|
||||||
@@ -24,13 +63,23 @@ export const ToolbarProjectTitle = () => {
|
|||||||
accessibilityLabel={t('project_title_options')}
|
accessibilityLabel={t('project_title_options')}
|
||||||
/>
|
/>
|
||||||
</DropdownToggle>
|
</DropdownToggle>
|
||||||
<DropdownMenu>
|
<DropdownMenu renderOnMount>
|
||||||
<OLDropdownMenuItem>TODO: Export</OLDropdownMenuItem>
|
{shouldDisplaySubmitButton && (
|
||||||
|
<>
|
||||||
|
<SubmitProjectButton />
|
||||||
|
<DropdownDivider />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<DownloadProjectPDF />
|
||||||
|
<DownloadProjectZip />
|
||||||
<DropdownDivider />
|
<DropdownDivider />
|
||||||
<OLDropdownMenuItem>{t('rename')}</OLDropdownMenuItem>
|
<OLDropdownMenuItem
|
||||||
<OLDropdownMenuItem>{t('download')}</OLDropdownMenuItem>
|
onClick={() => {
|
||||||
<OLDropdownMenuItem className="text-danger">
|
setIsRenaming(true)
|
||||||
{t('delete')}
|
}}
|
||||||
|
disabled={!hasRenamePermissions}
|
||||||
|
>
|
||||||
|
{t('rename')}
|
||||||
</OLDropdownMenuItem>
|
</OLDropdownMenuItem>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
|||||||
@@ -4,14 +4,9 @@ import { ToolbarMenuBar } from './menu-bar'
|
|||||||
import { ToolbarProjectTitle } from './project-title'
|
import { ToolbarProjectTitle } from './project-title'
|
||||||
import { OnlineUsers } from './online-users'
|
import { OnlineUsers } from './online-users'
|
||||||
import ShareProjectButton from './share-project-button'
|
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 ChangeLayoutButton from './change-layout-button'
|
||||||
import ShowHistoryButton from './show-history-button'
|
import ShowHistoryButton from './show-history-button'
|
||||||
|
|
||||||
const [publishModalModules] = importOverleafModules('publishModal')
|
|
||||||
const SubmitProjectButton = publishModalModules?.import.NewPublishToolbarButton
|
|
||||||
|
|
||||||
export const Toolbar = () => {
|
export const Toolbar = () => {
|
||||||
return (
|
return (
|
||||||
<div className="ide-redesign-toolbar">
|
<div className="ide-redesign-toolbar">
|
||||||
@@ -38,18 +33,11 @@ const ToolbarMenus = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ToolbarButtons = () => {
|
const ToolbarButtons = () => {
|
||||||
const { permissionsLevel } = useEditorContext()
|
|
||||||
|
|
||||||
const shouldDisplaySubmitButton =
|
|
||||||
(permissionsLevel === 'owner' || permissionsLevel === 'readAndWrite') &&
|
|
||||||
SubmitProjectButton
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ide-redesign-toolbar-actions">
|
<div className="ide-redesign-toolbar-actions">
|
||||||
<OnlineUsers />
|
<OnlineUsers />
|
||||||
<ShowHistoryButton />
|
<ShowHistoryButton />
|
||||||
<ChangeLayoutButton />
|
<ChangeLayoutButton />
|
||||||
{shouldDisplaySubmitButton && <SubmitProjectButton />}
|
|
||||||
<ShareProjectButton />
|
<ShareProjectButton />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -142,3 +142,10 @@
|
|||||||
.ide-redesign-online-users {
|
.ide-redesign-online-users {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ide-redesign-toolbar-editable-project-name {
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
margin: var(--spacing-02) 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -554,6 +554,8 @@
|
|||||||
"dont_reload_or_close_this_tab": "Don’t reload or close this tab.",
|
"dont_reload_or_close_this_tab": "Don’t reload or close this tab.",
|
||||||
"download": "Download",
|
"download": "Download",
|
||||||
"download_all": "Download all",
|
"download_all": "Download all",
|
||||||
|
"download_as_pdf": "Download as PDF",
|
||||||
|
"download_as_source_zip": "Download as source (.zip)",
|
||||||
"download_metadata": "Download Overleaf metadata",
|
"download_metadata": "Download Overleaf metadata",
|
||||||
"download_pdf": "Download PDF",
|
"download_pdf": "Download PDF",
|
||||||
"download_zip_file": "Download .zip file",
|
"download_zip_file": "Download .zip file",
|
||||||
@@ -587,6 +589,7 @@
|
|||||||
"dropbox_unlinked_premium_feature": "<0>Your Dropbox account has been unlinked</0> because Dropbox Sync is a premium feature that you had through an institutional license.",
|
"dropbox_unlinked_premium_feature": "<0>Your Dropbox account has been unlinked</0> because Dropbox Sync is a premium feature that you had through an institutional license.",
|
||||||
"due_date": "Due __date__",
|
"due_date": "Due __date__",
|
||||||
"due_today": "Due today",
|
"due_today": "Due today",
|
||||||
|
"duplicate": "Duplicate",
|
||||||
"duplicate_file": "Duplicate File",
|
"duplicate_file": "Duplicate File",
|
||||||
"duplicate_projects": "This user has projects with duplicate names",
|
"duplicate_projects": "This user has projects with duplicate names",
|
||||||
"each_user_will_have_access_to": "Each user will have access to",
|
"each_user_will_have_access_to": "Each user will have access to",
|
||||||
|
|||||||
Reference in New Issue
Block a user