default-navbar.tsx 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. import React, { useState } from 'react'
  2. import { sendMB } from '@/infrastructure/event-tracking'
  3. import { useTranslation } from 'react-i18next'
  4. import { Button, Container, Nav, Navbar } from 'react-bootstrap'
  5. import useWaitForI18n from '@/shared/hooks/use-wait-for-i18n'
  6. import AdminMenu from '@/shared/components/navbar/admin-menu'
  7. import type { DefaultNavbarMetadata } from '@/shared/components/types/default-navbar-metadata'
  8. import NavItemFromData from '@/shared/components/navbar/nav-item-from-data'
  9. import LoggedInItems from '@/shared/components/navbar/logged-in-items'
  10. import LoggedOutItems from '@/shared/components/navbar/logged-out-items'
  11. import HeaderLogoOrTitle from '@/shared/components/navbar/header-logo-or-title'
  12. import MaterialIcon from '@/shared/components/material-icon'
  13. import { useContactUsModal } from '@/shared/hooks/use-contact-us-modal'
  14. import { UserProvider } from '@/shared/context/user-context'
  15. import { X } from '@phosphor-icons/react'
  16. import overleafWhiteLogo from '@/shared/svgs/overleaf-white.svg'
  17. import overleafGreenLogo from '@/shared/svgs/overleaf-green.svg'
  18. import type { CSSPropertiesWithVariables } from '../../../../../types/css-properties-with-variables'
  19. function DefaultNavbar(
  20. props: DefaultNavbarMetadata & { overleafLogo?: string }
  21. ) {
  22. const {
  23. overleafLogo,
  24. customLogo,
  25. title,
  26. canDisplayAdminMenu,
  27. canDisplayAdminRedirect,
  28. canDisplayProjectUrlLookup,
  29. canDisplaySplitTestMenu,
  30. canDisplaySurveyMenu,
  31. canDisplayScriptLogMenu,
  32. enableUpgradeButton,
  33. suppressNavbarRight,
  34. suppressNavContentLinks,
  35. showCloseIcon = false,
  36. showSubscriptionLink,
  37. showSignUpLink,
  38. sessionUser,
  39. adminUrl,
  40. items,
  41. } = props
  42. const { t } = useTranslation()
  43. const [expanded, setExpanded] = useState(false)
  44. // The Contact us modal is rendered at this level rather than inside the nav
  45. // bar because otherwise the help wiki search results dropdown doesn't show up
  46. const { modal: contactUsModal, showModal: showContactUsModal } =
  47. useContactUsModal({
  48. autofillProjectUrl: false,
  49. })
  50. return (
  51. <>
  52. <Navbar
  53. className="navbar-default navbar-main"
  54. expand="lg"
  55. onToggle={expanded => setExpanded(expanded)}
  56. style={
  57. {
  58. '--navbar-brand-image-default-url': `url("${overleafWhiteLogo}")`,
  59. '--navbar-brand-image-redesign-url': `url("${overleafGreenLogo}")`,
  60. } as CSSPropertiesWithVariables
  61. }
  62. aria-label={t('primary')}
  63. >
  64. <Container className="navbar-container" fluid>
  65. <div className="navbar-header">
  66. <HeaderLogoOrTitle
  67. title={title}
  68. overleafLogo={overleafLogo}
  69. customLogo={customLogo}
  70. />
  71. {enableUpgradeButton ? (
  72. <Button
  73. as="a"
  74. href="/user/subscription/plans"
  75. className="me-2 d-md-none"
  76. onClick={() => {
  77. sendMB('upgrade-button-click', {
  78. source: 'dashboard-top',
  79. 'project-dashboard-react': 'enabled',
  80. 'is-dashboard-sidebar-hidden': 'true',
  81. 'is-screen-width-less-than-768px': 'true',
  82. })
  83. }}
  84. >
  85. {t('upgrade')}
  86. </Button>
  87. ) : null}
  88. </div>
  89. {suppressNavbarRight ? null : (
  90. <>
  91. <Navbar.Toggle
  92. aria-controls="navbar-main-collapse"
  93. aria-expanded="false"
  94. aria-label={t('primary')}
  95. >
  96. {showCloseIcon && expanded ? (
  97. <X />
  98. ) : (
  99. <MaterialIcon type="menu" />
  100. )}
  101. </Navbar.Toggle>
  102. <Navbar.Collapse
  103. id="navbar-main-collapse"
  104. className="justify-content-end"
  105. >
  106. <Nav as="ul" className="ms-auto" role="menubar">
  107. {canDisplayAdminMenu ||
  108. canDisplayAdminRedirect ||
  109. canDisplaySplitTestMenu ? (
  110. <AdminMenu
  111. canDisplayAdminMenu={canDisplayAdminMenu}
  112. canDisplayAdminRedirect={canDisplayAdminRedirect}
  113. canDisplayProjectUrlLookup={canDisplayProjectUrlLookup}
  114. canDisplaySplitTestMenu={canDisplaySplitTestMenu}
  115. canDisplaySurveyMenu={canDisplaySurveyMenu}
  116. canDisplayScriptLogMenu={canDisplayScriptLogMenu}
  117. adminUrl={adminUrl}
  118. />
  119. ) : null}
  120. {items.map((item, index) => {
  121. const showNavItem =
  122. (item.only_when_logged_in && sessionUser) ||
  123. (item.only_when_logged_out && sessionUser) ||
  124. (!item.only_when_logged_out &&
  125. !item.only_when_logged_in &&
  126. !item.only_content_pages) ||
  127. (item.only_content_pages && !suppressNavContentLinks)
  128. return showNavItem ? (
  129. <NavItemFromData
  130. item={item}
  131. key={index}
  132. showContactUsModal={showContactUsModal}
  133. />
  134. ) : null
  135. })}
  136. {sessionUser ? (
  137. <LoggedInItems
  138. sessionUser={sessionUser}
  139. showSubscriptionLink={showSubscriptionLink}
  140. />
  141. ) : (
  142. <LoggedOutItems showSignUpLink={showSignUpLink} />
  143. )}
  144. </Nav>
  145. </Navbar.Collapse>
  146. </>
  147. )}
  148. </Container>
  149. </Navbar>
  150. <UserProvider>{contactUsModal}</UserProvider>
  151. </>
  152. )
  153. }
  154. export const DefaultNavbarRoot = (props: DefaultNavbarMetadata) => {
  155. const { isReady } = useWaitForI18n()
  156. if (!isReady) {
  157. return null
  158. }
  159. return <DefaultNavbar {...props} />
  160. }
  161. export default DefaultNavbar