mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-07 20:31:06 -05:00
[ReactNavToolbar] Integration branch (#3513)
* Created ng-controller for react shared context and set editor.loading * toolbar-header component with menu button (and story) * Added editor-navigation-toolbar-root and react2angular plumbing * Added eslint-disable exception to use <a/> instead of <button/> * added 'menu' to extracted translation * [ReactNavToolbar] Added cobranding and back to projects buttons (#3515) GitOrigin-RevId: 27c3bba85cbc96a123d58c66a0bd5d6a2cfd8aca
This commit is contained in:
parent
0d57ddfd23
commit
37d45d64b3
15 changed files with 199 additions and 14 deletions
|
@ -833,6 +833,8 @@ const ProjectController = {
|
|||
wsUrl,
|
||||
showSupport: Features.hasFeature('support'),
|
||||
showNewLogsUI: user.alphaProgram && !wantsOldLogsUI,
|
||||
showNewNavigationUI:
|
||||
req.query && req.query.new_navigation_ui === 'true',
|
||||
showReactFileTree: user.betaProgram && !wantsOldFileTreeUI
|
||||
})
|
||||
timer.done()
|
||||
|
|
|
@ -8,7 +8,10 @@ block vars
|
|||
block content
|
||||
.editor(ng-controller="IdeController").full-size
|
||||
//- required by react2angular-shared-context, must be rendered as a top level component
|
||||
shared-context-react()
|
||||
div(
|
||||
ng-controller="ReactRootContextController"
|
||||
)
|
||||
shared-context-react(editor-loading="editorLoading")
|
||||
.loading-screen(ng-if="state.loading")
|
||||
.loading-screen-brand-container
|
||||
.loading-screen-brand(
|
||||
|
@ -74,7 +77,10 @@ block content
|
|||
ng-cloak
|
||||
)
|
||||
.ui-layout-center
|
||||
include ./editor/header
|
||||
if showNewNavigationUI
|
||||
include ./editor/header-react
|
||||
else
|
||||
include ./editor/header
|
||||
|
||||
include ./editor/share
|
||||
!= moduleIncludes("publish:body", locals)
|
||||
|
|
3
services/web/app/views/project/editor/header-react.pug
Normal file
3
services/web/app/views/project/editor/header-react.pug
Normal file
|
@ -0,0 +1,3 @@
|
|||
div(ng-controller="EditorNavigationToolbarController")
|
||||
|
||||
editor-navigation-toolbar-root(on-show-left-menu-click="onShowLeftMenuClick")
|
|
@ -5,6 +5,7 @@
|
|||
"autocompile_disabled_reason",
|
||||
"autocomplete",
|
||||
"autocomplete_references",
|
||||
"back_to_your_projects",
|
||||
"blocked_filename",
|
||||
"cancel",
|
||||
"clear_cached_files",
|
||||
|
@ -67,6 +68,7 @@
|
|||
"main_file_not_found",
|
||||
"math_display",
|
||||
"math_inline",
|
||||
"menu",
|
||||
"n_errors",
|
||||
"n_errors_plural",
|
||||
"n_items",
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
import React from 'react'
|
||||
import Icon from '../../../shared/components/icon'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
function BackToProjectsButton() {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<a className="toolbar-header-back-projects" href="/project">
|
||||
<Icon
|
||||
type="fw"
|
||||
modifier="level-up"
|
||||
accessibilityLabel={t('back_to_your_projects')}
|
||||
/>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
export default BackToProjectsButton
|
|
@ -0,0 +1,31 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
function CobrandingLogo({
|
||||
brandVariationHomeUrl,
|
||||
brandVariationName,
|
||||
logoImgUrl
|
||||
}) {
|
||||
return (
|
||||
<a
|
||||
className="btn btn-full-height header-cobranding-logo-container"
|
||||
href={brandVariationHomeUrl}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
<img
|
||||
src={logoImgUrl}
|
||||
className="header-cobranding-logo"
|
||||
alt={brandVariationName}
|
||||
/>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
CobrandingLogo.propTypes = {
|
||||
brandVariationHomeUrl: PropTypes.string.isRequired,
|
||||
brandVariationName: PropTypes.string.isRequired,
|
||||
logoImgUrl: PropTypes.string.isRequired
|
||||
}
|
||||
|
||||
export default CobrandingLogo
|
|
@ -0,0 +1,23 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import ToolbarHeader from './toolbar-header'
|
||||
import { useEditorContext } from '../../../shared/context/editor-context'
|
||||
|
||||
function EditorNavigationToolbarRoot({ onShowLeftMenuClick }) {
|
||||
const { cobranding, loading } = useEditorContext()
|
||||
|
||||
// using {display: 'none'} as 1:1 migration from Angular's ng-hide. Using
|
||||
// `loading ? null : <ToolbarHeader/>` causes UI glitches
|
||||
return (
|
||||
<ToolbarHeader
|
||||
style={loading ? { display: 'none' } : {}}
|
||||
cobranding={cobranding}
|
||||
onShowLeftMenuClick={onShowLeftMenuClick}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
EditorNavigationToolbarRoot.propTypes = {
|
||||
onShowLeftMenuClick: PropTypes.func.isRequired
|
||||
}
|
||||
export default EditorNavigationToolbarRoot
|
|
@ -0,0 +1,22 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Icon from '../../../shared/components/icon'
|
||||
|
||||
function MenuButton({ onClick }) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line jsx-a11y/anchor-is-valid
|
||||
<a role="button" className="btn btn-full-height" href="#" onClick={onClick}>
|
||||
<Icon type="fw" modifier="bars" classes={{ icon: 'editor-menu-icon' }} />
|
||||
<p className="toolbar-label">{t('menu')}</p>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
MenuButton.propTypes = {
|
||||
onClick: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
export default MenuButton
|
|
@ -0,0 +1,24 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import MenuButton from './menu-button'
|
||||
import CobrandingLogo from './cobranding-logo'
|
||||
import BackToProjectsButton from './back-to-projects-button'
|
||||
|
||||
function ToolbarHeader({ cobranding, onShowLeftMenuClick }) {
|
||||
return (
|
||||
<header className="toolbar toolbar-header toolbar-with-labels">
|
||||
<div className="toolbar-left">
|
||||
<MenuButton onClick={onShowLeftMenuClick} />
|
||||
{cobranding ? <CobrandingLogo {...cobranding} /> : null}
|
||||
<BackToProjectsButton />
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
ToolbarHeader.propTypes = {
|
||||
onShowLeftMenuClick: PropTypes.func.isRequired,
|
||||
cobranding: PropTypes.object
|
||||
}
|
||||
|
||||
export default ToolbarHeader
|
|
@ -0,0 +1,18 @@
|
|||
import App from '../../../base'
|
||||
import { react2angular } from 'react2angular'
|
||||
import EditorNavigationToolbarRoot from '../components/editor-navigation-toolbar-root'
|
||||
import { rootContext } from '../../../shared/context/root-context'
|
||||
|
||||
App.controller('EditorNavigationToolbarController', function($scope, ide) {
|
||||
$scope.onShowLeftMenuClick = () =>
|
||||
ide.$scope.$applyAsync(() => {
|
||||
ide.$scope.ui.leftMenuShown = !ide.$scope.ui.leftMenuShown
|
||||
})
|
||||
})
|
||||
|
||||
App.component(
|
||||
'editorNavigationToolbarRoot',
|
||||
react2angular(rootContext.use(EditorNavigationToolbarRoot), [
|
||||
'onShowLeftMenuClick'
|
||||
])
|
||||
)
|
|
@ -61,9 +61,8 @@ import './main/account-upgrade-angular'
|
|||
import './main/exposed-settings-angular'
|
||||
import './main/system-messages'
|
||||
import '../../modules/modules-ide.js'
|
||||
|
||||
import { react2angular } from 'react2angular'
|
||||
import { rootContext } from './shared/context/root-context'
|
||||
import './shared/context/controllers/root-context-controller'
|
||||
import './features/editor-navigation-toolbar/controllers/editor-navigation-toolbar-controller'
|
||||
|
||||
App.controller('IdeController', function(
|
||||
$scope,
|
||||
|
@ -353,10 +352,6 @@ If the project has been renamed please look in your project list for a new proje
|
|||
})
|
||||
})
|
||||
|
||||
// required by react2angular-shared-context, maps the shared context instance to an angular component
|
||||
// that must be rendered in the app
|
||||
App.component('sharedContextReact', react2angular(rootContext.component))
|
||||
|
||||
export default angular.bootstrap(document.body, ['SharelatexApp'])
|
||||
|
||||
function __guard__(value, transform) {
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
import App from '../../../base'
|
||||
import { react2angular } from 'react2angular'
|
||||
import { rootContext } from '../root-context'
|
||||
|
||||
App.controller('ReactRootContextController', function($scope, ide) {
|
||||
$scope.editorLoading = !!ide.$scope.state.loading
|
||||
ide.$scope.$watch('state.loading', editorLoading => {
|
||||
$scope.editorLoading = editorLoading
|
||||
})
|
||||
})
|
||||
|
||||
App.component(
|
||||
'sharedContextReact',
|
||||
react2angular(rootContext.component, ['editorLoading'])
|
||||
)
|
|
@ -3,13 +3,23 @@ import PropTypes from 'prop-types'
|
|||
|
||||
export const EditorContext = createContext()
|
||||
|
||||
export function EditorProvider({ children }) {
|
||||
export function EditorProvider({ children, loading }) {
|
||||
const cobranding = window.brandVariation
|
||||
? {
|
||||
logoImgUrl: window.brandVariation.logo_url,
|
||||
brandVariationName: window.brandVariation.name,
|
||||
brandVariationHomeUrl: window.brandVariation.home_url
|
||||
}
|
||||
: undefined
|
||||
|
||||
const ownerId =
|
||||
window._ide.$scope.project && window._ide.$scope.project.owner
|
||||
? window._ide.$scope.project.owner._id
|
||||
: null
|
||||
|
||||
const editorContextValue = {
|
||||
cobranding,
|
||||
loading,
|
||||
projectId: window.project_id,
|
||||
isProjectOwner: ownerId === window.user.id
|
||||
}
|
||||
|
@ -22,7 +32,8 @@ export function EditorProvider({ children }) {
|
|||
}
|
||||
|
||||
EditorProvider.propTypes = {
|
||||
children: PropTypes.any
|
||||
children: PropTypes.any,
|
||||
loading: PropTypes.bool
|
||||
}
|
||||
|
||||
export function useEditorContext() {
|
||||
|
|
|
@ -1,15 +1,20 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { ApplicationProvider } from './application-context'
|
||||
import { EditorProvider } from './editor-context'
|
||||
import createSharedContext from 'react2angular-shared-context'
|
||||
|
||||
// eslint-disable-next-line react/prop-types
|
||||
export function ContextRoot({ children }) {
|
||||
export function ContextRoot({ children, editorLoading }) {
|
||||
return (
|
||||
<ApplicationProvider>
|
||||
<EditorProvider>{children}</EditorProvider>
|
||||
<EditorProvider loading={editorLoading}>{children}</EditorProvider>
|
||||
</ApplicationProvider>
|
||||
)
|
||||
}
|
||||
|
||||
ContextRoot.propTypes = {
|
||||
children: PropTypes.any,
|
||||
editorLoading: PropTypes.bool
|
||||
}
|
||||
|
||||
export const rootContext = createSharedContext(ContextRoot)
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
import React from 'react'
|
||||
import ToolbarHeader from '../js/features/editor-navigation-toolbar/components/toolbar-header'
|
||||
|
||||
export const Default = () => {
|
||||
return <ToolbarHeader />
|
||||
}
|
||||
|
||||
export default {
|
||||
title: 'EditorNavigationToolbar'
|
||||
}
|
Loading…
Reference in a new issue