mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-26 18:51:50 +02:00
Merge pull request #23697 from overleaf/dp-layout-switcher
Add layout switcher button to toolbar GitOrigin-RevId: 33bf664c2e2cb5c2f992b70fd9ca90b5dfe2ee44
This commit is contained in:
@@ -463,6 +463,7 @@
|
||||
"editor_and_pdf": "",
|
||||
"editor_disconected_click_to_reconnect": "",
|
||||
"editor_limit_exceeded_in_this_project": "",
|
||||
"editor_only": "",
|
||||
"editor_only_hide_pdf": "",
|
||||
"editor_settings": "",
|
||||
"editor_theme": "",
|
||||
@@ -858,6 +859,7 @@
|
||||
"latex_places_figures_according_to_a_special_algorithm": "",
|
||||
"latex_places_tables_according_to_a_special_algorithm": "",
|
||||
"layout": "",
|
||||
"layout_options": "",
|
||||
"layout_processing": "",
|
||||
"learn_more": "",
|
||||
"learn_more_about_account": "",
|
||||
@@ -1091,6 +1093,7 @@
|
||||
"open_file": "",
|
||||
"open_link": "",
|
||||
"open_path": "",
|
||||
"open_pdf_in_separate_tab": "",
|
||||
"open_project": "",
|
||||
"open_survey": "",
|
||||
"open_target": "",
|
||||
@@ -1150,6 +1153,7 @@
|
||||
"pdf_compile_try_again": "",
|
||||
"pdf_couldnt_compile": "",
|
||||
"pdf_in_separate_tab": "",
|
||||
"pdf_only": "",
|
||||
"pdf_only_hide_editor": "",
|
||||
"pdf_preview_error": "",
|
||||
"pdf_rendering_error": "",
|
||||
@@ -1531,6 +1535,7 @@
|
||||
"sort_projects": "",
|
||||
"source": "",
|
||||
"spell_check": "",
|
||||
"split_view": "",
|
||||
"sso": "",
|
||||
"sso_active": "",
|
||||
"sso_already_setup_good_to_go": "",
|
||||
|
||||
Binary file not shown.
@@ -18,6 +18,7 @@ export default /** @type {const} */ ([
|
||||
'rate_review',
|
||||
'report',
|
||||
'settings',
|
||||
'space_dashboard',
|
||||
'table_chart',
|
||||
'upload_file',
|
||||
'web_asset',
|
||||
|
||||
@@ -7,17 +7,26 @@ import { RailLayout } from './rail'
|
||||
import { Toolbar } from './toolbar/toolbar'
|
||||
import { HorizontalToggler } from '@/features/ide-react/components/resize/horizontal-toggler'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { usePdfPane } from '../hooks/use-pdf-pane'
|
||||
import { usePdfPane } from '@/features/ide-react/hooks/use-pdf-pane'
|
||||
import { useLayoutContext } from '@/shared/context/layout-context'
|
||||
import { useState } from 'react'
|
||||
|
||||
export default function MainLayout() {
|
||||
const [resizing, setResizing] = useState(false)
|
||||
const {
|
||||
isOpen: isPdfOpen,
|
||||
setIsOpen: setIsPdfOpen,
|
||||
panelRef: pdfPanelRef,
|
||||
handlePaneCollapse: handlePdfPaneCollapse,
|
||||
handlePaneExpand: handlePdfPaneExpand,
|
||||
togglePane: togglePdfPane,
|
||||
togglePdfPane,
|
||||
handlePdfPaneExpand,
|
||||
handlePdfPaneCollapse,
|
||||
setPdfIsOpen: setIsPdfOpen,
|
||||
pdfIsOpen: isPdfOpen,
|
||||
pdfPanelRef,
|
||||
} = usePdfPane()
|
||||
|
||||
const { view, pdfLayout } = useLayoutContext()
|
||||
|
||||
const editorIsOpen =
|
||||
view === 'editor' || view === 'file' || pdfLayout === 'sideBySide'
|
||||
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<div className="ide-redesign-main">
|
||||
@@ -27,19 +36,32 @@ export default function MainLayout() {
|
||||
autoSaveId="ide-redesign-outer-layout"
|
||||
direction="horizontal"
|
||||
className={classNames('ide-redesign-inner', {
|
||||
'ide-panel-group-resizing': false,
|
||||
'ide-panel-group-resizing': resizing,
|
||||
})}
|
||||
>
|
||||
<RailLayout />
|
||||
<Panel id="ide-redesign-editor-panel" order={2}>
|
||||
<Panel
|
||||
id="ide-redesign-editor-panel"
|
||||
order={1}
|
||||
className={classNames({
|
||||
'ide-panel-group-resizing': resizing,
|
||||
hidden: !editorIsOpen,
|
||||
})}
|
||||
minSize={5}
|
||||
defaultSize={50}
|
||||
>
|
||||
<div className="ide-redesign-editor-container">
|
||||
<Editor />
|
||||
</div>
|
||||
</Panel>
|
||||
<HorizontalResizeHandle
|
||||
resizable
|
||||
resizable={pdfLayout === 'sideBySide'}
|
||||
onDragging={setResizing}
|
||||
onDoubleClick={togglePdfPane}
|
||||
hitAreaMargins={{ coarse: 0, fine: 0 }}
|
||||
className={classNames({
|
||||
hidden: !editorIsOpen,
|
||||
})}
|
||||
>
|
||||
<HorizontalToggler
|
||||
id="ide-redesign-pdf-panel"
|
||||
@@ -55,6 +77,8 @@ export default function MainLayout() {
|
||||
className="ide-redesign-pdf-container"
|
||||
id="ide-redesign-pdf-panel"
|
||||
order={2}
|
||||
defaultSize={50}
|
||||
minSize={5}
|
||||
ref={pdfPanelRef}
|
||||
onExpand={handlePdfPaneExpand}
|
||||
onCollapse={handlePdfPaneCollapse}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import OLButton from '@/features/ui/components/ol/ol-button'
|
||||
import MaterialIcon from '@/shared/components/material-icon'
|
||||
import {
|
||||
Dropdown,
|
||||
DropdownMenu,
|
||||
DropdownToggle,
|
||||
} from '@/features/ui/components/bootstrap-5/dropdown-menu'
|
||||
import React, { forwardRef } from 'react'
|
||||
import ChangeLayoutOptions from './change-layout-options'
|
||||
|
||||
const LayoutDropdownToggleButton = forwardRef<
|
||||
HTMLButtonElement,
|
||||
{
|
||||
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void
|
||||
}
|
||||
>(({ onClick }, ref) => {
|
||||
return (
|
||||
<OLButton
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="ide-redesign-toolbar-button-subdued"
|
||||
ref={ref}
|
||||
onClick={onClick}
|
||||
leadingIcon={<MaterialIcon unfilled type="space_dashboard" />}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
LayoutDropdownToggleButton.displayName = 'LayoutDropdownToggleButton'
|
||||
|
||||
export default function ChangeLayoutButton() {
|
||||
return (
|
||||
<div className="ide-redesign-toolbar-button-container">
|
||||
<Dropdown className="toolbar-item layout-dropdown" align="end">
|
||||
<DropdownToggle
|
||||
id="layout-dropdown-btn"
|
||||
className="btn-full-height"
|
||||
as={LayoutDropdownToggleButton}
|
||||
/>
|
||||
<DropdownMenu>
|
||||
<ChangeLayoutOptions />
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
import {
|
||||
DropdownItem,
|
||||
DropdownHeader,
|
||||
} from '@/features/ui/components/bootstrap-5/dropdown-menu'
|
||||
import {
|
||||
IdeLayout,
|
||||
IdeView,
|
||||
useLayoutContext,
|
||||
} from '@/shared/context/layout-context'
|
||||
import React, { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import * as eventTracking from '../../../../infrastructure/event-tracking'
|
||||
import useEventListener from '@/shared/hooks/use-event-listener'
|
||||
import { DetachRole } from '@/shared/context/detach-context'
|
||||
import { Spinner } from 'react-bootstrap-5'
|
||||
|
||||
type LayoutOption = 'sideBySide' | 'editorOnly' | 'pdfOnly' | 'detachedPdf'
|
||||
|
||||
const getActiveLayoutOption = ({
|
||||
pdfLayout,
|
||||
view,
|
||||
detachRole,
|
||||
}: {
|
||||
pdfLayout: IdeLayout
|
||||
view: IdeView | null
|
||||
detachRole?: DetachRole
|
||||
}): LayoutOption | null => {
|
||||
if (view === 'history') {
|
||||
return null
|
||||
}
|
||||
|
||||
if (detachRole === 'detacher') {
|
||||
return 'detachedPdf'
|
||||
}
|
||||
|
||||
if (pdfLayout === 'flat' && (view === 'editor' || view === 'file')) {
|
||||
return 'editorOnly'
|
||||
}
|
||||
|
||||
if (pdfLayout === 'flat' && view === 'pdf') {
|
||||
return 'pdfOnly'
|
||||
}
|
||||
|
||||
if (pdfLayout === 'sideBySide') {
|
||||
return 'sideBySide'
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
const LayoutDropdownItem = ({
|
||||
active,
|
||||
disabled = false,
|
||||
processing = false,
|
||||
leadingIcon,
|
||||
onClick,
|
||||
children,
|
||||
}: {
|
||||
active: boolean
|
||||
leadingIcon: string
|
||||
onClick: () => void
|
||||
children: React.ReactNode
|
||||
processing?: boolean
|
||||
disabled?: boolean
|
||||
}) => {
|
||||
let trailingIcon: string | React.ReactNode | null = null
|
||||
if (processing) {
|
||||
trailingIcon = (
|
||||
<Spinner animation="border" aria-hidden="true" size="sm" role="status" />
|
||||
)
|
||||
} else if (active) {
|
||||
trailingIcon = 'check'
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownItem
|
||||
active={active}
|
||||
aria-current={active}
|
||||
trailingIcon={trailingIcon}
|
||||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
leadingIcon={leadingIcon}
|
||||
>
|
||||
{children}
|
||||
</DropdownItem>
|
||||
)
|
||||
}
|
||||
|
||||
export default function ChangeLayoutOptions() {
|
||||
const {
|
||||
reattach,
|
||||
detach,
|
||||
detachIsLinked,
|
||||
detachRole,
|
||||
changeLayout,
|
||||
view,
|
||||
pdfLayout,
|
||||
} = useLayoutContext()
|
||||
|
||||
const handleDetach = useCallback(() => {
|
||||
detach()
|
||||
eventTracking.sendMB('project-layout-detach')
|
||||
}, [detach])
|
||||
|
||||
const handleReattach = useCallback(() => {
|
||||
if (detachRole !== 'detacher') {
|
||||
return
|
||||
}
|
||||
reattach()
|
||||
eventTracking.sendMB('project-layout-reattach')
|
||||
}, [detachRole, reattach])
|
||||
|
||||
// reattach when the PDF pane opens
|
||||
useEventListener('ui:pdf-open', handleReattach)
|
||||
|
||||
const handleChangeLayout = useCallback(
|
||||
(newLayout: IdeLayout, newView?: IdeView) => {
|
||||
handleReattach()
|
||||
changeLayout(newLayout, newView)
|
||||
eventTracking.sendMB('project-layout-change', {
|
||||
layout: newLayout,
|
||||
view: newView,
|
||||
})
|
||||
},
|
||||
[changeLayout, handleReattach]
|
||||
)
|
||||
|
||||
const { t } = useTranslation()
|
||||
|
||||
const detachable = 'BroadcastChannel' in window
|
||||
|
||||
const activeLayoutOption = getActiveLayoutOption({
|
||||
pdfLayout,
|
||||
view,
|
||||
detachRole,
|
||||
})
|
||||
|
||||
const waitingForDetachedLink = !detachIsLinked && detachRole === 'detacher'
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownHeader>{t('layout_options')}</DropdownHeader>
|
||||
<LayoutDropdownItem
|
||||
onClick={() => handleChangeLayout('sideBySide')}
|
||||
active={activeLayoutOption === 'sideBySide'}
|
||||
leadingIcon="splitscreen_right"
|
||||
>
|
||||
{t('split_view')}
|
||||
</LayoutDropdownItem>
|
||||
<LayoutDropdownItem
|
||||
onClick={() => handleChangeLayout('flat', 'editor')}
|
||||
active={activeLayoutOption === 'editorOnly'}
|
||||
leadingIcon="edit"
|
||||
>
|
||||
{t('editor_only')}
|
||||
</LayoutDropdownItem>
|
||||
<LayoutDropdownItem
|
||||
onClick={() => handleChangeLayout('flat', 'pdf')}
|
||||
active={activeLayoutOption === 'pdfOnly'}
|
||||
leadingIcon="picture_as_pdf"
|
||||
>
|
||||
{t('pdf_only')}
|
||||
</LayoutDropdownItem>
|
||||
<LayoutDropdownItem
|
||||
onClick={() => handleDetach()}
|
||||
active={activeLayoutOption === 'detachedPdf' && detachIsLinked}
|
||||
disabled={!detachable}
|
||||
leadingIcon="open_in_new"
|
||||
processing={waitingForDetachedLink}
|
||||
>
|
||||
{t('open_pdf_in_separate_tab')}
|
||||
</LayoutDropdownItem>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { MenuBar } from '@/shared/components/menu-bar/menu-bar'
|
||||
import { MenuBarDropdown } from '@/shared/components/menu-bar/menu-bar-dropdown'
|
||||
import { MenuBarOption } from '@/shared/components/menu-bar/menu-bar-option'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ChangeLayoutOptions from './change-layout-options'
|
||||
|
||||
export const ToolbarMenuBar = () => {
|
||||
const { t } = useTranslation()
|
||||
@@ -36,7 +37,7 @@ export const ToolbarMenuBar = () => {
|
||||
id="view"
|
||||
className="ide-redesign-toolbar-dropdown-toggle-subdued"
|
||||
>
|
||||
<MenuBarOption title="PDF only" />
|
||||
<ChangeLayoutOptions />
|
||||
</MenuBarDropdown>
|
||||
<MenuBarDropdown
|
||||
title={t('insert')}
|
||||
|
||||
@@ -8,6 +8,7 @@ import ShareProjectButton from './share-project-button'
|
||||
import importOverleafModules from '../../../../../macros/import-overleaf-module.macro'
|
||||
import { useEditorContext } from '@/shared/context/editor-context'
|
||||
import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
|
||||
import ChangeLayoutButton from './change-layout-button'
|
||||
|
||||
const [publishModalModules] = importOverleafModules('publishModal')
|
||||
const SubmitProjectButton = publishModalModules?.import.NewPublishToolbarButton
|
||||
@@ -66,6 +67,7 @@ const ToolbarButtons = () => {
|
||||
/>
|
||||
</OLTooltip>
|
||||
</div>
|
||||
<ChangeLayoutButton />
|
||||
{shouldDisplaySubmitButton && <SubmitProjectButton />}
|
||||
<ShareProjectButton />
|
||||
</div>
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
import useCollapsiblePanel from '@/features/ide-react/hooks/use-collapsible-panel'
|
||||
import { ImperativePanelHandle } from 'react-resizable-panels'
|
||||
|
||||
// FIXME: This is temporary, to avoid clashing with the existing usePdfPane
|
||||
// which uses the layout context. That's the correct approach.
|
||||
export const usePdfPane = () => {
|
||||
const [isOpen, setIsOpen] = useState(true)
|
||||
const [resizing, setResizing] = useState(false)
|
||||
const panelRef = useRef<ImperativePanelHandle>(null)
|
||||
useCollapsiblePanel(isOpen, panelRef)
|
||||
|
||||
const togglePane = useCallback(() => {
|
||||
setIsOpen(value => !value)
|
||||
}, [])
|
||||
|
||||
const handlePaneExpand = useCallback(() => {
|
||||
setIsOpen(true)
|
||||
}, [])
|
||||
|
||||
const handlePaneCollapse = useCallback(() => {
|
||||
setIsOpen(false)
|
||||
}, [])
|
||||
|
||||
return {
|
||||
isOpen,
|
||||
setIsOpen,
|
||||
panelRef,
|
||||
togglePane,
|
||||
handlePaneExpand,
|
||||
handlePaneCollapse,
|
||||
resizing,
|
||||
setResizing,
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,18 @@ $editor-toggler-bg-dark-color: color.adjust(
|
||||
}
|
||||
}
|
||||
|
||||
.ide-redesign-main {
|
||||
.ide-panel-group-resizing {
|
||||
background-color: var(--white);
|
||||
|
||||
// Hide panel contents while resizing
|
||||
.ide-redesign-editor-content,
|
||||
.pdf {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.global-alerts {
|
||||
height: 0;
|
||||
margin-top: var(--spacing-01);
|
||||
|
||||
@@ -598,6 +598,7 @@
|
||||
"editor_and_pdf": "Editor & PDF",
|
||||
"editor_disconected_click_to_reconnect": "Editor disconnected, click anywhere to reconnect.",
|
||||
"editor_limit_exceeded_in_this_project": "Too many editors in this project",
|
||||
"editor_only": "Editor only",
|
||||
"editor_only_hide_pdf": "Editor only <0>(hide PDF)</0>",
|
||||
"editor_settings": "Editor settings",
|
||||
"editor_theme": "Editor theme",
|
||||
@@ -1131,6 +1132,7 @@
|
||||
"latex_templates_for_journal_articles": "LaTeX templates for journal articles, academic papers, CVs and résumés, presentations, and more.",
|
||||
"latex_templates_sentence_case": "LaTeX templates",
|
||||
"layout": "Layout",
|
||||
"layout_options": "Layout options",
|
||||
"layout_processing": "Layout processing",
|
||||
"ldap": "LDAP",
|
||||
"ldap_create_admin_instructions": "Choose an email address for the first __appName__ admin account. This should correspond to an account in the LDAP system. You will then be asked to log in with this account.",
|
||||
@@ -1448,6 +1450,7 @@
|
||||
"open_file": "Edit file",
|
||||
"open_link": "Go to page",
|
||||
"open_path": "Open __path__",
|
||||
"open_pdf_in_separate_tab": "Open PDF in separate tab",
|
||||
"open_project": "Open Project",
|
||||
"open_survey": "Open survey",
|
||||
"open_target": "Go to target",
|
||||
@@ -1537,6 +1540,7 @@
|
||||
"pdf_compile_try_again": "Please wait for your other compile to finish before trying again.",
|
||||
"pdf_couldnt_compile": "PDF couldn’t compile",
|
||||
"pdf_in_separate_tab": "PDF in separate tab",
|
||||
"pdf_only": "PDF only",
|
||||
"pdf_only_hide_editor": "PDF only <0>(hide editor)</0>",
|
||||
"pdf_preview_error": "There was a problem displaying the compilation results for this project.",
|
||||
"pdf_rendering_error": "PDF Rendering Error",
|
||||
@@ -2007,6 +2011,7 @@
|
||||
"sort_projects": "Sort projects",
|
||||
"source": "Source",
|
||||
"spell_check": "Spell check",
|
||||
"split_view": "Split view",
|
||||
"sso": "SSO",
|
||||
"sso_account_already_linked": "Account already linked to another __appName__ user",
|
||||
"sso_active": "SSO active",
|
||||
|
||||
Reference in New Issue
Block a user