Skip to content

Commit cb3df66

Browse files
Merge pull request #7521 from nextcloud/feat/tiptap_typography
Add typography extension
2 parents 86fec6f + f6fe2f4 commit cb3df66

6 files changed

Lines changed: 202 additions & 0 deletions

File tree

cypress/e2e/inputRules.spec.js

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import { randUser } from '../utils/index.js'
7+
const user = randUser()
8+
9+
const testPlainInputRule = (chars, expected) => {
10+
cy.getContent().type(chars)
11+
cy.getContent().should('contain', expected)
12+
return cy.closeFile()
13+
}
14+
15+
describe('input rules', () => {
16+
before(() => {
17+
cy.createUser(user)
18+
})
19+
20+
beforeEach(() => {
21+
cy.login(user)
22+
cy.uploadTestFile()
23+
cy.visit('/apps/files')
24+
cy.openTestFile()
25+
})
26+
27+
it('unordered list item', () => {
28+
cy.getContent().type('* item')
29+
cy.getContent().find('ul').find('li').should('contain', 'item')
30+
cy.closeFile()
31+
})
32+
33+
it('ordered list item', () => {
34+
cy.getContent().type('1. item')
35+
cy.getContent().find('ol').find('li').should('contain', 'item')
36+
cy.closeFile()
37+
})
38+
39+
it('task list item', () => {
40+
cy.getContent().type('* [] item')
41+
cy.getContent().find('ul').find('li').find('input[type="checkbox"]')
42+
cy.getContent().find('ul').find('li').should('contain', 'item')
43+
cy.closeFile()
44+
})
45+
46+
it('blockquote', () => {
47+
cy.getContent().type('> quote')
48+
cy.getContent().find('blockquote').should('contain', 'uote')
49+
cy.closeFile()
50+
})
51+
52+
it('codeblock', () => {
53+
cy.getContent().type('```{enter}code')
54+
cy.getContent().find('pre').should('contain', 'code')
55+
cy.closeFile()
56+
})
57+
58+
it('inline code', () => {
59+
cy.getContent().type('`code`')
60+
cy.getContent().find('code').should('contain', 'code')
61+
cy.closeFile()
62+
})
63+
64+
it('link', () => {
65+
cy.getContent().type('[link](https://nextcloud.com/)')
66+
cy.getContent().find('a').should('contain', 'link')
67+
cy.closeFile()
68+
})
69+
70+
it('emoji', () => testPlainInputRule(':+1{enter}', '👍'))
71+
72+
it('typography: ellipsis', () => testPlainInputRule('...', '…'))
73+
it('typography: copyright', () => testPlainInputRule('(c)', '©'))
74+
it('typography: oneHalf', () => testPlainInputRule('1/2 ', '½'))
75+
it('typography: superscriptTwo', () => testPlainInputRule('^2', '²'))
76+
it('typography: emDash', () => testPlainInputRule('-- ', '—'))
77+
it('typography: leftArrow', () => testPlainInputRule('<- ', '←'))
78+
it('typography: rightArrow', () => testPlainInputRule('->', '→'))
79+
it('typography: leftRightArrow', () => testPlainInputRule('<->', '↔'))
80+
it('typography: leftRightDoubleArrow', () => testPlainInputRule('<=>', '⇔'))
81+
it('typography: leftRightLongArrow', () => testPlainInputRule('<-->', '⟷'))
82+
})

cypress/e2e/openreadonly.spec.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ describe('Open read-only mode', function () {
7272
cy.visit('/apps/files')
7373
})
7474

75+
after(function () {
76+
setReadOnlyMode(0)
77+
})
78+
7579
it('Test read-only markdown file', function () {
7680
cy.openFile('test.md')
7781

package-lock.json

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
"@tiptap/extension-task-item": "^2.26.1",
7777
"@tiptap/extension-task-list": "^2.26.1",
7878
"@tiptap/extension-text": "^2.26.1",
79+
"@tiptap/extension-typography": "^2.26.1",
7980
"@tiptap/extension-underline": "^2.26.1",
8081
"@tiptap/pm": "^2.26.1",
8182
"@tiptap/suggestion": "^2.26.1",

src/extensions/RichText.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import Markdown from './../extensions/Markdown.js'
2525
import Mention from './../extensions/Mention.js'
2626
import Search from './../extensions/Search.js'
2727
import TextDirection from './../extensions/TextDirection.ts'
28+
import Typography from './../extensions/Typography.ts'
2829
import BulletList from './../nodes/BulletList.js'
2930
import Callouts from './../nodes/Callouts.js'
3031
import CodeBlock from './../nodes/CodeBlock.js'
@@ -132,6 +133,7 @@ export default Extension.create({
132133
'taskItem',
133134
],
134135
}),
136+
Typography,
135137
]
136138
const additionalExtensionNames = this.options.extensions.map((e) => e.name)
137139
return [

src/extensions/Typography.ts

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import { textInputRule } from '@tiptap/core'
7+
import { Typography as TiptapTypography } from '@tiptap/extension-typography'
8+
9+
export const emDash = () =>
10+
textInputRule({
11+
find: /(?<![<])-- $/,
12+
replace: '— ',
13+
})
14+
15+
export const leftRightArrow = () =>
16+
textInputRule({
17+
find: /<->$/,
18+
replace: '↔',
19+
})
20+
21+
export const leftArrow = () =>
22+
textInputRule({
23+
find: /<- $/,
24+
replace: '← ',
25+
})
26+
27+
export const rightArrow = () =>
28+
textInputRule({
29+
find: /(?<![<-])->$/,
30+
replace: '→',
31+
})
32+
33+
export const leftRightDoubleArrow = () =>
34+
textInputRule({
35+
find: /<=>$/,
36+
replace: '⇔',
37+
})
38+
39+
export const leftDoubleArrow = () =>
40+
textInputRule({
41+
find: /<= $/,
42+
replace: '⇐ ',
43+
})
44+
45+
export const rightDoubleArrow = () =>
46+
textInputRule({
47+
find: /(?<![<=])=>$/,
48+
replace: '⇒',
49+
})
50+
51+
export const leftRightLongArrow = () =>
52+
textInputRule({
53+
find: /<-->$/,
54+
replace: '⟷',
55+
})
56+
57+
export const leftLongArrow = () =>
58+
textInputRule({
59+
find: /<-- $/,
60+
replace: '⟵ ',
61+
})
62+
63+
export const rightLongArrow = () =>
64+
textInputRule({
65+
find: /(?<!<)-->$/,
66+
replace: '⟶',
67+
})
68+
69+
const Typography = TiptapTypography.extend({
70+
addOptions() {
71+
const options = { ...this.parent?.() }
72+
options.emDash = false
73+
options.leftArrow = false
74+
options.rightArrow = false
75+
return options
76+
},
77+
78+
addInputRules() {
79+
const rules = this.parent?.() || []
80+
rules.push(emDash())
81+
rules.push(leftArrow())
82+
rules.push(rightArrow())
83+
rules.push(leftRightArrow())
84+
rules.push(leftRightDoubleArrow())
85+
rules.push(leftDoubleArrow())
86+
rules.push(rightDoubleArrow())
87+
rules.push(leftRightLongArrow())
88+
rules.push(leftLongArrow())
89+
rules.push(rightLongArrow())
90+
return rules
91+
},
92+
})
93+
94+
export default Typography

0 commit comments

Comments
 (0)