Переглянути джерело

Merge pull request #5071 from overleaf/jpa-web-learn-de-ng

[web] de-ng learn wiki page

GitOrigin-RevId: defb1c1c90fe17e843f36253e81c2455b7dddfb1
Jakob Ackermann 4 роки тому
батько
коміт
40a1c302f9

+ 30 - 0
services/web/app/views/_mixins/faq_search-marketing.pug

@@ -0,0 +1,30 @@
+mixin faq_search-marketing(headerText, headerClass)
+	if (typeof(settings.algolia) != "undefined" && typeof(settings.algolia.indexes) != "undefined" && typeof(settings.algolia.indexes.wiki) != "undefined")
+		if headerText
+			div(class=headerClass, ng-non-bindable) #{headerText}
+		.wiki
+			form.project-search.form-horizontal(role="search" data-ol-faq-search)
+				.form-group.has-feedback.has-feedback-left
+					.col-sm-12
+						input.form-control(type='text', placeholder="Search help library…")
+						i.fa.fa-search.form-control-feedback-left(aria-hidden="true")
+						i.fa.fa-times.form-control-feedback(
+							style="cursor: pointer;",
+							hidden
+							data-ol-clear-search
+							aria-hidden="true"
+						)
+						button.sr-only(
+							type="button"
+							hidden
+							data-ol-clear-search
+							aria-label=translate('clear_search')
+						)
+
+				.row(role="region" aria-label="search results")
+					.col-md-12()
+						div(data-ol-search-results-wrapper)
+							span.sr-only(aria-live="polite" data-ol-search-sr-help-message)
+							div(data-ol-search-results)
+						.row-spaced-small.search-result.card.card-thin(hidden data-ol-search-no-results)
+							p #{translate("no_search_results")}

+ 2 - 44
services/web/frontend/js/base.js

@@ -1,5 +1,3 @@
-/* global MathJax */
-
 /* eslint-disable
     camelcase,
     max-len,
@@ -24,6 +22,7 @@ import './modules/errorCatcher'
 import './modules/localStorage'
 import './modules/sessionStorage'
 import getMeta from './utils/meta'
+import { configureMathJax } from './features/mathjax/configure'
 
 const App = angular
   .module('SharelatexApp', [
@@ -42,42 +41,7 @@ const App = angular
     $qProvider.errorOnUnhandledRejections(false)
     uiSelectConfig.spinnerClass = 'fa fa-refresh ui-select-spin'
 
-    return __guard__(
-      typeof MathJax !== 'undefined' && MathJax !== null
-        ? MathJax.Hub
-        : undefined,
-      x =>
-        x.Config({
-          messageStyle: 'none',
-          imageFont: null,
-          // Fast preview, introduced in 2.5, is unhelpful due to extra codemirror refresh
-          // and disabling it avoids issues with math processing errors
-          // github.com/overleaf/write_latex/pull/1375
-          'fast-preview': { disabled: true },
-          'HTML-CSS': {
-            availableFonts: ['TeX'],
-            // MathJax's automatic font scaling does not work well when we render math
-            // that isn't yet on the page, so we disable it and set a global font
-            // scale factor
-            scale: 110,
-            matchFontHeight: false,
-          },
-          TeX: {
-            equationNumbers: { autoNumber: 'AMS' },
-            useLabelIDs: false,
-          },
-          skipStartupTypeset: true,
-          tex2jax: {
-            processEscapes: true,
-            // Dollar delimiters are added by the mathjax directive
-            inlineMath: [['\\(', '\\)']],
-            displayMath: [
-              ['$$', '$$'],
-              ['\\[', '\\]'],
-            ],
-          },
-        })
-    )
+    configureMathJax()
   })
 
 App.run(($rootScope, $templateCache) => {
@@ -97,9 +61,3 @@ window.sl_debugging = sl_debugging // make a global flag for debugging code
 window.sl_console = sl_debugging ? console : { log() {} }
 
 export default App
-
-function __guard__(value, transform) {
-  return typeof value !== 'undefined' && value !== null
-    ? transform(value)
-    : undefined
-}

+ 22 - 0
services/web/frontend/js/features/algolia-search/search-wiki.js

@@ -17,3 +17,25 @@ export async function searchWiki(...args) {
   }
   return wikiIdx.search(...args)
 }
+
+export function formatWikiHit(hit) {
+  const pageUnderscored = hit.pageName.replace(/\s/g, '_')
+  const pageSlug = encodeURIComponent(pageUnderscored)
+  const pagePath = hit.kb ? 'how-to' : 'latex'
+
+  let pageAnchor = ''
+  let pageName = hit._highlightResult.pageName.value
+  if (hit.sectionName) {
+    pageAnchor = `#${hit.sectionName.replace(/\s/g, '_')}`
+    pageName += ' - ' + hit.sectionName
+  }
+
+  const body = hit._highlightResult.content.value
+  const content = body
+    .split('\n')
+    .filter(line => line.includes('<em>') && !line.includes('[edit]'))
+    .join('\n...\n')
+
+  const url = `/learn/${pagePath}/${pageSlug}${pageAnchor}`
+  return { url, pageName, content }
+}

+ 2 - 18
services/web/frontend/js/features/contact-form/search.js

@@ -1,23 +1,7 @@
 import _ from 'lodash'
-import { searchWiki } from '../algolia-search/search-wiki'
+import { formatWikiHit, searchWiki } from '../algolia-search/search-wiki'
 import { sendMB } from '../../infrastructure/event-tracking'
 
-function formatHit(hit) {
-  const pageUnderscored = hit.pageName.replace(/\s/g, '_')
-  const pageSlug = encodeURIComponent(pageUnderscored)
-  const pagePath = hit.kb ? 'how-to' : 'latex'
-
-  let pageAnchor = ''
-  let pageName = hit._highlightResult.pageName.value
-  if (hit.sectionName) {
-    pageAnchor = `#${hit.sectionName.replace(/\s/g, '_')}`
-    pageName += ' - ' + hit.sectionName
-  }
-
-  const url = `/learn/${pagePath}/${pageSlug}${pageAnchor}`
-  return { url, pageName }
-}
-
 export function setupSearch(formEl) {
   const inputEl = formEl.querySelector('[name="subject"]')
   const resultsEl = formEl.querySelector('[data-ol-search-results]')
@@ -48,7 +32,7 @@ export function setupSearch(formEl) {
       resultsEl.innerText = ''
 
       for (const hit of hits) {
-        const { url, pageName } = formatHit(hit)
+        const { url, pageName } = formatWikiHit(hit)
         const liEl = document.createElement('li')
 
         const linkEl = document.createElement('a')

+ 103 - 0
services/web/frontend/js/features/faq-search/index.js

@@ -0,0 +1,103 @@
+import _ from 'lodash'
+import { formatWikiHit, searchWiki } from '../algolia-search/search-wiki'
+
+function setupSearch(formEl) {
+  const inputEl = formEl.querySelector('input[type="text"]')
+  const resultsEl = formEl.querySelector('[data-ol-search-results]')
+  const wrapperEl = formEl.querySelector('[data-ol-search-results-wrapper]')
+  const noResultsEl = formEl.querySelector('[data-ol-search-no-results]')
+  const srHelpMsgEl = formEl.querySelector('[data-ol-search-sr-help-message]')
+
+  function hideResultsPane() {
+    wrapperEl.hidden = true
+  }
+  function showResultsPane() {
+    wrapperEl.hidden = false
+    hideNoResultsMsg()
+  }
+  function hideNoResultsMsg() {
+    noResultsEl.hidden = true
+  }
+  function showNoResultsMsg() {
+    noResultsEl.hidden = false
+    hideResultsPane()
+  }
+
+  let lastValue = ''
+
+  async function handleChange() {
+    const value = inputEl.value
+    if (value === lastValue) return
+    lastValue = value
+
+    if (value.length === 0) {
+      hideResultsPane()
+      hideNoResultsMsg()
+      return
+    }
+
+    try {
+      const { hits, nbHits } = await searchWiki(value, {
+        hitsPerPage: 20,
+      })
+      if (nbHits === 0) {
+        showNoResultsMsg()
+        return
+      }
+
+      if (nbHits > 20) {
+        srHelpMsgEl.innerText = `Showing first 20 results of ${nbHits} for ${value}`
+      } else {
+        srHelpMsgEl.innerText = `${nbHits} results for ${value}`
+      }
+
+      resultsEl.innerText = ''
+      for (const hit of hits) {
+        const { url, pageName, content } = formatWikiHit(hit)
+        const linkEl = document.createElement('a')
+        linkEl.className = 'search-result card card-thin'
+        linkEl.href = url
+
+        const headerEl = document.createElement('span')
+        headerEl.innerHTML = pageName
+        linkEl.append(headerEl)
+
+        if (content) {
+          const contentEl = document.createElement('div')
+          contentEl.className = 'search-result-content'
+          contentEl.innerHTML = content
+          linkEl.append(contentEl)
+        }
+
+        resultsEl.append(linkEl)
+      }
+      showResultsPane()
+    } catch (e) {
+      showNoResultsMsg()
+    }
+  }
+  function updateClearBtnVisibility() {
+    const value = inputEl.value
+    formEl.querySelectorAll('[data-ol-clear-search]').forEach(el => {
+      el.hidden = value === ''
+    })
+  }
+
+  function handleClear() {
+    inputEl.value = ''
+    hideResultsPane()
+    hideNoResultsMsg()
+    updateClearBtnVisibility()
+  }
+
+  formEl.querySelectorAll('[data-ol-clear-search]').forEach(el => {
+    el.addEventListener('click', handleClear)
+  })
+  inputEl.addEventListener('input', _.debounce(handleChange, 100))
+  inputEl.addEventListener('input', updateClearBtnVisibility)
+
+  // display initial results
+  handleChange()
+}
+
+document.querySelectorAll('[data-ol-faq-search]').forEach(setupSearch)

+ 42 - 0
services/web/frontend/js/features/mathjax/configure.js

@@ -0,0 +1,42 @@
+/* global MathJax */
+
+import { mathJaxLoaded } from './util'
+
+let configured = false
+
+export function configureMathJax() {
+  if (configured) return
+  if (!mathJaxLoaded()) return
+
+  MathJax.Hub.Config({
+    messageStyle: 'none',
+    imageFont: null,
+    // Fast preview, introduced in 2.5, is unhelpful due to extra codemirror refresh
+    // and disabling it avoids issues with math processing errors
+    // github.com/overleaf/write_latex/pull/1375
+    'fast-preview': { disabled: true },
+    'HTML-CSS': {
+      availableFonts: ['TeX'],
+      // MathJax's automatic font scaling does not work well when we render math
+      // that isn't yet on the page, so we disable it and set a global font
+      // scale factor
+      scale: 110,
+      matchFontHeight: false,
+    },
+    TeX: {
+      equationNumbers: { autoNumber: 'AMS' },
+      useLabelIDs: false,
+    },
+    skipStartupTypeset: true,
+    tex2jax: {
+      processEscapes: true,
+      // Dollar delimiters are added by the mathjax directive
+      inlineMath: [['\\(', '\\)']],
+      displayMath: [
+        ['$$', '$$'],
+        ['\\[', '\\]'],
+      ],
+    },
+  })
+  configured = true
+}

+ 34 - 0
services/web/frontend/js/features/mathjax/index.js

@@ -0,0 +1,34 @@
+/* global MathJax */
+import _ from 'lodash'
+import { configureMathJax } from './configure'
+import { mathJaxLoaded } from './util'
+
+function render(el) {
+  if (!mathJaxLoaded()) return
+  configureMathJax()
+
+  if (!el.hasAttribute('data-ol-no-single-dollar')) {
+    const inlineMathConfig =
+      MathJax.Hub.config &&
+      MathJax.Hub.config.tex2jax &&
+      MathJax.Hub.config.tex2jax.inlineMath
+    const alreadyConfigured = _.find(
+      inlineMathConfig,
+      c => c[0] === '$' && c[1] === '$'
+    )
+
+    if (!alreadyConfigured) {
+      MathJax.Hub.Config({
+        tex2jax: {
+          inlineMath: inlineMathConfig.concat([['$', '$']]),
+        },
+      })
+    }
+  }
+
+  setTimeout(() => {
+    MathJax.Hub.Queue(['Typeset', MathJax.Hub, el])
+  }, 0)
+}
+
+document.querySelectorAll('[data-ol-mathjax]').forEach(render)

+ 5 - 0
services/web/frontend/js/features/mathjax/util.js

@@ -0,0 +1,5 @@
+/* global MathJax */
+
+export function mathJaxLoaded() {
+  return !!(typeof MathJax !== 'undefined' && MathJax && MathJax.Hub)
+}