Merge pull request #22928 from overleaf/mj-ide-toolbar
[web] Initial editor toolbar GitOrigin-RevId: 33658d886bf86cbaa22a161f2ff463ef608e5f6b
This commit is contained in:
committed by
Copybot
parent
71ab3a41ed
commit
4b46120ec1
@@ -507,6 +507,7 @@
|
||||
"failed_to_send_sso_link_invite_to_email": "",
|
||||
"fast": "",
|
||||
"features_like_track_changes": "",
|
||||
"file": "",
|
||||
"file_action_created": "",
|
||||
"file_action_deleted": "",
|
||||
"file_action_edited": "",
|
||||
@@ -1082,6 +1083,7 @@
|
||||
"overleaf": "",
|
||||
"overleaf_history_system": "",
|
||||
"overleaf_labs": "",
|
||||
"overleaf_logo": "",
|
||||
"overview": "",
|
||||
"overwrite": "",
|
||||
"overwriting_the_original_folder": "",
|
||||
@@ -1210,6 +1212,7 @@
|
||||
"project_timed_out_intro": "",
|
||||
"project_timed_out_learn_more": "",
|
||||
"project_timed_out_optimize_images": "",
|
||||
"project_title_options": "",
|
||||
"project_too_large": "",
|
||||
"project_too_large_please_reduce": "",
|
||||
"project_too_much_editable_text": "",
|
||||
@@ -1863,6 +1866,7 @@
|
||||
"vat": "",
|
||||
"vat_number": "",
|
||||
"verify_email_address_before_enabling_managed_users": "",
|
||||
"view": "",
|
||||
"view_all": "",
|
||||
"view_code": "",
|
||||
"view_configuration": "",
|
||||
|
||||
@@ -21,7 +21,7 @@ import { debugConsole } from '@/utils/debugging'
|
||||
import { IdeEvents } from '@/features/ide-react/create-ide-event-emitter'
|
||||
import { getHueForUserId } from '@/shared/utils/colors'
|
||||
|
||||
type OnlineUser = {
|
||||
export type OnlineUser = {
|
||||
id: string
|
||||
user_id: string
|
||||
email: string
|
||||
|
||||
@@ -4,11 +4,12 @@ import { HorizontalResizeHandle } from '@/features/ide-react/components/resize/h
|
||||
import PdfPreview from '@/features/pdf-preview/components/pdf-preview'
|
||||
import { Editor } from './editor'
|
||||
import { RailLayout } from './rail'
|
||||
import { Toolbar } from './toolbar/toolbar'
|
||||
|
||||
export default function MainLayout() {
|
||||
return (
|
||||
<div className="ide-redesign-main">
|
||||
<div className="ide-skeleton-block">Toolbar</div>
|
||||
<Toolbar />
|
||||
<div className="ide-redesign-body">
|
||||
<PanelGroup
|
||||
autoSaveId="ide-redesign-outer-layout"
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import {
|
||||
Dropdown,
|
||||
DropdownDivider,
|
||||
DropdownMenu,
|
||||
DropdownToggle,
|
||||
} from '@/features/ui/components/bootstrap-5/dropdown-menu'
|
||||
import OLDropdownMenuItem from '@/features/ui/components/ol/ol-dropdown-menu-item'
|
||||
import { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
type MenuBarOptionProps = {
|
||||
title: string
|
||||
onClick?: () => void
|
||||
}
|
||||
|
||||
type MenuBarDropdownProps = {
|
||||
title: string
|
||||
id: string
|
||||
}
|
||||
|
||||
export const ToolbarMenuBar = () => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<div className="ide-redesign-toolbar-menu-bar">
|
||||
<MenuBarDropdown title={t('file')} id="file">
|
||||
<MenuBarOption title="New File" />
|
||||
<MenuBarOption title="New Project" />
|
||||
</MenuBarDropdown>
|
||||
<MenuBarDropdown title={t('edit')} id="edit">
|
||||
<MenuBarOption title="Undo" />
|
||||
<MenuBarOption title="Redo" />
|
||||
<DropdownDivider />
|
||||
<MenuBarOption title="Cut" />
|
||||
<MenuBarOption title="Copy" />
|
||||
<MenuBarOption title="Pate" />
|
||||
</MenuBarDropdown>
|
||||
<MenuBarDropdown title={t('view')} id="view">
|
||||
<MenuBarOption title="PDF only" />
|
||||
</MenuBarDropdown>
|
||||
<MenuBarDropdown title={t('insert')} id="insert">
|
||||
<MenuBarOption title="Insert figure" />
|
||||
<MenuBarOption title="Insert table" />
|
||||
<MenuBarOption title="Insert link" />
|
||||
<MenuBarOption title="Add comment" />
|
||||
</MenuBarDropdown>
|
||||
<MenuBarDropdown title={t('format')} id="format">
|
||||
<MenuBarOption title="Bold text" />
|
||||
</MenuBarDropdown>
|
||||
<MenuBarDropdown title={t('help')} id="help">
|
||||
<MenuBarOption title="Keyboard shortcuts" />
|
||||
<MenuBarOption title="Documentation" />
|
||||
<DropdownDivider />
|
||||
<MenuBarOption title="Contact us" />
|
||||
<MenuBarOption title="Give feedback" />
|
||||
</MenuBarDropdown>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const MenuBarDropdown: FC<MenuBarDropdownProps> = ({ title, children, id }) => {
|
||||
return (
|
||||
<Dropdown align="start">
|
||||
<DropdownToggle
|
||||
id={`toolbar-menu-bar-item-${id}`}
|
||||
variant="secondary"
|
||||
className="ide-redesign-toolbar-dropdown-toggle-subdued"
|
||||
>
|
||||
{title}
|
||||
</DropdownToggle>
|
||||
<DropdownMenu>{children}</DropdownMenu>
|
||||
</Dropdown>
|
||||
)
|
||||
}
|
||||
|
||||
const MenuBarOption = ({ title, onClick }: MenuBarOptionProps) => {
|
||||
return <OLDropdownMenuItem onClick={onClick}>{title}</OLDropdownMenuItem>
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import OnlineUsersWidget from '@/features/editor-navigation-toolbar/components/online-users-widget'
|
||||
import { useEditorManagerContext } from '@/features/ide-react/context/editor-manager-context'
|
||||
import {
|
||||
OnlineUser,
|
||||
useOnlineUsersContext,
|
||||
} from '@/features/ide-react/context/online-users-context'
|
||||
import { useCallback } from 'react'
|
||||
|
||||
export const OnlineUsers = () => {
|
||||
const { openDoc } = useEditorManagerContext()
|
||||
const { onlineUsersArray } = useOnlineUsersContext()
|
||||
|
||||
const goToUser = useCallback(
|
||||
(user: OnlineUser) => {
|
||||
if (user.doc && typeof user.row === 'number') {
|
||||
openDoc(user.doc, { gotoLine: user.row + 1 })
|
||||
}
|
||||
},
|
||||
[openDoc]
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="ide-redesign-online-users">
|
||||
<OnlineUsersWidget onlineUsers={onlineUsersArray} goToUser={goToUser} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import {
|
||||
Dropdown,
|
||||
DropdownDivider,
|
||||
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'
|
||||
|
||||
export const ToolbarProjectTitle = () => {
|
||||
const { name } = useProjectContext()
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<Dropdown align="start">
|
||||
<DropdownToggle
|
||||
id="project-title-options"
|
||||
className="ide-redesign-toolbar-dropdown-toggle-subdued"
|
||||
variant="secondary"
|
||||
>
|
||||
{name}
|
||||
<MaterialIcon
|
||||
type="keyboard_arrow_down"
|
||||
accessibilityLabel={t('project_title_options')}
|
||||
/>
|
||||
</DropdownToggle>
|
||||
<DropdownMenu>
|
||||
<OLDropdownMenuItem>TODO: Export</OLDropdownMenuItem>
|
||||
<DropdownDivider />
|
||||
<OLDropdownMenuItem>{t('rename')}</OLDropdownMenuItem>
|
||||
<OLDropdownMenuItem>{t('download')}</OLDropdownMenuItem>
|
||||
<OLDropdownMenuItem className="text-danger">
|
||||
{t('delete')}
|
||||
</OLDropdownMenuItem>
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import OLButton from '@/features/ui/components/ol/ol-button'
|
||||
import MaterialIcon from '@/shared/components/material-icon'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { ToolbarMenuBar } from './menu-bar'
|
||||
import { ToolbarProjectTitle } from './project-title'
|
||||
import { OnlineUsers } from './online-users'
|
||||
|
||||
export const Toolbar = () => {
|
||||
return (
|
||||
<div className="ide-redesign-toolbar">
|
||||
<ToolbarMenus />
|
||||
<ToolbarProjectTitle />
|
||||
<ToolbarButtons />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const ToolbarMenus = () => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<div className="ide-redesign-toolbar-menu">
|
||||
<div className="ide-redesign-toolbar-home-button">
|
||||
<a href="/project" className="ide-redesign-toolbar-home-link">
|
||||
<img
|
||||
className="toolbar-ol-logo"
|
||||
src="/img/ol-brand/overleaf-o-dark.svg"
|
||||
alt={t('overleaf_logo')}
|
||||
/>
|
||||
<MaterialIcon type="home" className="toolbar-ol-home-button" />
|
||||
</a>
|
||||
</div>
|
||||
<ToolbarMenuBar />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const ToolbarButtons = () => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<div className="ide-redesign-toolbar-actions">
|
||||
<OnlineUsers />
|
||||
<div className="ide-redesign-toolbar-button-container">
|
||||
<OLButton
|
||||
variant="link"
|
||||
className="ide-redesign-toolbar-button-subdued"
|
||||
leadingIcon={<MaterialIcon type="history" />}
|
||||
/>
|
||||
</div>
|
||||
<div className="ide-redesign-toolbar-button-container">
|
||||
<OLButton
|
||||
variant="link"
|
||||
className="ide-redesign-toolbar-button-subdued"
|
||||
leadingIcon={<MaterialIcon type="send" />}
|
||||
>
|
||||
{t('submit_title')}
|
||||
</OLButton>
|
||||
</div>
|
||||
<div className="ide-redesign-toolbar-button-container">
|
||||
<OLButton
|
||||
variant="primary"
|
||||
leadingIcon={<MaterialIcon type="person_add" />}
|
||||
>
|
||||
{t('share')}
|
||||
</OLButton>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
@import 'editor/rail';
|
||||
@import 'editor/settings';
|
||||
@import 'editor/toolbar';
|
||||
@import 'editor/toolbar-redesign';
|
||||
@import 'editor/online-users';
|
||||
@import 'editor/hotkeys';
|
||||
@import 'editor/left-menu';
|
||||
|
||||
@@ -54,11 +54,11 @@
|
||||
height: 100%;
|
||||
padding: var(--spacing-02);
|
||||
background: var(--ide-rail-background);
|
||||
border-right: 1px solid var(--border-divider);
|
||||
}
|
||||
|
||||
.ide-rail-content {
|
||||
height: 100%;
|
||||
border: 1px solid var(--border-divider);
|
||||
}
|
||||
|
||||
.ide-rail-tabs-nav {
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
@use 'sass:math';
|
||||
|
||||
:root {
|
||||
--toolbar-home-button-hover-background: var(--neutral-20);
|
||||
}
|
||||
|
||||
.ide-redesign-toolbar {
|
||||
$toolbar-height: 48px;
|
||||
$home-button-size: 36px;
|
||||
$ol-icon-height: 24px;
|
||||
$home-icon-font-size: 24px;
|
||||
|
||||
border-bottom: 1px solid var(--border-divider);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
box-sizing: border-box;
|
||||
height: $toolbar-height;
|
||||
|
||||
.ide-redesign-toolbar-menu {
|
||||
display: flex;
|
||||
gap: var(--spacing-05);
|
||||
}
|
||||
|
||||
.ide-redesign-toolbar-home-button {
|
||||
width: $home-button-size;
|
||||
height: $home-button-size;
|
||||
margin: math.div($toolbar-height - $home-button-size, 2);
|
||||
}
|
||||
|
||||
.ide-redesign-toolbar-home-link {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
border-radius: 100%;
|
||||
|
||||
.toolbar-ol-home-button {
|
||||
display: none;
|
||||
font-size: $home-icon-font-size;
|
||||
}
|
||||
|
||||
.toolbar-ol-logo {
|
||||
margin: 0 auto;
|
||||
height: $ol-icon-height;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--toolbar-home-button-hover-background);
|
||||
|
||||
.toolbar-ol-home-button {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.toolbar-ol-logo {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ide-redesign-toolbar-dropdown-toggle-subdued {
|
||||
$height: 32px;
|
||||
|
||||
border: none;
|
||||
border-radius: var(--border-radius-base);
|
||||
padding: var(--spacing-02);
|
||||
height: $height;
|
||||
font-size: var(--font-size-03);
|
||||
line-height: var(--line-height-03);
|
||||
font-weight: 400;
|
||||
box-sizing: border-box;
|
||||
margin: math.div($toolbar-height - $height, 2) 0;
|
||||
|
||||
&.dropdown-toggle::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.material-symbols {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ide-redesign-toolbar-menu-bar {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
height: 100%;
|
||||
gap: var(--spacing-05);
|
||||
}
|
||||
|
||||
.ide-redesign-toolbar-actions {
|
||||
display: flex;
|
||||
gap: var(--spacing-06);
|
||||
padding: 0 var(--spacing-05);
|
||||
}
|
||||
|
||||
.ide-redesign-toolbar-button-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.ide-redesign-toolbar-button-subdued {
|
||||
color: var(--neutral-90);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.ide-redesign-online-users {
|
||||
display: flex;
|
||||
}
|
||||
@@ -679,6 +679,7 @@
|
||||
"features_and_benefits": "Features & Benefits",
|
||||
"features_like_track_changes": "Features like real-time track changes",
|
||||
"february": "February",
|
||||
"file": "File",
|
||||
"file_action_created": "Created",
|
||||
"file_action_deleted": "Deleted",
|
||||
"file_action_edited": "Edited",
|
||||
@@ -1454,6 +1455,7 @@
|
||||
"overleaf_history_system": "Overleaf History System",
|
||||
"overleaf_individual_plans": "Overleaf individual plans",
|
||||
"overleaf_labs": "Overleaf Labs",
|
||||
"overleaf_logo": "Overleaf logo",
|
||||
"overleaf_plans_and_pricing": "overleaf plans and pricing",
|
||||
"overleaf_template_gallery": "overleaf template gallery",
|
||||
"overview": "Overview",
|
||||
@@ -2407,6 +2409,7 @@
|
||||
"vat": "VAT",
|
||||
"vat_number": "VAT Number",
|
||||
"verify_email_address_before_enabling_managed_users": "You need to verify your email address before enabling managed users.",
|
||||
"view": "View",
|
||||
"view_all": "View All",
|
||||
"view_code": "View code",
|
||||
"view_configuration": "View configuration",
|
||||
|
||||
Reference in New Issue
Block a user