Merge pull request #20501 from overleaf/rd-filetree-toolbar

Migrate the file tree toolbar to Bootstrap 5

GitOrigin-RevId: 00ebe585206bf163bf9a00aa56b52d43effd5605
This commit is contained in:
Rebeka Dekany 2024-09-25 15:46:17 +02:00 committed by Copybot
parent abb59e4603
commit ac74ba9e8c
8 changed files with 142 additions and 48 deletions

View file

@ -1076,6 +1076,7 @@
"progress_bar_percentage": "", "progress_bar_percentage": "",
"project_approaching_file_limit": "", "project_approaching_file_limit": "",
"project_figure_modal": "", "project_figure_modal": "",
"project_files": "",
"project_flagged_too_many_compiles": "", "project_flagged_too_many_compiles": "",
"project_has_too_many_files": "", "project_has_too_many_files": "",
"project_last_published_at": "", "project_last_published_at": "",

View file

@ -1,23 +1,27 @@
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import * as eventTracking from '../../../infrastructure/event-tracking' import * as eventTracking from '../../../infrastructure/event-tracking'
import { Button } from 'react-bootstrap'
import Tooltip from '../../../shared/components/tooltip'
import Icon from '../../../shared/components/icon' import Icon from '../../../shared/components/icon'
import { useFileTreeActionable } from '../contexts/file-tree-actionable' import { useFileTreeActionable } from '../contexts/file-tree-actionable'
import { useFileTreeData } from '@/shared/context/file-tree-data-context' import { useFileTreeData } from '@/shared/context/file-tree-data-context'
import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
import MaterialIcon from '@/shared/components/material-icon'
import OLButtonToolbar from '@/features/ui/components/ol/ol-button-toolbar'
function FileTreeToolbar() { function FileTreeToolbar() {
const { fileTreeReadOnly } = useFileTreeData() const { fileTreeReadOnly } = useFileTreeData()
const { t } = useTranslation()
if (fileTreeReadOnly) return null if (fileTreeReadOnly) return null
return ( return (
<div className="toolbar toolbar-filetree"> <OLButtonToolbar
className="toolbar toolbar-filetree"
aria-label={t('project_files')}
>
<FileTreeToolbarLeft /> <FileTreeToolbarLeft />
<FileTreeToolbarRight /> <FileTreeToolbarRight />
</div> </OLButtonToolbar>
) )
} }
@ -44,33 +48,54 @@ function FileTreeToolbarLeft() {
return ( return (
<div className="toolbar-left"> <div className="toolbar-left">
<Tooltip <OLTooltip
id="new-file" id="new-file"
description={t('new_file')} description={t('new_file')}
overlayProps={{ placement: 'bottom' }} overlayProps={{ placement: 'bottom' }}
> >
<Button onClick={createWithAnalytics} bsStyle={null}> <button className="btn" onClick={createWithAnalytics}>
<Icon type="file" fw accessibilityLabel={t('new_file')} /> <BootstrapVersionSwitcher
</Button> bs5={
</Tooltip> <MaterialIcon
<Tooltip type="description"
accessibilityLabel={t('new_file')}
/>
}
bs3={<Icon type="file" fw accessibilityLabel={t('new_file')} />}
/>
</button>
</OLTooltip>
<OLTooltip
id="new-folder" id="new-folder"
description={t('new_folder')} description={t('new_folder')}
overlayProps={{ placement: 'bottom' }} overlayProps={{ placement: 'bottom' }}
> >
<Button onClick={startCreatingFolder} bsStyle={null}> <button className="btn" onClick={startCreatingFolder} tabIndex={-1}>
<Icon type="folder" fw accessibilityLabel={t('new_folder')} /> <BootstrapVersionSwitcher
</Button> bs5={
</Tooltip> <MaterialIcon
<Tooltip type="folder"
accessibilityLabel={t('new_folder')}
/>
}
bs3={<Icon type="folder" fw accessibilityLabel={t('new_folder')} />}
/>
</button>
</OLTooltip>
<OLTooltip
id="upload" id="upload"
description={t('upload')} description={t('upload')}
overlayProps={{ placement: 'bottom' }} overlayProps={{ placement: 'bottom' }}
> >
<Button onClick={uploadWithAnalytics}> <button className="btn" onClick={uploadWithAnalytics} tabIndex={-1}>
<Icon type="upload" fw accessibilityLabel={t('upload')} /> <BootstrapVersionSwitcher
</Button> bs5={
</Tooltip> <MaterialIcon type="upload" accessibilityLabel={t('upload')} />
}
bs3={<Icon type="upload" fw accessibilityLabel={t('upload')} />}
/>
</button>
</OLTooltip>
</div> </div>
) )
} }
@ -87,26 +112,36 @@ function FileTreeToolbarRight() {
return ( return (
<div className="toolbar-right"> <div className="toolbar-right">
{canRename ? ( {canRename ? (
<Tooltip <OLTooltip
id="rename" id="rename"
description={t('rename')} description={t('rename')}
overlayProps={{ placement: 'bottom' }} overlayProps={{ placement: 'bottom' }}
> >
<Button onClick={startRenaming}> <button className="btn" onClick={startRenaming} tabIndex={-1}>
<Icon type="pencil" fw accessibilityLabel={t('rename')} /> <BootstrapVersionSwitcher
</Button> bs3={<Icon type="pencil" fw accessibilityLabel={t('rename')} />}
</Tooltip> bs5={
<MaterialIcon type="edit" accessibilityLabel={t('rename')} />
}
/>
</button>
</OLTooltip>
) : null} ) : null}
{canDelete ? ( {canDelete ? (
<Tooltip <OLTooltip
id="delete" id="delete"
description={t('delete')} description={t('delete')}
overlayProps={{ placement: 'bottom' }} overlayProps={{ placement: 'bottom' }}
> >
<Button onClick={startDeleting}> <button className="btn" onClick={startDeleting} tabIndex={-1}>
<Icon type="trash-o" fw accessibilityLabel={t('delete')} /> <BootstrapVersionSwitcher
</Button> bs3={<Icon type="trash-o" fw accessibilityLabel={t('delete')} />}
</Tooltip> bs5={
<MaterialIcon type="delete" accessibilityLabel={t('delete')} />
}
/>
</button>
</OLTooltip>
) : null} ) : null}
</div> </div>
) )

View file

@ -11,7 +11,7 @@ import UntrashProjectsButton from './buttons/untrash-projects-button'
import DeleteLeaveProjectsButton from './buttons/delete-leave-projects-button' import DeleteLeaveProjectsButton from './buttons/delete-leave-projects-button'
import LeaveProjectsButton from './buttons/leave-projects-button' import LeaveProjectsButton from './buttons/leave-projects-button'
import DeleteProjectsButton from './buttons/delete-projects-button' import DeleteProjectsButton from './buttons/delete-projects-button'
import OlButtonToolbar from '@/features/ui/components/ol/ol-button-toolbar' import OLButtonToolbar from '@/features/ui/components/ol/ol-button-toolbar'
import OlButtonGroup from '@/features/ui/components/ol/ol-button-group' import OlButtonGroup from '@/features/ui/components/ol/ol-button-group'
function ProjectTools() { function ProjectTools() {
@ -19,7 +19,7 @@ function ProjectTools() {
const { filter, selectedProjects } = useProjectListContext() const { filter, selectedProjects } = useProjectListContext()
return ( return (
<OlButtonToolbar aria-label={t('toolbar_selected_projects')}> <OLButtonToolbar aria-label={t('toolbar_selected_projects')}>
<OlButtonGroup <OlButtonGroup
aria-label={t('toolbar_selected_projects_management_actions')} aria-label={t('toolbar_selected_projects_management_actions')}
> >
@ -48,7 +48,7 @@ function ProjectTools() {
{selectedProjects.length === 1 && {selectedProjects.length === 1 &&
filter !== 'archived' && filter !== 'archived' &&
filter !== 'trashed' && <ProjectToolsMoreDropdownButton />} filter !== 'trashed' && <ProjectToolsMoreDropdownButton />}
</OlButtonToolbar> </OLButtonToolbar>
) )
} }

View file

@ -10,7 +10,7 @@ type OLButtonToolbarProps = ButtonToolbarProps & {
bs3Props?: Record<string, unknown> bs3Props?: Record<string, unknown>
} }
function OlButtonToolbar(props: OLButtonToolbarProps) { function OLButtonToolbar(props: OLButtonToolbarProps) {
const { bs3Props, ...rest } = props const { bs3Props, ...rest } = props
const bs3ButtonToolbarProps: BS3ButtonToolbarProps = { const bs3ButtonToolbarProps: BS3ButtonToolbarProps = {
@ -28,4 +28,4 @@ function OlButtonToolbar(props: OLButtonToolbarProps) {
) )
} }
export default OlButtonToolbar export default OLButtonToolbar

View file

@ -50,14 +50,6 @@
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
.toolbar.toolbar-filetree {
@include toolbar-sm-height;
@include toolbar-alt-bg;
padding: 0 var(--spacing-03);
flex-shrink: 0;
}
> file-tree-root, > file-tree-root,
.file-tree-inner { .file-tree-inner {
position: relative; position: relative;

View file

@ -4,7 +4,9 @@
--toolbar-header-btn-border-color: var(--neutral-80); --toolbar-header-btn-border-color: var(--neutral-80);
--toolbar-btn-color: var(--white); --toolbar-btn-color: var(--white);
--toolbar-btn-hover-bg-color: var(--neutral-80); --toolbar-btn-hover-bg-color: var(--neutral-80);
--toolbar-btn-hover-color: var(--white);
--project-name-color: var(--neutral-40); --project-name-color: var(--neutral-40);
--toolbar-filetree-bg-color: var(--neutral-80);
--project-rename-link-color: var(--neutral-40); --project-rename-link-color: var(--neutral-40);
--project-rename-link-color-hover: var(--neutral-20); --project-rename-link-color-hover: var(--neutral-20);
--editor-header-logo-background: url(../../../../../public/img/ol-brand/overleaf-o-white.svg) --editor-header-logo-background: url(../../../../../public/img/ol-brand/overleaf-o-white.svg)
@ -17,7 +19,9 @@
--toolbar-header-btn-border-color: var(--neutral-20); --toolbar-header-btn-border-color: var(--neutral-20);
--toolbar-btn-color: var(--neutral-70); --toolbar-btn-color: var(--neutral-70);
--toolbar-btn-hover-bg-color: var(--neutral-10); --toolbar-btn-hover-bg-color: var(--neutral-10);
--toolbar-btn-hover-color: var(--neutral-70);
--project-name-color: var(--neutral-70); --project-name-color: var(--neutral-70);
--toolbar-filetree-bg-color: var(--white);
--project-rename-link-color: var(--neutral-70); --project-rename-link-color: var(--neutral-70);
--project-rename-link-color-hover: var(--neutral-70); --project-rename-link-color-hover: var(--neutral-70);
--editor-header-logo-background: url(../../../../../public/img/ol-brand/overleaf-o.svg) --editor-header-logo-background: url(../../../../../public/img/ol-brand/overleaf-o.svg)
@ -25,10 +29,8 @@
} }
.toolbar { .toolbar {
--toolbar-height: 40px;
display: flex; display: flex;
align-items: stretch; align-items: center;
height: var(--toolbar-height); height: var(--toolbar-height);
min-height: var(--toolbar-height); min-height: var(--toolbar-height);
border-bottom: 1px solid var(--toolbar-border-color); border-bottom: 1px solid var(--toolbar-border-color);
@ -58,6 +60,59 @@
.toolbar-right .back-to-editor-btn { .toolbar-right .back-to-editor-btn {
margin-right: var(--spacing-09); margin-right: var(--spacing-09);
display: flex;
align-items: center;
.toolbar-label {
margin-bottom: 0;
}
}
> a:focus,
button:focus {
outline: none;
}
> a:not(.btn),
> button,
.toolbar-left > a:not(.btn),
.toolbar-left > button,
.toolbar-right > a:not(.btn),
.toolbar-right > button:not(.back-to-editor-btn) {
display: inline-block;
color: var(--toolbar-btn-color);
background-color: transparent;
padding: var(--spacing-01);
line-height: 1;
height: 24px;
border-radius: var(--border-radius-base);
&.toolbar-header-back-projects {
padding: var(--spacing-02) var(--spacing-04) var(--spacing-02);
margin-bottom: var(--spacing-01);
}
&:hover {
text-shadow: none;
color: var(--toolbar-btn-hover-color);
background-color: transparent;
text-decoration: none;
}
&.active,
&:active {
.label {
display: none;
}
color: white;
background-color: var(--bg-info-01);
box-shadow: none;
&:hover {
color: white;
}
}
} }
a.btn-full-height, a.btn-full-height,
@ -140,6 +195,9 @@
} }
&.toolbar-header { &.toolbar-header {
--toolbar-height: 40px;
align-items: stretch;
background-color: var(--toolbar-header-bg-color); background-color: var(--toolbar-header-bg-color);
position: absolute; position: absolute;
top: 0; top: 0;
@ -184,6 +242,14 @@
} }
} }
.toolbar-filetree {
@include toolbar-sm-height;
background-color: var(--toolbar-filetree-bg-color);
padding: 0 var(--spacing-03);
flex-shrink: 0;
}
.editor-menu-icon { .editor-menu-icon {
&.material-symbols { &.material-symbols {
width: 1em; width: 1em;

View file

@ -37,7 +37,6 @@
// Optional: Group multiple button groups together for a toolbar // Optional: Group multiple button groups together for a toolbar
.btn-toolbar { .btn-toolbar {
margin-left: -5px; // Offset the first child's margin
&:extend(.clearfix all); &:extend(.clearfix all);
.btn-group, .btn-group,

View file

@ -1541,6 +1541,7 @@
"project": "project", "project": "project",
"project_approaching_file_limit": "This project is approaching the file limit", "project_approaching_file_limit": "This project is approaching the file limit",
"project_figure_modal": "Project", "project_figure_modal": "Project",
"project_files": "Project files",
"project_flagged_too_many_compiles": "This project has been flagged for compiling too often. The limit will be lifted shortly.", "project_flagged_too_many_compiles": "This project has been flagged for compiling too often. The limit will be lifted shortly.",
"project_has_too_many_files": "This project has reached the 2000 file limit", "project_has_too_many_files": "This project has reached the 2000 file limit",
"project_last_published_at": "Your project was last published at", "project_last_published_at": "Your project was last published at",