Skip to content

Commit

Permalink
feat: add option to merge nested span styles
Browse files Browse the repository at this point in the history
  • Loading branch information
mieze018 committed Nov 30, 2024
1 parent ec8d654 commit 036244d
Show file tree
Hide file tree
Showing 7 changed files with 450 additions and 4 deletions.
70 changes: 70 additions & 0 deletions demos/src/Marks/TextStyle/React/AddGlobalAttributes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import '@tiptap/extension-text-style'

import { Extension, Mark } from '@tiptap/core'

export const AddGlobalAttributes = Extension.create({
name: 'color',

addOptions() {
return {
types: ['textStyle'],
}
},

addGlobalAttributes() {
return [
{
types: this.options.types,
attributes: {
color: {
default: null,
parseHTML: element => element.style.color?.replace(/['"]+/g, ''),
renderHTML: attributes => {
if (!attributes.color) {
return {}
}

return {
style: `color: ${attributes.color}`,
}
},
},
fontFamily: {
default: null,
parseHTML: element => element.style.fontFamily,
renderHTML: attributes => {
if (!attributes.fontFamily) {
return {}
}
return {
style: `font-family: ${attributes.fontFamily}`,
}
},
},
fontSize: {
default: null,
parseHTML: element => element.style.fontSize?.replace(/['"]+/g, ''),
renderHTML: attributes => {
if (!attributes.fontSize) {
return {}
}

return {
style: `font-size: ${attributes.fontSize}`,
}
},
},
},
},
]
},
})

export const Strong = Mark.create({
parseHTML() {
return [{ tag: 'strong' }]
},
renderHTML({ HTMLAttributes }) {
return ['strong', HTMLAttributes, 0]
},
})
82 changes: 81 additions & 1 deletion demos/src/Marks/TextStyle/React/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,92 @@ import TextStyle from '@tiptap/extension-text-style'
import { EditorContent, useEditor } from '@tiptap/react'
import React from 'react'

import { AddGlobalAttributes, Strong } from './AddGlobalAttributes.ts'

export default () => {
const editor = useEditor({
extensions: [Document, Paragraph, Text, TextStyle],
extensions: [
Document,
Paragraph,
Text,
TextStyle.configure({ mergeNestedSpanStyles: true }),
AddGlobalAttributes,
Strong,
],
content: `
<p><span>This has a &lt;span&gt; tag without a style attribute, so it’s thrown away.</span></p>
<p><span style="">But this one is wrapped in a &lt;span&gt; tag with an inline style attribute, so it’s kept - even if it’s empty for now.</span></p>
<p>--- merge Nested Span Styles option enabled ---</p>
<p>
<span style="color: #ff0000;">
<span style="font-family: serif; font-size: 20px;">
red 20px serif
</span>
</span>
</p>
<p>
<span style="color: #0000FF;">
<span style="font-family: serif;">
<span style="font-size: 24px;">
blue 24px serif
</span>
</span>
</span>
</p>
<p>
<span style="color: #00FF00;">
<span style="font-family: serif;">green serif </span>
<span style="font-size: 24px;">green 24px </span>
<span style="font-family: serif;color: #ff0000;">red serif</span>
</span>
</p>
<p>
<span>
plain
<span style="color: #0000FF;">blue</span>
plain
<span style="color: #00FF00;">
green
<span style="font-family: serif;">green serif</span>
</span>
plain
</span>
</p>
<p>
<span style="color: #0000FF;">
blue
<span style="color: #00FF00;">
green
<span style="font-family: serif;">
green serif
<span style="font-size: 24px;">green serif 24px</span>
</span>
</span>
</span>
</p>
<p>
<strong>
strong
<span style="color: #0000FF;">
<strong>
strong blue
<span style="font-size: 24px;">strong blue 24px </span>
<span style="color: #00FF00;">
strong green
<span style="font-family: serif;">strong green serif</span>
</span>
</strong>
</span>
</strong>
</p>
`,
})

Expand Down
80 changes: 79 additions & 1 deletion demos/src/Marks/TextStyle/React/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,83 @@ context('/src/Marks/TextStyle/React/', () => {
cy.visit('/src/Marks/TextStyle/React/')
})

// TODO: Write tests
describe('mergeNestedSpanStyles', () => {
it('should merge styles of a span with one child span', () => {
cy.get('.tiptap > p:nth-child(4) > span')
.should('have.length', 1)
.and('have.text', 'red 20px serif')
.and('have.attr', 'style', 'color: rgb(255, 0, 0); font-family: serif; font-size: 20px')
})
it('should merge styles of a span with one nested child span into the descendant span', () => {
cy.get('.tiptap > p:nth-child(5) > span')
.should('have.length', 1)
.and('have.text', 'blue 24px serif')
.and('have.attr', 'style', 'color: rgb(0, 0, 255); font-family: serif; font-size: 24px')
})
it('should merge styles of a span with multiple child spans into all child spans', () => {
cy.get('.tiptap > p:nth-child(6) > span').should('have.length', 3)
cy.get('.tiptap > p:nth-child(6) > span:nth-child(1)')
.should('have.text', 'green serif ')
.and('have.attr', 'style', 'color: rgb(0, 255, 0); font-family: serif')
cy.get('.tiptap > p:nth-child(6) > span:nth-child(2)')
.should('have.text', 'green 24px ')
.and('have.attr', 'style', 'color: rgb(0, 255, 0); font-size: 24px')
cy.get('.tiptap > p:nth-child(6) > span:nth-child(3)')
.should('have.text', 'red serif')
.and('have.attr', 'style', 'color: rgb(255, 0, 0); font-family: serif')
})
it('should merge styles of descendant spans into each descendant span when the parent span has no style', () => {
cy.get('.tiptap > p:nth-child(7) > span').should('have.length', 4)
cy.get('.tiptap > p:nth-child(7) > span:nth-child(1)')
.should('have.text', 'blue')
.and('have.attr', 'style', 'color: rgb(0, 0, 255)')
cy.get('.tiptap > p:nth-child(7) > span:nth-child(2)')
.should('have.text', 'green ')
.and('have.attr', 'style', 'color: rgb(0, 255, 0)')
cy.get('.tiptap > p:nth-child(7) > span:nth-child(3)')
.should('have.text', 'green serif')
.and('have.attr', 'style', 'color: rgb(0, 255, 0); font-family: serif')
})
it('should merge styles of a span with nested root text and descendant spans into each descendant span', () => {
cy.get('.tiptap > p:nth-child(8) > span').should('have.length', 4)
cy.get('.tiptap > p:nth-child(8) > span:nth-child(1)')
.should('have.text', 'blue ')
.and('have.attr', 'style', 'color: rgb(0, 0, 255)')
cy.get('.tiptap > p:nth-child(8) > span:nth-child(2)')
.should('have.text', 'green ')
.and('have.attr', 'style', 'color: rgb(0, 255, 0)')
cy.get('.tiptap > p:nth-child(8) > span:nth-child(3)')
.should('have.text', 'green serif ')
.and('have.attr', 'style', 'color: rgb(0, 255, 0); font-family: serif')
cy.get('.tiptap > p:nth-child(8) > span:nth-child(4)')
.should('have.text', 'green serif 24px')
.and('have.attr', 'style', 'color: rgb(0, 255, 0); font-family: serif; font-size: 24px')
})
it('should merge styles of descendant spans into each descendant span when the parent span has other tags', () => {
cy.get('.tiptap > p:nth-child(9) > span').should('have.length', 4)
cy.get('.tiptap > p:nth-child(9) > :nth-child(1)')
.should('have.prop', 'tagName', 'STRONG')
.and('have.text', 'strong ')
cy.get('.tiptap > p:nth-child(9) > span:nth-child(2)')
.should('have.text', 'strong blue ')
.and('have.attr', 'style', 'color: rgb(0, 0, 255)')
.find('strong')
.should('exist')
cy.get('.tiptap > p:nth-child(9) > span:nth-child(3)')
.should('have.text', 'strong blue 24px ')
.and('have.attr', 'style', 'color: rgb(0, 0, 255); font-size: 24px')
.find('strong')
.should('exist')
cy.get('.tiptap > p:nth-child(9) > span:nth-child(4)')
.should('have.text', 'strong green ')
.and('have.attr', 'style', 'color: rgb(0, 255, 0)')
.find('strong')
.should('exist')
cy.get('.tiptap > p:nth-child(9) > span:nth-child(5)')
.should('have.text', 'strong green serif')
.and('have.attr', 'style', 'color: rgb(0, 255, 0); font-family: serif')
.find('strong')
.should('exist')
})
})
})
61 changes: 61 additions & 0 deletions demos/src/Marks/TextStyle/Vue/AddGlobalAttributes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import '@tiptap/extension-text-style'

import { Extension } from '@tiptap/core'

export const AddGlobalAttributes = Extension.create({
name: 'color',

addOptions() {
return {
types: ['textStyle'],
}
},

addGlobalAttributes() {
return [
{
types: this.options.types,
attributes: {
color: {
default: null,
parseHTML: element => element.style.color?.replace(/['"]+/g, ''),
renderHTML: attributes => {
if (!attributes.color) {
return {}
}

return {
style: `color: ${attributes.color}`,
}
},
},
fontFamily: {
default: null,
parseHTML: element => element.style.fontFamily,
renderHTML: attributes => {
if (!attributes.fontFamily) {
return {}
}
return {
style: `font-family: ${attributes.fontFamily}`,
}
},
},
fontSize: {
default: null,
parseHTML: element => element.style.fontSize?.replace(/['"]+/g, ''),
renderHTML: attributes => {
if (!attributes.fontSize) {
return {}
}

return {
style: `font-size: ${attributes.fontSize}`,
}
},
},
},
},
]
},
})
80 changes: 79 additions & 1 deletion demos/src/Marks/TextStyle/Vue/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,83 @@ context('/src/Marks/TextStyle/Vue/', () => {
cy.visit('/src/Marks/TextStyle/Vue/')
})

// TODO: Write tests
describe('mergeNestedSpanStyles', () => {
it('should merge styles of a span with one child span', () => {
cy.get('.tiptap > p:nth-child(4) > span')
.should('have.length', 1)
.and('have.text', 'red 20px serif')
.and('have.attr', 'style', 'color: rgb(255, 0, 0); font-family: serif; font-size: 20px')
})
it('should merge styles of a span with one nested child span into the descendant span', () => {
cy.get('.tiptap > p:nth-child(5) > span')
.should('have.length', 1)
.and('have.text', 'blue 24px serif')
.and('have.attr', 'style', 'color: rgb(0, 0, 255); font-family: serif; font-size: 24px')
})
it('should merge styles of a span with multiple child spans into all child spans', () => {
cy.get('.tiptap > p:nth-child(6) > span').should('have.length', 3)
cy.get('.tiptap > p:nth-child(6) > span:nth-child(1)')
.should('have.text', 'green serif ')
.and('have.attr', 'style', 'color: rgb(0, 255, 0); font-family: serif')
cy.get('.tiptap > p:nth-child(6) > span:nth-child(2)')
.should('have.text', 'green 24px ')
.and('have.attr', 'style', 'color: rgb(0, 255, 0); font-size: 24px')
cy.get('.tiptap > p:nth-child(6) > span:nth-child(3)')
.should('have.text', 'red serif')
.and('have.attr', 'style', 'color: rgb(255, 0, 0); font-family: serif')
})
it('should merge styles of descendant spans into each descendant span when the parent span has no style', () => {
cy.get('.tiptap > p:nth-child(7) > span').should('have.length', 4)
cy.get('.tiptap > p:nth-child(7) > span:nth-child(1)')
.should('have.text', 'blue')
.and('have.attr', 'style', 'color: rgb(0, 0, 255)')
cy.get('.tiptap > p:nth-child(7) > span:nth-child(2)')
.should('have.text', 'green ')
.and('have.attr', 'style', 'color: rgb(0, 255, 0)')
cy.get('.tiptap > p:nth-child(7) > span:nth-child(3)')
.should('have.text', 'green serif')
.and('have.attr', 'style', 'color: rgb(0, 255, 0); font-family: serif')
})
it('should merge styles of a span with nested root text and descendant spans into each descendant span', () => {
cy.get('.tiptap > p:nth-child(8) > span').should('have.length', 4)
cy.get('.tiptap > p:nth-child(8) > span:nth-child(1)')
.should('have.text', 'blue ')
.and('have.attr', 'style', 'color: rgb(0, 0, 255)')
cy.get('.tiptap > p:nth-child(8) > span:nth-child(2)')
.should('have.text', 'green ')
.and('have.attr', 'style', 'color: rgb(0, 255, 0)')
cy.get('.tiptap > p:nth-child(8) > span:nth-child(3)')
.should('have.text', 'green serif ')
.and('have.attr', 'style', 'color: rgb(0, 255, 0); font-family: serif')
cy.get('.tiptap > p:nth-child(8) > span:nth-child(4)')
.should('have.text', 'green serif 24px')
.and('have.attr', 'style', 'color: rgb(0, 255, 0); font-family: serif; font-size: 24px')
})
it('should merge styles of descendant spans into each descendant span when the parent span has other tags', () => {
cy.get('.tiptap > p:nth-child(9) > span').should('have.length', 4)
cy.get('.tiptap > p:nth-child(9) > :nth-child(1)')
.should('have.prop', 'tagName', 'STRONG')
.and('have.text', 'strong ')
cy.get('.tiptap > p:nth-child(9) > span:nth-child(2)')
.should('have.text', 'strong blue ')
.and('have.attr', 'style', 'color: rgb(0, 0, 255)')
.find('strong')
.should('exist')
cy.get('.tiptap > p:nth-child(9) > span:nth-child(3)')
.should('have.text', 'strong blue 24px ')
.and('have.attr', 'style', 'color: rgb(0, 0, 255); font-size: 24px')
.find('strong')
.should('exist')
cy.get('.tiptap > p:nth-child(9) > span:nth-child(4)')
.should('have.text', 'strong green ')
.and('have.attr', 'style', 'color: rgb(0, 255, 0)')
.find('strong')
.should('exist')
cy.get('.tiptap > p:nth-child(9) > span:nth-child(5)')
.should('have.text', 'strong green serif')
.and('have.attr', 'style', 'color: rgb(0, 255, 0); font-family: serif')
.find('strong')
.should('exist')
})
})
})
Loading

0 comments on commit 036244d

Please sign in to comment.