mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
[visual] Add decoration for footnotes (#13428)
GitOrigin-RevId: b0c8e475e9d8b4a19977a48b615596b88ce65797
This commit is contained in:
parent
4d39c31acc
commit
8402081d9b
5 changed files with 148 additions and 1 deletions
|
@ -47,6 +47,7 @@ import getMeta from '../../../../utils/meta'
|
|||
import { EditableGraphicsWidget } from './visual-widgets/editable-graphics'
|
||||
import { EditableInlineGraphicsWidget } from './visual-widgets/editable-inline-graphics'
|
||||
import { CloseBrace, OpenBrace } from '../../lezer-latex/latex.terms.mjs'
|
||||
import { FootnoteWidget } from './visual-widgets/footnote'
|
||||
|
||||
type Options = {
|
||||
fileTreeManager: {
|
||||
|
@ -857,6 +858,43 @@ export const atomicDecorations = (options: Options) => {
|
|||
)
|
||||
return false
|
||||
}
|
||||
} else if (
|
||||
commandName === '\\footnote' ||
|
||||
commandName === '\\endnote'
|
||||
) {
|
||||
if (textArgumentNode) {
|
||||
if (
|
||||
state.readOnly &&
|
||||
selectionIntersects(state.selection, nodeRef)
|
||||
) {
|
||||
// a special case for a read-only document:
|
||||
// always display the content, styled differently from the main content.
|
||||
decorations.push(
|
||||
...decorateArgumentBraces(
|
||||
new BraceWidget(),
|
||||
textArgumentNode,
|
||||
nodeRef.from
|
||||
),
|
||||
Decoration.mark({
|
||||
class: 'ol-cm-footnote ol-cm-footnote-view',
|
||||
}).range(textArgumentNode.from, textArgumentNode.to)
|
||||
)
|
||||
} else {
|
||||
if (shouldDecorate(state, nodeRef)) {
|
||||
// collapse the footnote when the selection is outside it
|
||||
decorations.push(
|
||||
Decoration.replace({
|
||||
widget: new FootnoteWidget(
|
||||
commandName === '\\footnote'
|
||||
? 'footnote'
|
||||
: 'endnote'
|
||||
),
|
||||
}).range(nodeRef.from, nodeRef.to)
|
||||
)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (commandName === '\\LaTeX') {
|
||||
if (shouldDecorate(state, nodeRef)) {
|
||||
decorations.push(
|
||||
|
|
|
@ -360,4 +360,22 @@ export const visualTheme = EditorView.theme({
|
|||
top: '18px',
|
||||
right: '18px',
|
||||
},
|
||||
'.ol-cm-footnote': {
|
||||
display: 'inline-flex',
|
||||
padding: '0 0.1em',
|
||||
background: 'rgba(125, 125, 125, 0.25)',
|
||||
borderRadius: '2px',
|
||||
height: '1em',
|
||||
cursor: 'pointer',
|
||||
verticalAlign: 'text-top',
|
||||
'&:not(.ol-cm-footnote-view):hover': {
|
||||
background: 'rgba(125, 125, 125, 0.5)',
|
||||
},
|
||||
'&.ol-cm-footnote-view': {
|
||||
height: 'auto',
|
||||
verticalAlign: 'unset',
|
||||
display: 'inline',
|
||||
padding: '0 0.5em',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import { WidgetType } from '@codemirror/view'
|
||||
|
||||
type NoteType = 'footnote' | 'endnote'
|
||||
|
||||
const symbols: Record<NoteType, string> = {
|
||||
footnote: '*',
|
||||
endnote: '†',
|
||||
}
|
||||
|
||||
export class FootnoteWidget extends WidgetType {
|
||||
constructor(private type: NoteType = 'footnote') {
|
||||
super()
|
||||
}
|
||||
|
||||
toDOM() {
|
||||
const element = document.createElement('span')
|
||||
element.classList.add('ol-cm-footnote')
|
||||
element.setAttribute('role', 'button')
|
||||
element.innerHTML = symbols[this.type]
|
||||
return element
|
||||
}
|
||||
|
||||
eq(widget: FootnoteWidget) {
|
||||
return this.type === widget.type
|
||||
}
|
||||
|
||||
updateDOM(element: HTMLElement): boolean {
|
||||
element.innerHTML = symbols[this.type]
|
||||
return true
|
||||
}
|
||||
|
||||
ignoreEvent(event: Event) {
|
||||
return event.type !== 'mousedown' && event.type !== 'mouseup'
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
import { mockScope } from '../helpers/mock-scope'
|
||||
import { EditorProviders } from '../../../helpers/editor-providers'
|
||||
import CodemirrorEditor from '../../../../../frontend/js/features/source-editor/components/codemirror-editor'
|
||||
import { FC } from 'react'
|
||||
|
||||
const Container: FC = ({ children }) => (
|
||||
<div style={{ width: 785, height: 785 }}>{children}</div>
|
||||
)
|
||||
|
||||
describe('<CodeMirrorEditor/> in Visual mode with read-only permission', function () {
|
||||
beforeEach(function () {
|
||||
window.metaAttributesCache.set('ol-preventCompileOnLoad', true)
|
||||
window.metaAttributesCache.set(
|
||||
'ol-mathJax3Path',
|
||||
'https://unpkg.com/mathjax@3.2.2/es5/tex-svg-full.js'
|
||||
)
|
||||
cy.interceptEvents()
|
||||
cy.interceptSpelling()
|
||||
})
|
||||
|
||||
it('decorates footnote content', function () {
|
||||
const scope = mockScope('Foo \\footnote{Bar.} ')
|
||||
scope.permissionsLevel = 'readOnly'
|
||||
scope.editor.showVisual = true
|
||||
|
||||
cy.mount(
|
||||
<Container>
|
||||
<EditorProviders scope={scope}>
|
||||
<CodemirrorEditor />
|
||||
</EditorProviders>
|
||||
</Container>
|
||||
)
|
||||
|
||||
// wait for the content to be parsed and revealed
|
||||
cy.get('.cm-content').should('have.css', 'opacity', '1')
|
||||
|
||||
// select the footnote, so it expands
|
||||
cy.get('.ol-cm-footnote').click()
|
||||
|
||||
cy.get('.cm-line').eq(0).as('first-line')
|
||||
cy.get('@first-line').should('contain', 'Foo')
|
||||
cy.get('@first-line').should('contain', 'Bar')
|
||||
})
|
||||
})
|
|
@ -10,7 +10,7 @@ const Container: FC = ({ children }) => (
|
|||
<div style={{ width: 785, height: 785 }}>{children}</div>
|
||||
)
|
||||
|
||||
describe('<CodeMirrorEditor/> in Rich Text mode', function () {
|
||||
describe('<CodeMirrorEditor/> in Visual mode', function () {
|
||||
beforeEach(function () {
|
||||
window.metaAttributesCache.set('ol-preventCompileOnLoad', true)
|
||||
window.metaAttributesCache.set(
|
||||
|
@ -46,6 +46,10 @@ describe('<CodeMirrorEditor/> in Rich Text mode', function () {
|
|||
cy.get('@first-line').click()
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
window.metaAttributesCache.clear()
|
||||
})
|
||||
|
||||
forEach(['LaTeX', 'TeX']).it('renders the %s logo', function (logo) {
|
||||
cy.get('@first-line').type(`\\${logo}{{}}{Enter}`)
|
||||
cy.get('@first-line').should('have.text', logo)
|
||||
|
@ -374,6 +378,14 @@ describe('<CodeMirrorEditor/> in Rich Text mode', function () {
|
|||
cy.get('.ol-cm-author').should('have.text', 'Author')
|
||||
})
|
||||
|
||||
it('decorates footnotes', function () {
|
||||
cy.get('@first-line').type('Foo \\footnote{{}Bar.} ')
|
||||
cy.get('@first-line').should('contain', 'Foo')
|
||||
cy.get('@first-line').should('not.contain', 'Bar')
|
||||
cy.get('@first-line').type('{leftArrow}')
|
||||
cy.get('@first-line').should('have.text', 'Foo \\footnote{Bar.} ')
|
||||
})
|
||||
|
||||
// TODO: \input
|
||||
// TODO: Math
|
||||
// TODO: Abstract
|
||||
|
|
Loading…
Reference in a new issue