From 4fe87d35e537f3d295a8985b27b3a23360afa2d5 Mon Sep 17 00:00:00 2001 From: bdbch <6538827+bdbch@users.noreply.github.com> Date: Thu, 10 Aug 2023 20:30:56 -0700 Subject: [PATCH] add overrides to typography extension (#4287) --- .../src/Extensions/Typography/React/index.jsx | 7 +- .../TypographyWithOverrides/React/index.html | 0 .../TypographyWithOverrides/React/index.jsx | 25 +++ .../React/index.spec.js | 15 ++ .../TypographyWithOverrides/Vue/index.html | 0 .../TypographyWithOverrides/Vue/index.spec.js | 15 ++ .../TypographyWithOverrides/Vue/index.vue | 44 +++++ docs/api/extensions/typography.md | 18 ++ .../extension-typography/src/typography.ts | 176 +++++++++--------- 9 files changed, 211 insertions(+), 89 deletions(-) create mode 100644 demos/src/Extensions/TypographyWithOverrides/React/index.html create mode 100644 demos/src/Extensions/TypographyWithOverrides/React/index.jsx create mode 100644 demos/src/Extensions/TypographyWithOverrides/React/index.spec.js create mode 100644 demos/src/Extensions/TypographyWithOverrides/Vue/index.html create mode 100644 demos/src/Extensions/TypographyWithOverrides/Vue/index.spec.js create mode 100644 demos/src/Extensions/TypographyWithOverrides/Vue/index.vue diff --git a/demos/src/Extensions/Typography/React/index.jsx b/demos/src/Extensions/Typography/React/index.jsx index 34d06f92a..002e59854 100644 --- a/demos/src/Extensions/Typography/React/index.jsx +++ b/demos/src/Extensions/Typography/React/index.jsx @@ -7,7 +7,12 @@ import React from 'react' export default () => { const editor = useEditor({ - extensions: [Document, Paragraph, Text, Typography], + extensions: [ + Document, + Paragraph, + Text, + Typography, + ], content: `

“I have been suffering from Typomania all my life, a sickness that is incurable but not lethal.”

— Erik Spiekermann, December 2008

diff --git a/demos/src/Extensions/TypographyWithOverrides/React/index.html b/demos/src/Extensions/TypographyWithOverrides/React/index.html new file mode 100644 index 000000000..e69de29bb diff --git a/demos/src/Extensions/TypographyWithOverrides/React/index.jsx b/demos/src/Extensions/TypographyWithOverrides/React/index.jsx new file mode 100644 index 000000000..af305cbd7 --- /dev/null +++ b/demos/src/Extensions/TypographyWithOverrides/React/index.jsx @@ -0,0 +1,25 @@ +import Document from '@tiptap/extension-document' +import Paragraph from '@tiptap/extension-paragraph' +import Text from '@tiptap/extension-text' +import Typography from '@tiptap/extension-typography' +import { EditorContent, useEditor } from '@tiptap/react' +import React from 'react' + +export default () => { + const editor = useEditor({ + extensions: [ + Document, + Paragraph, + Text, + Typography.configure({ + rightArrow: '=====>', + }), + ], + content: ` +

“I have been suffering from Typomania all my life, a sickness that is incurable but not lethal.”

+

— Erik Spiekermann, December 2008

+ `, + }) + + return +} diff --git a/demos/src/Extensions/TypographyWithOverrides/React/index.spec.js b/demos/src/Extensions/TypographyWithOverrides/React/index.spec.js new file mode 100644 index 000000000..8f3f99625 --- /dev/null +++ b/demos/src/Extensions/TypographyWithOverrides/React/index.spec.js @@ -0,0 +1,15 @@ +context('/src/Extensions/TypographyWithOverrides/React/', () => { + before(() => { + cy.visit('/src/Extensions/TypographyWithOverrides/React/') + }) + + beforeEach(() => { + cy.get('.tiptap').then(([{ editor }]) => { + editor.commands.clearContent() + }) + }) + + it('should use correct override for rightArrow', () => { + cy.get('.tiptap').type('-> Hello!').should('contain', '=====> Hello!') + }) +}) diff --git a/demos/src/Extensions/TypographyWithOverrides/Vue/index.html b/demos/src/Extensions/TypographyWithOverrides/Vue/index.html new file mode 100644 index 000000000..e69de29bb diff --git a/demos/src/Extensions/TypographyWithOverrides/Vue/index.spec.js b/demos/src/Extensions/TypographyWithOverrides/Vue/index.spec.js new file mode 100644 index 000000000..ec4851792 --- /dev/null +++ b/demos/src/Extensions/TypographyWithOverrides/Vue/index.spec.js @@ -0,0 +1,15 @@ +context('/src/Extensions/TypographyWithOverrides/Vue/', () => { + before(() => { + cy.visit('/src/Extensions/TypographyWithOverrides/Vue/') + }) + + beforeEach(() => { + cy.get('.tiptap').then(([{ editor }]) => { + editor.commands.clearContent() + }) + }) + + it('should use correct override for rightArrow', () => { + cy.get('.tiptap').type('-> Hello!').should('contain', '=====> Hello!') + }) +}) diff --git a/demos/src/Extensions/TypographyWithOverrides/Vue/index.vue b/demos/src/Extensions/TypographyWithOverrides/Vue/index.vue new file mode 100644 index 000000000..4912db8c4 --- /dev/null +++ b/demos/src/Extensions/TypographyWithOverrides/Vue/index.vue @@ -0,0 +1,44 @@ + + + diff --git a/docs/api/extensions/typography.md b/docs/api/extensions/typography.md index aa3b068d9..56e441aa2 100644 --- a/docs/api/extensions/typography.md +++ b/docs/api/extensions/typography.md @@ -70,3 +70,21 @@ const editor = new Editor({ ], }) ``` + +### Overriding rules + +You can override the output of a rule by passing a string to the option you want to override. + +```js +import { Editor } from '@tiptap/core' +import Typography from '@tiptap/extension-typography' + +const editor = new Editor({ + extensions: [ + // Disable some included rules + Typography.configure({ + oneHalf: "1 / 2", // this will insert "1 / 2" instead of the default "½" + }), + ], +}) +``` diff --git a/packages/extension-typography/src/typography.ts b/packages/extension-typography/src/typography.ts index 49c5b2379..4ce2f08ef 100644 --- a/packages/extension-typography/src/typography.ts +++ b/packages/extension-typography/src/typography.ts @@ -1,138 +1,138 @@ import { Extension, textInputRule } from '@tiptap/core' export interface TypographyOptions { - emDash: false, - ellipsis: false, - openDoubleQuote: false, - closeDoubleQuote: false, - openSingleQuote: false, - closeSingleQuote: false, - leftArrow: false, - rightArrow: false, - copyright: false, - trademark: false, - servicemark: false, - registeredTrademark: false, - oneHalf: false, - plusMinus: false, - notEqual: false, - laquo: false, - raquo: false, - multiplication: false, - superscriptTwo: false, - superscriptThree: false, - oneQuarter: false, - threeQuarters: false, + emDash: false | string, + ellipsis: false | string, + openDoubleQuote: false | string, + closeDoubleQuote: false | string, + openSingleQuote: false | string, + closeSingleQuote: false | string, + leftArrow: false | string, + rightArrow: false | string, + copyright: false | string, + trademark: false | string, + servicemark: false | string, + registeredTrademark: false | string, + oneHalf: false | string, + plusMinus: false | string, + notEqual: false | string, + laquo: false | string, + raquo: false | string, + multiplication: false | string, + superscriptTwo: false | string, + superscriptThree: false | string, + oneQuarter: false | string, + threeQuarters: false | string, } -export const emDash = textInputRule({ +export const emDash = (override?: string) => textInputRule({ find: /--$/, - replace: '—', + replace: override ?? '—', }) -export const ellipsis = textInputRule({ +export const ellipsis = (override?: string) => textInputRule({ find: /\.\.\.$/, - replace: '…', + replace: override ?? '…', }) -export const openDoubleQuote = textInputRule({ +export const openDoubleQuote = (override?: string) => textInputRule({ find: /(?:^|[\s{[(<'"\u2018\u201C])(")$/, - replace: '“', + replace: override ?? '“', }) -export const closeDoubleQuote = textInputRule({ +export const closeDoubleQuote = (override?: string) => textInputRule({ find: /"$/, - replace: '”', + replace: override ?? '”', }) -export const openSingleQuote = textInputRule({ +export const openSingleQuote = (override?: string) => textInputRule({ find: /(?:^|[\s{[(<'"\u2018\u201C])(')$/, - replace: '‘', + replace: override ?? '‘', }) -export const closeSingleQuote = textInputRule({ +export const closeSingleQuote = (override?: string) => textInputRule({ find: /'$/, - replace: '’', + replace: override ?? '’', }) -export const leftArrow = textInputRule({ +export const leftArrow = (override?: string) => textInputRule({ find: /<-$/, - replace: '←', + replace: override ?? '←', }) -export const rightArrow = textInputRule({ +export const rightArrow = (override?: string) => textInputRule({ find: /->$/, - replace: '→', + replace: override ?? '→', }) -export const copyright = textInputRule({ +export const copyright = (override?: string) => textInputRule({ find: /\(c\)$/, - replace: '©', + replace: override ?? '©', }) -export const trademark = textInputRule({ +export const trademark = (override?: string) => textInputRule({ find: /\(tm\)$/, - replace: '™', + replace: override ?? '™', }) -export const servicemark = textInputRule({ +export const servicemark = (override?: string) => textInputRule({ find: /\(sm\)$/, - replace: '℠', + replace: override ?? '℠', }) -export const registeredTrademark = textInputRule({ +export const registeredTrademark = (override?: string) => textInputRule({ find: /\(r\)$/, - replace: '®', + replace: override ?? '®', }) -export const oneHalf = textInputRule({ +export const oneHalf = (override?: string) => textInputRule({ find: /(?:^|\s)(1\/2)$/, - replace: '½', + replace: override ?? '½', }) -export const plusMinus = textInputRule({ +export const plusMinus = (override?: string) => textInputRule({ find: /\+\/-$/, - replace: '±', + replace: override ?? '±', }) -export const notEqual = textInputRule({ +export const notEqual = (override?: string) => textInputRule({ find: /!=$/, - replace: '≠', + replace: override ?? '≠', }) -export const laquo = textInputRule({ +export const laquo = (override?: string) => textInputRule({ find: /<<$/, - replace: '«', + replace: override ?? '«', }) -export const raquo = textInputRule({ +export const raquo = (override?: string) => textInputRule({ find: />>$/, - replace: '»', + replace: override ?? '»', }) -export const multiplication = textInputRule({ +export const multiplication = (override?: string) => textInputRule({ find: /\d+\s?([*x])\s?\d+$/, - replace: '×', + replace: override ?? '×', }) -export const superscriptTwo = textInputRule({ +export const superscriptTwo = (override?: string) => textInputRule({ find: /\^2$/, - replace: '²', + replace: override ?? '²', }) -export const superscriptThree = textInputRule({ +export const superscriptThree = (override?: string) => textInputRule({ find: /\^3$/, - replace: '³', + replace: override ?? '³', }) -export const oneQuarter = textInputRule({ +export const oneQuarter = (override?: string) => textInputRule({ find: /(?:^|\s)(1\/4)$/, - replace: '¼', + replace: override ?? '¼', }) -export const threeQuarters = textInputRule({ +export const threeQuarters = (override?: string) => textInputRule({ find: /(?:^|\s)(3\/4)$/, - replace: '¾', + replace: override ?? '¾', }) export const Typography = Extension.create({ @@ -142,91 +142,91 @@ export const Typography = Extension.create({ const rules = [] if (this.options.emDash !== false) { - rules.push(emDash) + rules.push(emDash(this.options.emDash)) } if (this.options.ellipsis !== false) { - rules.push(ellipsis) + rules.push(ellipsis(this.options.ellipsis)) } if (this.options.openDoubleQuote !== false) { - rules.push(openDoubleQuote) + rules.push(openDoubleQuote(this.options.openDoubleQuote)) } if (this.options.closeDoubleQuote !== false) { - rules.push(closeDoubleQuote) + rules.push(closeDoubleQuote(this.options.closeDoubleQuote)) } if (this.options.openSingleQuote !== false) { - rules.push(openSingleQuote) + rules.push(openSingleQuote(this.options.openSingleQuote)) } if (this.options.closeSingleQuote !== false) { - rules.push(closeSingleQuote) + rules.push(closeSingleQuote(this.options.closeSingleQuote)) } if (this.options.leftArrow !== false) { - rules.push(leftArrow) + rules.push(leftArrow(this.options.leftArrow)) } if (this.options.rightArrow !== false) { - rules.push(rightArrow) + rules.push(rightArrow(this.options.rightArrow)) } if (this.options.copyright !== false) { - rules.push(copyright) + rules.push(copyright(this.options.copyright)) } if (this.options.trademark !== false) { - rules.push(trademark) + rules.push(trademark(this.options.trademark)) } if (this.options.servicemark !== false) { - rules.push(servicemark) + rules.push(servicemark(this.options.servicemark)) } if (this.options.registeredTrademark !== false) { - rules.push(registeredTrademark) + rules.push(registeredTrademark(this.options.registeredTrademark)) } if (this.options.oneHalf !== false) { - rules.push(oneHalf) + rules.push(oneHalf(this.options.oneHalf)) } if (this.options.plusMinus !== false) { - rules.push(plusMinus) + rules.push(plusMinus(this.options.plusMinus)) } if (this.options.notEqual !== false) { - rules.push(notEqual) + rules.push(notEqual(this.options.notEqual)) } if (this.options.laquo !== false) { - rules.push(laquo) + rules.push(laquo(this.options.laquo)) } if (this.options.raquo !== false) { - rules.push(raquo) + rules.push(raquo(this.options.raquo)) } if (this.options.multiplication !== false) { - rules.push(multiplication) + rules.push(multiplication(this.options.multiplication)) } if (this.options.superscriptTwo !== false) { - rules.push(superscriptTwo) + rules.push(superscriptTwo(this.options.superscriptTwo)) } if (this.options.superscriptThree !== false) { - rules.push(superscriptThree) + rules.push(superscriptThree(this.options.superscriptThree)) } if (this.options.oneQuarter !== false) { - rules.push(oneQuarter) + rules.push(oneQuarter(this.options.oneQuarter)) } if (this.options.threeQuarters !== false) { - rules.push(threeQuarters) + rules.push(threeQuarters(this.options.threeQuarters)) } return rules