Просмотр исходного кода

Add new fat footer (#6260)

GitOrigin-RevId: 64c50caac2ec8d56b3f49d6f97c8a1c4d4b3a496
Alf Eaton 4 лет назад
Родитель
Сommit
4610734f07

+ 1 - 0
services/web/app/src/Features/Project/ProjectController.js

@@ -613,6 +613,7 @@ const ProjectController = {
             zipFileSizeLimit: Settings.maxUploadSize,
             isOverleaf: !!Settings.overleaf,
             metadata: { viewport: false },
+            showThinFooter: true, // don't show the fat footer on the projects dashboard, as there's a fixed space available
           }
 
           const paidUser =

+ 5 - 0
services/web/app/src/infrastructure/ExpressLocals.js

@@ -355,6 +355,11 @@ module.exports = function (webRouter, privateApiRouter, publicApiRouter) {
     next()
   })
 
+  webRouter.use(function (req, res, next) {
+    res.locals.showThinFooter = !Features.hasFeature('saas')
+    next()
+  })
+
   webRouter.use(function (req, res, next) {
     res.locals.ExposedSettings = {
       isOverleaf: Settings.overleaf != null,

+ 5 - 2
services/web/app/views/layout-marketing.pug

@@ -99,7 +99,7 @@ html(
 		block head-scripts
 
 
-	body
+	body(class=(showThinFooter ? 'thin-footer' : undefined))
 		if(settings.recaptcha && settings.recaptcha.siteKeyV3)
 			script(type="text/javascript", nonce=scriptNonce, src="https://www.recaptcha.net/recaptcha/api.js?render="+settings.recaptcha.siteKeyV3)
 
@@ -112,7 +112,10 @@ html(
 		block content
 
 		if (typeof(suppressFooter) == "undefined")
-			include layout/footer-marketing
+			if showThinFooter
+				include layout/footer-marketing
+			else
+				include layout/fat-footer
 
 		!= moduleIncludes("contactModal-marketing", locals)
 

+ 5 - 2
services/web/app/views/layout.pug

@@ -100,7 +100,7 @@ html(
 		block head-scripts
 
 
-	body(ng-csp=(cspEnabled ? "no-unsafe-eval" : false))
+	body(ng-csp=(cspEnabled ? "no-unsafe-eval" : false) class=(showThinFooter ? 'thin-footer' : undefined))
 		if(settings.recaptcha && settings.recaptcha.siteKeyV3)
 			script(type="text/javascript", nonce=scriptNonce, src="https://www.recaptcha.net/recaptcha/api.js?render="+settings.recaptcha.siteKeyV3)
 
@@ -113,7 +113,10 @@ html(
 		block content
 
 		if (typeof(suppressFooter) == "undefined")
-			include layout/footer
+			if showThinFooter
+				include layout/footer
+			else
+				include layout/fat-footer
 
 		!= moduleIncludes("contactModal", locals)
 

+ 18 - 0
services/web/app/views/layout/fat-footer-base.pug

@@ -0,0 +1,18 @@
+.fat-footer-base
+	.fat-footer-base-section.fat-footer-base-meta
+		.fat-footer-base-item 
+			.fat-footer-base-copyright © #{new Date().getFullYear()} Overleaf
+			a(href="/legal") #{translate('privacy_and_terms')}
+		ul.fat-footer-base-item.list-unstyled.fat-footer-base-language
+			include language-picker
+	.fat-footer-base-section.fat-footer-base-social
+		.fat-footer-base-item
+			a.fat-footer-social(href="https://twitter.com/overleaf")
+				i.fa.fa-twitter-square(aria-hidden="true")
+					.sr-only Overleaf on Twitter
+			a.fat-footer-social(href="https://www.facebook.com/overleaf.editor")
+				i.fa.fa-facebook-square(aria-hidden="true")
+					.sr-only Overleaf on Facebook
+			a.fat-footer-social(href="https://www.linkedin.com/company/writelatex-limited")
+				i.fa.fa-linkedin-square(aria-hidden="true")
+					.sr-only Overleaf on LinkedIn

+ 79 - 0
services/web/app/views/layout/fat-footer.pug

@@ -0,0 +1,79 @@
+footer.fat-footer.hidden-print
+	.fat-footer-container
+		.fat-footer-sections(class=hideFatFooter ? 'hidden' : undefined)
+			.footer-section#footer-brand
+				a(href='/', aria-label=settings.appName).footer-brand
+					
+			.footer-section
+				.footer-section-heading #{translate('About')}
+					
+				ul.list-unstyled
+					li
+						a(href="/about") #{translate('footer_about_us')}
+					li
+						a(href="/about/values") #{translate('our_values')}
+					li
+						a(href="https://apply.workable.com/overleaf/") #{translate('careers')}
+					li
+						a(href="/for/press") #{translate('press_and_awards')}
+					li
+						a(href="/blog") #{translate('blog')}
+						
+			.footer-section
+				.footer-section-heading #{translate('learn')}
+		
+				ul.list-unstyled
+					li
+						a(href="/learn/latex/Learn_LaTeX_in_30_minutes") #{translate('latex_in_thirty_minutes')}
+					li
+						a(href="/latex/templates") #{translate('templates')}
+					li
+						a(href="/events/webinars") #{translate('webinars')}
+					li
+						a(href="/learn/latex/Tutorials") #{translate('tutorials')}
+					li
+						a(href="/learn/latex/Inserting_Images") #{translate('how_to_insert_images')}
+					li
+						a(href="/learn/latex/Tables") #{translate('how_to_create_tables')}
+						
+			.footer-section
+				.footer-section-heading #{translate('footer_plans_and_pricing')}
+		
+				ul.list-unstyled
+					li
+						a(href="/learn/how-to/Overleaf_premium_features") #{translate('premium_features')}
+					li
+						a(href="/user/subscription/plans") #{translate('for_individuals_and_groups')}
+					li
+						a(href="/for/enterprises") #{translate('for_enterprise')}
+					li
+						a(href="/for/universities") #{translate('for_universities')}
+					li
+						a(href="/user/subscription/plans#view=student") #{translate('for_students')}
+						
+			.footer-section
+				.footer-section-heading #{translate('get_involved')}
+		
+				ul.list-unstyled
+					li
+						a(href="/for/community/advisors") #{translate('become_an_advisor')}
+					li
+						a(href="https://calendly.com/overleaf/user-research") #{translate('participate_in_user_research')}
+					if user
+						li
+							a(href="/beta/participate") #{translate('join_beta_program')}
+						li
+							a(href="/user/bonus") #{translate('refer_a_friend')}
+		
+			.footer-section
+				.footer-section-heading #{translate('help')}
+		
+				ul.list-unstyled
+					li
+						a(href="/learn") #{translate('Documentation')} 
+					li
+						a(href="/contact") #{translate('footer_contact_us')}  
+					li
+						a(href="https://status.overleaf.com/") #{translate('website_status')}
+					
+		include fat-footer-base

+ 1 - 22
services/web/app/views/layout/footer-marketing.pug

@@ -17,27 +17,7 @@ footer.site-footer
 							strong.text-muted |
 
 				if showLanguagePicker
-					li.dropdown.dropup.subdued
-						a.dropdown-toggle(
-							href="#",
-							data-toggle="dropdown",
-							aria-haspopup="true",
-							aria-expanded="false",
-							aria-label="Select " + translate('language'),
-							data-ol-lang-selector-tooltip,
-							title=translate('language')
-						)
-							i.fa.fa-fw.fa-language
-							|
-							| #{settings.translatedLanguages[currentLngCode]}
-
-						ul.dropdown-menu(role="menu")
-							li.dropdown-header #{translate("language")}
-							each subdomainDetails, subdomain in settings.i18n.subdomainLang
-								if !subdomainDetails.hide
-									li.lngOption
-										a.menu-indent(href=subdomainDetails.url+currentUrlWithQueryParams)
-											| #{settings.translatedLanguages[subdomainDetails.lngCode]}
+					include language-picker
 
 				if showLanguagePicker && hasCustomLeftNav
 					li
@@ -51,7 +31,6 @@ footer.site-footer
 							| !{item.text}
 
 			ul.col-md-3.text-right
-
 				each item in nav.right_footer
 					li(ng-non-bindable)
 						if item.url

+ 3 - 24
services/web/app/views/layout/footer.pug

@@ -6,10 +6,9 @@ footer.site-footer
 	.site-footer-content.hidden-print
 		.row
 			ul.col-md-9
-				if hasFeature('saas')
-					li © #{new Date().getFullYear()} Overleaf
-				else if !settings.nav.hide_powered_by
+				if !settings.nav.hide_powered_by
 					li
+						//- year of Server Pro release, static
 						| © 2021
 						|
 						a(href='https://www.overleaf.com/for/enterprises') Powered by Overleaf
@@ -19,27 +18,7 @@ footer.site-footer
 							strong.text-muted |
 
 				if showLanguagePicker
-					li.dropdown.dropup.subdued(dropdown)
-						a.dropdown-toggle(
-							href="#",
-							dropdown-toggle,
-							data-toggle="dropdown",
-							aria-haspopup="true",
-							aria-expanded="false",
-							aria-label="Select " + translate('language')
-							tooltip=translate('language')
-						)
-							i.fa.fa-fw.fa-language
-							|
-							| #{settings.translatedLanguages[currentLngCode]}
-
-						ul.dropdown-menu
-							li.dropdown-header #{translate("language")}
-							each subdomainDetails, subdomain in settings.i18n.subdomainLang
-								if !subdomainDetails.hide
-									li.lngOption
-										a.menu-indent(href=subdomainDetails.url+currentUrlWithQueryParams)
-											| #{settings.translatedLanguages[subdomainDetails.lngCode]}
+					include language-picker
 
 				if showLanguagePicker && hasCustomLeftNav
 					li

+ 23 - 0
services/web/app/views/layout/language-picker.pug

@@ -0,0 +1,23 @@
+li.dropdown.dropup.subdued(dropdown).language-picker
+	a.dropdown-toggle#language-picker-toggle(
+		href="#",
+		dropdown-toggle,
+		data-ol-lang-selector-tooltip,
+		data-toggle="dropdown",
+		role="button"
+		aria-haspopup="true",
+		aria-expanded="false",
+		aria-label="Select " + translate('language'),
+		tooltip=translate('language')
+	)
+		i.fa.fa-fw.fa-language
+		|
+		| #{settings.translatedLanguages[currentLngCode]}
+
+	ul.dropdown-menu(role="menu" aria-labelledby="language-picker-toggle")
+		li.dropdown-header #{translate("language")}
+		each subdomainDetails, subdomain in settings.i18n.subdomainLang
+			if !subdomainDetails.hide
+				li.lngOption
+					a.menu-indent(href=subdomainDetails.url+currentUrlWithQueryParams role="menuitem")
+						| #{settings.translatedLanguages[subdomainDetails.lngCode]}

+ 37 - 13
services/web/frontend/js/pages/user/subscription/plans.js

@@ -7,24 +7,28 @@ import getMeta from '../../../utils/meta'
 let currentView = 'monthly'
 let currentCurrencyCode = getMeta('ol-recommendedCurrency')
 
+function selectView(view) {
+  document.querySelectorAll('[data-ol-view-tab]').forEach(el => {
+    if (el.getAttribute('data-ol-view-tab') === view) {
+      el.classList.add('active')
+    } else {
+      el.classList.remove('active')
+    }
+  })
+  document.querySelectorAll('[data-ol-view]').forEach(el => {
+    el.hidden = el.getAttribute('data-ol-view') !== view
+  })
+  currentView = view
+  updateLinkTargets()
+}
+
 function setUpViewSwitching(liEl) {
   const view = liEl.getAttribute('data-ol-view-tab')
   liEl.querySelector('a').addEventListener('click', function (e) {
     e.preventDefault()
-    eventTracking.send('subscription-funnel', 'plans-page', `${view}-prices`) // deprecated by plans-page-toggle
+    eventTracking.send('subscription-funnel', 'plans-page', `${view}-prices`)
     eventTracking.sendMB('plans-page-toggle', { button: view })
-    document.querySelectorAll('[data-ol-view-tab]').forEach(el => {
-      if (el.getAttribute('data-ol-view-tab') === view) {
-        el.classList.add('active')
-      } else {
-        el.classList.remove('active')
-      }
-    })
-    document.querySelectorAll('[data-ol-view]').forEach(el => {
-      el.hidden = el.getAttribute('data-ol-view') !== view
-    })
-    currentView = view
-    updateLinkTargets()
+    selectView(view)
   })
 }
 
@@ -77,6 +81,23 @@ function updateLinkTargets() {
   })
 }
 
+function selectViewFromHash() {
+  try {
+    const params = new URLSearchParams(window.location.hash.substring(1))
+    const view = params.get('view')
+    if (view) {
+      // make sure the selected view is valid
+      if (document.querySelector(`[data-ol-view-tab="${view}"]`)) {
+        selectView(view)
+        // clear the hash so it doesn't persist when switching plans
+        window.location.hash = ''
+      }
+    }
+  } catch {
+    // do nothing
+  }
+}
+
 document.querySelectorAll('[data-ol-view-tab]').forEach(setUpViewSwitching)
 document
   .querySelectorAll('[data-ol-currencyCode-switch]')
@@ -85,3 +106,6 @@ document
   .querySelectorAll('[data-ol-start-new-subscription]')
   .forEach(setUpSubscriptionTracking)
 updateLinkTargets()
+
+selectViewFromHash()
+window.addEventListener('hashchange', selectViewFromHash)

+ 212 - 0
services/web/frontend/stylesheets/components/footer.less

@@ -50,3 +50,215 @@ footer.site-footer {
   display: inline-block;
   vertical-align: middle;
 }
+
+.fat-footer {
+  background: @ol-blue-gray-6;
+  color: @ol-blue-gray-1;
+  display: flex;
+  flex-direction: column;
+
+  .fat-footer-container {
+    margin: @margin-xxl auto;
+  }
+
+  a {
+    color: @ol-blue-gray-1;
+  }
+
+  .footer-brand-container {
+    flex: 1;
+  }
+
+  .fat-footer-sections {
+    display: grid;
+    column-gap: @margin-md;
+    padding: 0 @padding-md @padding-md;
+  }
+
+  .footer-brand {
+    display: block;
+    height: 37px; /* TODO: configurable? */
+    width: @navbar-brand-width;
+    background-image: @navbar-brand-image-url;
+    background-size: contain;
+    background-repeat: no-repeat;
+    background-position: left center;
+  }
+
+  .footer-section-heading {
+    font-family: @font-family-serif;
+    font-size: @font-size-h3;
+    line-height: @headings-line-height;
+    margin-bottom: @margin-md;
+    margin-top: @margin-md;
+  }
+
+  .footer-section ul {
+    font-family: @font-family-sans-serif;
+    font-size: @font-size-base;
+    line-height: @line-height-small;
+  }
+
+  .footer-section li {
+    padding-bottom: @padding-sm;
+  }
+
+  #footer-brand {
+    grid-column: ~'1/-1';
+    margin-top: @margin-md;
+  }
+
+  .fat-footer-base {
+    color: @ol-blue-gray-2;
+    font-size: @font-size-small;
+    line-height: @line-height-base;
+
+    .fat-footer-base-meta a:not(.dropdown-toggle) {
+      color: inherit;
+    }
+
+    .language-picker .dropdown-menu {
+      .dropdown-header {
+        display: none; /* hiding rather than removing as still needed in the thin footer */
+      }
+
+      a {
+        color: @gray-dark;
+
+        &:hover {
+          color: white;
+        }
+      }
+    }
+  }
+
+  .fat-footer-base-item {
+    display: flex;
+    white-space: nowrap;
+  }
+
+  @media (max-width: @screen-xs-max) {
+    .fat-footer-sections {
+      grid-template-columns: repeat(2, 1fr);
+      grid-template-rows: repeat(4, auto);
+    }
+
+    .fat-footer-base {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      margin: @margin-md auto 0;
+
+      .fat-footer-base-section {
+        display: flex;
+        align-items: center;
+      }
+
+      .fat-footer-base-social {
+        order: 1;
+      }
+
+      .fat-footer-base-meta {
+        display: flex;
+        flex-direction: column-reverse;
+        align-items: center;
+        order: 2;
+      }
+
+      .fat-footer-base-item {
+        padding: @padding-xs;
+        margin: @margin-lg / 2;
+      }
+
+      .fat-footer-base-meta .fat-footer-base-item {
+        gap: @margin-lg;
+      }
+
+      .fat-footer-base-social .fat-footer-base-item {
+        gap: @margin-md;
+      }
+
+      .fat-footer-social {
+        font-size: @fat-footer-social-font-size;
+      }
+
+      .fat-footer-base-copyright {
+        order: 2;
+      }
+    }
+  }
+
+  @media (min-width: @screen-sm-min) {
+    .fat-footer-container {
+      width: (@screen-sm - (@padding-sm * 2));
+    }
+
+    #footer-brand {
+      grid-column: auto;
+      grid-row: ~'1/-1';
+    }
+
+    .fat-footer-sections {
+      grid-template-columns: repeat(4, 1fr);
+      grid-template-rows: repeat(2, auto);
+    }
+
+    .footer-section:last-of-type {
+      grid-column: 4;
+    }
+
+    .footer-section ul {
+      line-height: 20px;
+    }
+
+    .fat-footer-base {
+      display: flex;
+      justify-content: space-between;
+      margin: @margin-md auto;
+
+      .fat-footer-base-section {
+        display: flex;
+        align-items: center;
+      }
+
+      .fat-footer-base-item {
+        padding: @padding-xs;
+        margin: @margin-xs;
+      }
+
+      .fat-footer-base-meta .fat-footer-base-item {
+        gap: @margin-md;
+      }
+
+      .fat-footer-social {
+        margin: 0 @margin-xs;
+        font-size: @fat-footer-social-font-size;
+      }
+    }
+  }
+
+  @media (min-width: @screen-md-min) {
+    .fat-footer-container {
+      width: (@screen-md - @padding-md);
+    }
+
+    .fat-footer-sections {
+      grid-template-columns: repeat(4, 1fr);
+    }
+  }
+
+  @media (min-width: @screen-lg-min) {
+    .fat-footer-container {
+      width: (@screen-lg - @padding-lg);
+    }
+
+    .fat-footer-sections {
+      grid-template-columns: repeat(6, 1fr);
+      grid-template-rows: auto;
+    }
+
+    .footer-section:last-of-type {
+      grid-column: 6;
+    }
+  }
+}

+ 4 - 1
services/web/frontend/stylesheets/core/scaffolding.less

@@ -31,11 +31,14 @@ body {
   background-color: @body-bg;
   min-height: 100%;
   position: relative;
-  padding-bottom: @footer-height;
   & > .content {
     min-height: calc(~'100vh -' @footer-height);
     padding-top: @header-height + @content-margin-vertical;
   }
+
+  .thin-footer {
+    padding-bottom: @footer-height;
+  }
 }
 
 // Reset fonts for relevant elements

+ 1 - 0
services/web/frontend/stylesheets/core/variables.less

@@ -969,6 +969,7 @@
 @footer-link-hover-color: @link-hover-color-alt;
 @footer-bg-color: #fff;
 @footer-padding: 2em 0;
+@fat-footer-social-font-size: 30px;
 
 // Editor header
 @ide-body-top-offset: 40px;

+ 23 - 0
services/web/locales/en.json

@@ -832,6 +832,29 @@
   "Terms": "Terms",
   "Security": "Security",
   "About": "About",
+  "learn": "Learn",
+  "latex_in_thirty_minutes": "LaTeX in 30 minutes",
+  "how_to_insert_images": "How to insert images",
+  "how_to_create_tables": "How to create tables",
+  "for_individuals_and_groups": "For individuals & groups",
+  "for_enterprise": "For enterprise",
+  "for_universities": "For universities",
+  "for_students": "For students",
+  "get_involved": "Get involved",
+  "become_an_advisor": "Become an __appName__ advisor",
+  "participate_in_user_research": "Participate in user research",
+  "join_beta_program": "Join beta program",
+  "refer_a_friend": "Refer a friend",
+  "webinars": "Webinars",
+  "tutorials": "Tutorials",
+  "footer_plans_and_pricing": "Plans & pricing",
+  "footer_contact_us": "Contact us",
+  "website_status": "Website status",
+  "privacy_and_terms": "Privacy and Terms",
+  "footer_about_us": "About us",
+  "our_values": "Our values",
+  "careers": "Careers",
+  "press_and_awards": "Press & awards",
   "editor_disconected_click_to_reconnect": "Editor disconnected, click anywhere to reconnect.",
   "word_count": "Word Count",
   "please_compile_pdf_before_word_count": "Please compile your project before performing a word count",