mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-07 20:31:06 -05:00
[visual] support for multiple authors in /maketitle (#13713)
* [visual] support for multiple titles in /maketitle * support \and in author name * using isMatchWith * fix isMatchWith check * fix isMatchWith... again * add tests * test author text replace and mouse events * prettier * don't handle updating node events * use the arguments order as before GitOrigin-RevId: 99b3d1c22a2dc1258a5cdf95738852028895f54a
This commit is contained in:
parent
7487f37bc1
commit
9cb0122ea3
3 changed files with 120 additions and 44 deletions
|
@ -142,11 +142,11 @@ export const atomicDecorations = (options: Options) => {
|
|||
node: SyntaxNode
|
||||
content: string
|
||||
}
|
||||
author?: {
|
||||
authors: {
|
||||
node: SyntaxNode
|
||||
content: string
|
||||
}
|
||||
} = { from: 0, to: 0 }
|
||||
}[]
|
||||
} = { from: 0, to: 0, authors: [] }
|
||||
|
||||
// find the positions of the title and author in the preamble
|
||||
tree.iterate({
|
||||
|
@ -165,7 +165,7 @@ export const atomicDecorations = (options: Options) => {
|
|||
const node = nodeRef.node.getChild('TextArgument')
|
||||
if (node) {
|
||||
const content = state.sliceDoc(node.from, node.to)
|
||||
preamble.author = { node, content }
|
||||
preamble.authors.push({ node, content })
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -10,10 +10,10 @@ type Preamble = {
|
|||
node: SyntaxNode
|
||||
content: string
|
||||
}
|
||||
author?: {
|
||||
authors: {
|
||||
node: SyntaxNode
|
||||
content: string
|
||||
}
|
||||
}[]
|
||||
}
|
||||
|
||||
export class MakeTitleWidget extends WidgetType {
|
||||
|
@ -32,10 +32,7 @@ export class MakeTitleWidget extends WidgetType {
|
|||
}
|
||||
|
||||
eq(widget: MakeTitleWidget) {
|
||||
return isShallowEqualPreamble(widget.preamble, this.preamble, [
|
||||
'title',
|
||||
'author',
|
||||
])
|
||||
return isShallowEqualPreamble(widget.preamble, this.preamble)
|
||||
}
|
||||
|
||||
updateDOM(element: HTMLElement, view: EditorView): boolean {
|
||||
|
@ -80,26 +77,30 @@ export class MakeTitleWidget extends WidgetType {
|
|||
})
|
||||
}
|
||||
|
||||
if (this.preamble.author) {
|
||||
const authorsElement = buildAuthorsElement(
|
||||
view.state,
|
||||
this.preamble.author.node
|
||||
)
|
||||
authorsElement.addEventListener('mouseup', () => {
|
||||
if (this.preamble.author) {
|
||||
selectNode(view, this.preamble.author.node)
|
||||
}
|
||||
})
|
||||
if (this.preamble.authors.length) {
|
||||
const authorsElement = buildAuthorsElement(this.preamble.authors, view)
|
||||
element.append(authorsElement)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const isShallowEqualPreamble = (
|
||||
a: Preamble,
|
||||
b: Preamble,
|
||||
fields: Array<keyof Preamble>
|
||||
) => fields.every(field => a[field]?.content === b[field]?.content)
|
||||
function isShallowEqualPreamble(a: Preamble, b: Preamble) {
|
||||
if (a.title?.content !== b.title?.content) {
|
||||
return false // title changed
|
||||
}
|
||||
|
||||
if (a.authors.length !== b.authors.length) {
|
||||
return false // number of authors changed
|
||||
}
|
||||
|
||||
for (let i = 0; i < a.authors.length; i++) {
|
||||
if (a.authors[i].content !== b.authors[i].content) {
|
||||
return false // author changed
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function buildTitleElement(
|
||||
state: EditorState,
|
||||
|
@ -112,28 +113,33 @@ function buildTitleElement(
|
|||
}
|
||||
|
||||
function buildAuthorsElement(
|
||||
state: EditorState,
|
||||
argumentNode: SyntaxNode
|
||||
): HTMLDivElement {
|
||||
const element = document.createElement('div')
|
||||
element.classList.add('ol-cm-authors')
|
||||
authors: { node: SyntaxNode; content: string }[],
|
||||
view: EditorView
|
||||
) {
|
||||
const authorsElement = document.createElement('div')
|
||||
authorsElement.classList.add('ol-cm-authors')
|
||||
|
||||
const content = state.sliceDoc(argumentNode.from + 1, argumentNode.to - 1)
|
||||
const authors = content.replaceAll(/\s+/g, ' ').split('\\and')
|
||||
for (const { node, content } of authors) {
|
||||
const authorContent = content.slice(1, -1) // trimming the braces
|
||||
const authors = authorContent.replaceAll(/\s+/g, ' ').split('\\and')
|
||||
|
||||
for (const authorParts of authors) {
|
||||
const authorElement = document.createElement('div')
|
||||
authorElement.classList.add('ol-cm-author')
|
||||
for (const author of authors) {
|
||||
const authorElement = document.createElement('div')
|
||||
authorElement.classList.add('ol-cm-author')
|
||||
|
||||
for (const authorInfoItem of authorParts.split('\\\\')) {
|
||||
const authorLineElement = document.createElement('div')
|
||||
authorLineElement.classList.add('ol-cm-author-line')
|
||||
authorLineElement.textContent = authorInfoItem.trim()
|
||||
authorElement.appendChild(authorLineElement)
|
||||
for (const authorInfoItem of author.split('\\\\')) {
|
||||
const authorLineElement = document.createElement('div')
|
||||
authorLineElement.classList.add('ol-cm-author-line')
|
||||
authorLineElement.textContent = authorInfoItem.trim()
|
||||
authorElement.appendChild(authorLineElement)
|
||||
}
|
||||
|
||||
authorElement.addEventListener('mouseup', () => {
|
||||
selectNode(view, node)
|
||||
})
|
||||
authorsElement.append(authorElement)
|
||||
}
|
||||
|
||||
element.append(authorElement)
|
||||
}
|
||||
|
||||
return element
|
||||
return authorsElement
|
||||
}
|
||||
|
|
|
@ -370,8 +370,78 @@ describe('<CodeMirrorEditor/> in Visual mode', function () {
|
|||
cy.get('@first-line').should('have.text', 'Foo \\footnote{Bar.} ')
|
||||
})
|
||||
|
||||
it('should show document preamble', function () {
|
||||
cy.get('@first-line').type(
|
||||
[
|
||||
'\\author{{}Author}',
|
||||
'\\title{{}Document title}',
|
||||
'\\begin{{}document}',
|
||||
'\\maketitle',
|
||||
'\\end{{}document}',
|
||||
'',
|
||||
].join('{Enter}')
|
||||
)
|
||||
cy.get('.ol-cm-preamble-widget').should('have.length', 1)
|
||||
cy.get('.ol-cm-preamble-widget').click()
|
||||
|
||||
cy.get('.ol-cm-preamble-line').eq(0).should('contain', '\\author{Author}')
|
||||
cy.get('.ol-cm-preamble-line')
|
||||
.eq(1)
|
||||
.should('contain', '\\title{Document title}')
|
||||
cy.get('.ol-cm-preamble-line').eq(2).should('contain', '\\begin{document}')
|
||||
cy.get('.ol-cm-preamble-line').eq(3).should('not.exist')
|
||||
})
|
||||
|
||||
it('should show multiple authors', function () {
|
||||
cy.get('@first-line').type(
|
||||
[
|
||||
'\\author{{}Author \\and Author2}',
|
||||
'\\author{{}Author3}',
|
||||
'\\title{{}Document title}',
|
||||
'\\begin{{}document}',
|
||||
'\\maketitle',
|
||||
'\\end{{}document}',
|
||||
'',
|
||||
].join('{Enter}')
|
||||
)
|
||||
cy.get('.ol-cm-preamble-widget').should('have.length', 1)
|
||||
cy.get('.ol-cm-preamble-widget').click()
|
||||
|
||||
cy.get('.ol-cm-authors').should('have.length', 1)
|
||||
cy.get('.ol-cm-authors .ol-cm-author').should('have.length', 3)
|
||||
})
|
||||
|
||||
it('should update authors', function () {
|
||||
cy.get('@first-line').type(
|
||||
[
|
||||
'\\author{{}Author \\and Author2}',
|
||||
'\\author{{}Author3}',
|
||||
'\\title{{}Document title}',
|
||||
'\\begin{{}document}',
|
||||
'\\maketitle',
|
||||
'\\end{{}document}',
|
||||
'',
|
||||
].join('{Enter}')
|
||||
)
|
||||
cy.get('.ol-cm-preamble-widget').should('have.length', 1)
|
||||
cy.get('.ol-cm-preamble-widget').click()
|
||||
|
||||
cy.get('.ol-cm-authors').should('have.length', 1)
|
||||
cy.get('.ol-cm-author').eq(0).should('contain', 'Author')
|
||||
cy.get('.ol-cm-author').eq(1).should('contain', 'Author2')
|
||||
cy.get('.ol-cm-author').eq(2).should('contain', 'Author3')
|
||||
|
||||
cy.get('.ol-cm-author').eq(0).click()
|
||||
cy.get('.ol-cm-preamble-line').eq(0).type('{leftarrow}{backspace}New')
|
||||
cy.get('.ol-cm-author').eq(1).should('contain', 'AuthorNew')
|
||||
|
||||
// update author without changing node from/to coordinates
|
||||
cy.get('.ol-cm-author').eq(0).click()
|
||||
cy.get('.ol-cm-preamble-line').eq(0).type('{leftarrow}{shift}{leftarrow}X')
|
||||
cy.get('.ol-cm-author').eq(1).should('contain', 'AuthorNeX')
|
||||
})
|
||||
|
||||
// TODO: \input
|
||||
// TODO: Math
|
||||
// TODO: Abstract
|
||||
// TODO: Preamble
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue