feat(text-style): merge all pkgs into text-style, add background-color and line-height (#6001)

This commit is contained in:
Nick Perez 2025-01-08 08:03:52 +01:00 committed by GitHub
parent 6a53bb2699
commit 0b4981c832
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
61 changed files with 1052 additions and 162 deletions

View File

@ -0,0 +1,5 @@
---
'@tiptap/extension-text-style': minor
---
Add `toggleTextStyle` command and add typings for the commands parameters based on the installed extensions that include `textStyle`

View File

@ -0,0 +1,5 @@
---
'@tiptap/extension-background-color': patch
---
Adds a new extension, `extension-background-color` which can color the background of `textStyle`s

View File

@ -0,0 +1,5 @@
---
'@tiptap/extension-line-height': patch
---
This adds the `extension-line-height` package which controls the line-height of text like `font-size`, `font-family`, etc.

View File

@ -1,8 +1,7 @@
import './styles.scss'
import { Color } from '@tiptap/extension-color'
import ListItem from '@tiptap/extension-list-item'
import TextStyle from '@tiptap/extension-text-style'
import { Color, TextStyle } from '@tiptap/extension-text-style'
import { EditorContent, useEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import React, { useCallback } from 'react'

View File

@ -1,10 +1,9 @@
import './styles.scss'
import { Color } from '@tiptap/extension-color'
import { Image } from '@tiptap/extension-image'
import Link from '@tiptap/extension-link'
import ListItem from '@tiptap/extension-list-item'
import TextStyle from '@tiptap/extension-text-style'
import { Color, TextStyle } from '@tiptap/extension-text-style'
import { EditorProvider, useCurrentEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import React, { useCallback } from 'react'

View File

@ -1,9 +1,8 @@
import './styles.scss'
import { Color } from '@tiptap/extension-color'
import ListItem from '@tiptap/extension-list-item'
import Mentions from '@tiptap/extension-mention'
import TextStyle from '@tiptap/extension-text-style'
import { Color , TextStyle } from '@tiptap/extension-text-style'
import { EditorProvider } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import React from 'react'

View File

@ -1,8 +1,7 @@
import './styles.scss'
import { Color } from '@tiptap/extension-color'
import ListItem from '@tiptap/extension-list-item'
import TextStyle from '@tiptap/extension-text-style'
import { Color , TextStyle } from '@tiptap/extension-text-style'
import { EditorProvider, useCurrentEditor, useEditorState } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import React from 'react'

View File

@ -1,9 +1,9 @@
<script>
import "./styles.scss";
import { Color } from '@tiptap/extension-color'
import { Color } from '@tiptap/extension-text-style'
import ListItem from '@tiptap/extension-list-item'
import TextStyle from '@tiptap/extension-text-style'
import { TextStyle } from '@tiptap/extension-text-style'
import StarterKit from "@tiptap/starter-kit";
import { Editor } from "@tiptap/core";
import { onMount } from "svelte";

View File

@ -119,9 +119,8 @@
</template>
<script>
import { Color } from '@tiptap/extension-color'
import ListItem from '@tiptap/extension-list-item'
import TextStyle from '@tiptap/extension-text-style'
import { Color, TextStyle } from '@tiptap/extension-text-style'
import StarterKit from '@tiptap/starter-kit'
import { Editor, EditorContent } from '@tiptap/vue-3'

View File

@ -1,8 +1,7 @@
import './styles.scss'
import { Color } from '@tiptap/extension-color'
import ListItem from '@tiptap/extension-list-item'
import TextStyle from '@tiptap/extension-text-style'
import { Color, TextStyle } from '@tiptap/extension-text-style'
import { EditorProvider, JSONContent, useCurrentEditor, useEditorState } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import { renderToHTMLString, renderToMarkdown, renderToReactElement } from '@tiptap/static-renderer'

View File

@ -104,7 +104,6 @@ import Bold from '@tiptap/extension-bold'
import BulletList from '@tiptap/extension-bullet-list'
import Code from '@tiptap/extension-code'
import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight'
import Color from '@tiptap/extension-color'
import Document from '@tiptap/extension-document'
import Dropcursor from '@tiptap/extension-dropcursor'
import Gapcursor from '@tiptap/extension-gapcursor'
@ -129,7 +128,7 @@ import TaskItem from '@tiptap/extension-task-item'
import TaskList from '@tiptap/extension-task-list'
import Text from '@tiptap/extension-text'
import TextAlign from '@tiptap/extension-text-align'
import TextStyle from '@tiptap/extension-text-style'
import { Color, TextStyle } from '@tiptap/extension-text-style'
import Underline from '@tiptap/extension-underline'
import { Editor, EditorContent } from '@tiptap/vue-3'
import { all, createLowlight } from 'lowlight'

View File

@ -1,8 +1,7 @@
import './styles.scss'
import { Color } from '@tiptap/extension-color'
import ListItem from '@tiptap/extension-list-item'
import TextStyle from '@tiptap/extension-text-style'
import { Color, TextStyle } from '@tiptap/extension-text-style'
import { EditorContent, Node, useEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import React from 'react'

View File

@ -0,0 +1,56 @@
context('/src/Extensions/BackgroundColor/React/', () => {
before(() => {
cy.visit('/src/Extensions/BackgroundColor/React/')
})
beforeEach(() => {
cy.get('.tiptap').then(([{ editor }]) => {
editor.commands.setContent('<p>Example Text</p>')
})
cy.get('.tiptap').type('{selectall}')
})
it('should set the background color of the selected text', () => {
cy.get('[data-testid="setPurple"]').should('not.have.class', 'is-active').click().should('have.class', 'is-active')
cy.get('.tiptap').find('span').should('have.attr', 'style', 'background-color: #958DF1')
})
it('should remove the background color of the selected text', () => {
cy.get('[data-testid="setPurple"]').click()
cy.get('.tiptap span').should('exist')
cy.get('[data-testid="unsetBackgroundColor"]').click()
cy.get('.tiptap span').should('not.exist')
})
it('should change background color with color picker', () => {
cy.get('input[type=color]').invoke('val', '#ff0000').trigger('input')
cy.get('.tiptap').find('span').should('have.attr', 'style', 'background-color: #ff0000')
})
it('should match background color and color picker color values', () => {
cy.get('[data-testid="setPurple"]').click()
cy.get('input[type=color]').should('have.value', '#958df1')
})
it('should preserve background color on new lines', () => {
cy.get('[data-testid="setPurple"]').click()
cy.get('.ProseMirror').type('Example Text{enter}')
cy.get('[data-testid="setPurple"]').should('have.class', 'is-active')
})
it('should unset background color on new lines after unset clicked', () => {
cy.get('[data-testid="setPurple"]').click()
cy.get('.ProseMirror').type('Example Text{enter}')
cy.get('[data-testid="unsetBackgroundColor"]').click()
cy.get('.ProseMirror').type('Example Text')
cy.get('[data-testid="setPurple"]').should('not.have.class', 'is-active')
})
})

View File

@ -0,0 +1,109 @@
import './styles.scss'
import Document from '@tiptap/extension-document'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
import { BackgroundColor, TextStyle } from '@tiptap/extension-text-style'
import { EditorContent, useEditor, useEditorState } from '@tiptap/react'
import React from 'react'
export default () => {
const editor = useEditor({
extensions: [Document, Paragraph, Text, TextStyle, BackgroundColor],
content: `
<p><span style="background-color: #958DF1">Oh, for some reason thats purple.</span></p>
`,
})
const editorState = useEditorState({
editor,
selector: ctx => {
return {
color: ctx.editor.getAttributes('textStyle').backgroundColor,
isPurple: ctx.editor.isActive('textStyle', { backgroundColor: '#958DF1' }),
isRed: ctx.editor.isActive('textStyle', { backgroundColor: '#F98181' }),
isOrange: ctx.editor.isActive('textStyle', { backgroundColor: '#FBBC88' }),
isYellow: ctx.editor.isActive('textStyle', { backgroundColor: '#FAF594' }),
isBlue: ctx.editor.isActive('textStyle', { backgroundColor: '#70CFF8' }),
isTeal: ctx.editor.isActive('textStyle', { backgroundColor: '#94FADB' }),
isGreen: ctx.editor.isActive('textStyle', { backgroundColor: '#B9F18D' }),
}
},
})
if (!editor) {
return null
}
return (
<>
<div className="control-group">
<div className="button-group">
<input
type="color"
onInput={event => editor.chain().focus().setBackgroundColor(event.currentTarget.value).run()}
value={editorState.color}
data-testid="setBackgroundColor"
/>
<button
onClick={() => editor.chain().focus().setBackgroundColor('#958DF1').run()}
className={editorState.isPurple ? 'is-active' : ''}
data-testid="setPurple"
>
Purple
</button>
<button
onClick={() => editor.chain().focus().setBackgroundColor('#F98181').run()}
className={editorState.isRed ? 'is-active' : ''}
data-testid="setRed"
>
Red
</button>
<button
onClick={() => editor.chain().focus().setBackgroundColor('#FBBC88').run()}
className={editorState.isOrange ? 'is-active' : ''}
data-testid="setOrange"
>
Orange
</button>
<button
onClick={() => editor.chain().focus().setBackgroundColor('#FAF594').run()}
className={editorState.isYellow ? 'is-active' : ''}
data-testid="setYellow"
>
Yellow
</button>
<button
onClick={() => editor.chain().focus().setBackgroundColor('#70CFF8').run()}
className={editorState.isBlue ? 'is-active' : ''}
data-testid="setBlue"
>
Blue
</button>
<button
onClick={() => editor.chain().focus().setBackgroundColor('#94FADB').run()}
className={editorState.isTeal ? 'is-active' : ''}
data-testid="setTeal"
>
Teal
</button>
<button
onClick={() => editor.chain().focus().setBackgroundColor('#B9F18D').run()}
className={editorState.isGreen ? 'is-active' : ''}
data-testid="setGreen"
>
Green
</button>
<button
onClick={() => editor.chain().focus().unsetBackgroundColor().run()}
data-testid="unsetBackgroundColor"
>
Unset color
</button>
</div>
</div>
<EditorContent editor={editor} />
</>
)
}

View File

@ -0,0 +1,6 @@
/* Basic editor styles */
.tiptap {
:first-child {
margin-top: 0;
}
}

View File

@ -0,0 +1,56 @@
context('/src/Extensions/BackgroundColor/Vue/', () => {
before(() => {
cy.visit('/src/Extensions/BackgroundColor/Vue/')
})
beforeEach(() => {
cy.get('.tiptap').then(([{ editor }]) => {
editor.commands.setContent('<p>Example Text</p>')
})
cy.get('.tiptap').type('{selectall}')
})
it('should set the background color of the selected text', () => {
cy.get('[data-testid="setPurple"]').should('not.have.class', 'is-active').click().should('have.class', 'is-active')
cy.get('.tiptap').find('span').should('have.attr', 'style', 'background-color: #958DF1')
})
it('should remove the background color of the selected text', () => {
cy.get('[data-testid="setPurple"]').click()
cy.get('.tiptap span').should('exist')
cy.get('[data-testid="unsetBackgroundColor"]').click()
cy.get('.tiptap span').should('not.exist')
})
it('should change background color with color picker', () => {
cy.get('input[type=color]').invoke('val', '#ff0000').trigger('input')
cy.get('.tiptap').find('span').should('have.attr', 'style', 'background-color: #ff0000')
})
it('should match background color and color picker color values', () => {
cy.get('[data-testid="setPurple"]').click()
cy.get('input[type=color]').should('have.value', '#958df1')
})
it('should preserve background color on new lines', () => {
cy.get('[data-testid="setPurple"]').click()
cy.get('.ProseMirror').type('Example Text{enter}')
cy.get('[data-testid="setPurple"]').should('have.class', 'is-active')
})
it('should unset background color on new lines after unset clicked', () => {
cy.get('[data-testid="setPurple"]').click()
cy.get('.ProseMirror').type('Example Text{enter}')
cy.get('[data-testid="unsetBackgroundColor"]').click()
cy.get('.ProseMirror').type('Example Text')
cy.get('[data-testid="setPurple"]').should('not.have.class', 'is-active')
})
})

View File

@ -0,0 +1,109 @@
<template>
<div v-if="editor" class="container">
<div class="control-group">
<div class="button-group">
<input
type="color"
@input="editor.chain().focus().setBackgroundColor($event.target.value).run()"
:value="editor.getAttributes('textStyle').backgroundColor"
data-testid="setBackgroundColor"
/>
<button
@click="editor.chain().focus().setBackgroundColor('#958DF1').run()"
:class="{ 'is-active': editor.isActive('textStyle', { backgroundColor: '#958DF1' }) }"
data-testid="setPurple"
>
Purple
</button>
<button
@click="editor.chain().focus().setBackgroundColor('#F98181').run()"
:class="{ 'is-active': editor.isActive('textStyle', { backgroundColor: '#F98181' }) }"
data-testid="setRed"
>
Red
</button>
<button
@click="editor.chain().focus().setBackgroundColor('#FBBC88').run()"
:class="{ 'is-active': editor.isActive('textStyle', { backgroundColor: '#FBBC88' }) }"
data-testid="setOrange"
>
Orange
</button>
<button
@click="editor.chain().focus().setBackgroundColor('#FAF594').run()"
:class="{ 'is-active': editor.isActive('textStyle', { backgroundColor: '#FAF594' }) }"
data-testid="setYellow"
>
Yellow
</button>
<button
@click="editor.chain().focus().setBackgroundColor('#70CFF8').run()"
:class="{ 'is-active': editor.isActive('textStyle', { backgroundColor: '#70CFF8' }) }"
data-testid="setBlue"
>
Blue
</button>
<button
@click="editor.chain().focus().setBackgroundColor('#94FADB').run()"
:class="{ 'is-active': editor.isActive('textStyle', { backgroundColor: '#94FADB' }) }"
data-testid="setTeal"
>
Teal
</button>
<button
@click="editor.chain().focus().setBackgroundColor('#B9F18D').run()"
:class="{ 'is-active': editor.isActive('textStyle', { backgroundColor: '#B9F18D' }) }"
data-testid="setGreen"
>
Green
</button>
<button @click="editor.chain().focus().unsetBackgroundColor().run()" data-testid="unsetBackgroundColor">
Unset color
</button>
</div>
</div>
<editor-content :editor="editor" />
</div>
</template>
<script>
import Document from '@tiptap/extension-document'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
import { BackgroundColor , TextStyle } from '@tiptap/extension-text-style'
import { Editor, EditorContent } from '@tiptap/vue-3'
export default {
components: {
EditorContent,
},
data() {
return {
editor: null,
}
},
mounted() {
this.editor = new Editor({
extensions: [Document, Paragraph, Text, TextStyle, BackgroundColor],
content: `
<p><span style="background-color: #958DF1">Oh, for some reason that's purple.</span></p>
`,
})
},
beforeUnmount() {
this.editor.destroy()
},
}
</script>
<style lang="scss">
/* Basic editor styles */
.tiptap {
:first-child {
margin-top: 0;
}
}
</style>

View File

@ -1,10 +1,9 @@
import './styles.scss'
import { Color } from '@tiptap/extension-color'
import Document from '@tiptap/extension-document'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
import TextStyle from '@tiptap/extension-text-style'
import { Color, TextStyle } from '@tiptap/extension-text-style'
import { EditorContent, useEditor, useEditorState } from '@tiptap/react'
import React from 'react'
@ -42,7 +41,7 @@ export default () => {
<div className="button-group">
<input
type="color"
onInput={event => editor.chain().focus().setColor(event.target.value).run()}
onInput={event => editor.chain().focus().setColor(event.currentTarget.value).run()}
value={editorState.color}
data-testid="setColor"
/>

View File

@ -57,11 +57,10 @@
</template>
<script>
import { Color } from '@tiptap/extension-color'
import Document from '@tiptap/extension-document'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
import TextStyle from '@tiptap/extension-text-style'
import { Color, TextStyle } from '@tiptap/extension-text-style'
import { Editor, EditorContent } from '@tiptap/vue-3'
export default {

View File

@ -1,10 +1,9 @@
import './styles.scss'
import Document from '@tiptap/extension-document'
import FontFamily from '@tiptap/extension-font-family'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
import TextStyle from '@tiptap/extension-text-style'
import { FontFamily , TextStyle } from '@tiptap/extension-text-style'
import { EditorContent, useEditor, useEditorState } from '@tiptap/react'
import React from 'react'

View File

@ -41,10 +41,9 @@
<script>
import Document from '@tiptap/extension-document'
import FontFamily from '@tiptap/extension-font-family'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
import TextStyle from '@tiptap/extension-text-style'
import { FontFamily , TextStyle } from '@tiptap/extension-text-style'
import { Editor, EditorContent } from '@tiptap/vue-3'
export default {

View File

@ -1,5 +1,4 @@
import FontSize from '@tiptap/extension-font-size'
import TextStyle from '@tiptap/extension-text-style'
import { FontSize , TextStyle } from '@tiptap/extension-text-style'
import { EditorContent, useEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'

View File

@ -26,8 +26,7 @@
</template>
<script>
import FontSize from '@tiptap/extension-font-size'
import TextStyle from '@tiptap/extension-text-style'
import { FontSize , TextStyle } from '@tiptap/extension-text-style'
import StarterKit from '@tiptap/starter-kit'
import { Editor, EditorContent } from '@tiptap/vue-3'

View File

@ -0,0 +1,38 @@
context('/src/Extensions/LineHeight/React/', () => {
before(() => {
cy.visit('/src/Extensions/LineHeight/React/')
})
beforeEach(() => {
cy.get('.tiptap').then(([{ editor }]) => {
editor.commands.setContent('<p>Example Text</p>')
})
cy.get('.tiptap').type('{selectall}')
})
it('should set line-height 1.5 for the selected text', () => {
cy.get('[data-test-id="1.5"]').should('not.have.class', 'is-active').click().should('have.class', 'is-active')
cy.get('.tiptap').find('span').should('have.attr', 'style', 'line-height: 1.5')
})
it('should set line-height 2.0 for the selected text', () => {
cy.get('[data-test-id="2.0"]').should('not.have.class', 'is-active').click().should('have.class', 'is-active')
cy.get('.tiptap').find('span').should('have.attr', 'style', 'line-height: 2.0')
})
it('should set line-height 4.0 for the selected text', () => {
cy.get('[data-test-id="4.0"]').should('not.have.class', 'is-active').click().should('have.class', 'is-active')
cy.get('.tiptap').find('span').should('have.attr', 'style', 'line-height: 4.0')
})
it('should remove the line-height of the selected text', () => {
cy.get('[data-test-id="1.5"]').click()
cy.get('.tiptap').find('span').should('have.attr', 'style', 'line-height: 1.5')
cy.get('[data-test-id="unsetLineHeight"]').click()
cy.get('.tiptap span').should('not.exist')
})
})

View File

@ -0,0 +1,66 @@
import './styles.scss'
import { LineHeight, TextStyle } from '@tiptap/extension-text-style'
import { EditorContent, useEditor, useEditorState } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import React from 'react'
export default () => {
const editor = useEditor({
extensions: [StarterKit, TextStyle, LineHeight],
content: `
<p>Adjusting line heights can greatly affect the readability of your text, making it easier for users to engage with your content.</p>
<p>Line height is the vertical distance between lines of text in a paragraph. It's also known as leading, which comes from the days of metal type, when strips of lead were placed between lines of type to add space.</p>
<p>Line height is expressed as a ratio, meaning the default line height is 1.0. A line height of 1.5 would be 1.5 times the height of the font, while a line height of 2.0 would be twice the height of the font.</p>
<p>It's important to choose a line height that's appropriate for your font size and line length. A line height that's too small can make text feel cramped, while a line height that's too large can make text feel disconnected.</p>
<p><span style="line-height: 1.5">This paragraph has a line height of 1.5.</span></p>
<p>This paragraph has the default line height of 1.0.</p>
<p><span style="line-height: 4.0">This paragraph has a line height of 4.0.</span></p>
`,
})
const { isLarge, isSmall, isExtraLarge } = useEditorState({
editor,
selector: ctx => {
return {
isSmall: ctx.editor.isActive('textStyle', { lineHeight: '1.5' }),
isLarge: ctx.editor.isActive('textStyle', { lineHeight: '2.0' }),
isExtraLarge: ctx.editor.isActive('textStyle', { lineHeight: '4.0' }),
}
},
})
return (
<>
<div className="control-group">
<div className="button-group">
<button
onClick={() => editor.chain().focus().toggleTextStyle({ lineHeight: '1.5' }).run()}
className={isSmall ? 'is-active' : ''}
data-test-id="1.5"
>
Line height 1.5
</button>
<button
onClick={() => editor.chain().focus().toggleTextStyle({ lineHeight: '2.0' }).run()}
className={isLarge ? 'is-active' : ''}
data-test-id="2.0"
>
Line height 2.0
</button>
<button
onClick={() => editor.chain().focus().toggleTextStyle({ lineHeight: '4.0' }).run()}
className={isExtraLarge ? 'is-active' : ''}
data-test-id="4.0"
>
Line height 4.0
</button>
<button onClick={() => editor.chain().focus().unsetLineHeight().run()} data-test-id="unsetLineHeight">
Unset line height
</button>
</div>
</div>
<EditorContent editor={editor} />
</>
)
}

View File

@ -0,0 +1,18 @@
/* Basic editor styles */
.tiptap {
line-height: 1.0;
:first-child {
margin-top: 0;
}
img {
display: block;
height: auto;
margin: 1.5rem 0;
max-width: 100%;
&.ProseMirror-selectednode {
outline: 3px solid var(--purple);
}
}
}

View File

@ -0,0 +1,38 @@
context('/src/Extensions/LineHeight/Vue/', () => {
before(() => {
cy.visit('/src/Extensions/LineHeight/Vue/')
})
beforeEach(() => {
cy.get('.tiptap').then(([{ editor }]) => {
editor.commands.setContent('<p>Example Text</p>')
})
cy.get('.tiptap').type('{selectall}')
})
it('should set line-height 1.5 for the selected text', () => {
cy.get('[data-test-id="1.5"]').should('not.have.class', 'is-active').click().should('have.class', 'is-active')
cy.get('.tiptap').find('span').should('have.attr', 'style', 'line-height: 1.5')
})
it('should set line-height 2.0 for the selected text', () => {
cy.get('[data-test-id="2.0"]').should('not.have.class', 'is-active').click().should('have.class', 'is-active')
cy.get('.tiptap').find('span').should('have.attr', 'style', 'line-height: 2.0')
})
it('should set line-height 4.0 for the selected text', () => {
cy.get('[data-test-id="4.0"]').should('not.have.class', 'is-active').click().should('have.class', 'is-active')
cy.get('.tiptap').find('span').should('have.attr', 'style', 'line-height: 4.0')
})
it('should remove the line-height of the selected text', () => {
cy.get('[data-test-id="1.5"]').click()
cy.get('.tiptap').find('span').should('have.attr', 'style', 'line-height: 1.5')
cy.get('[data-test-id="unsetLineHeight"]').click()
cy.get('.tiptap span').should('not.exist')
})
})

View File

@ -0,0 +1,72 @@
<template>
<div v-if="editor" class="container">
<div class="control-group">
<div class="button-group">
<button
@click="editor.chain().focus().toggleTextStyle({ lineHeight: '1.5' }).run()"
:class="{ 'is-active': editor.isActive('textStyle', { lineHeight: '1.5' }) }"
data-test-id="1.5"
>
Line height 1.5
</button>
<button
@click="editor.chain().focus().toggleTextStyle({ lineHeight: '2.0' }).run()"
:class="{ 'is-active': editor.isActive('textStyle', { lineHeight: '2.0' }) }"
data-test-id="2.0"
>
Line height 2.0
</button>
<button
@click="editor.chain().focus().toggleTextStyle({ lineHeight: '4.0' }).run()"
:class="{ 'is-active': editor.isActive('textStyle', { lineHeight: '4.0' }) }"
data-test-id="4.0"
>
Line height 4.0
</button>
<button @click="editor.chain().focus().unsetLineHeight().run()" data-test-id="unsetLineHeight">
Unset line height
</button>
</div>
</div>
<editor-content :editor="editor" />
</div>
</template>
<script>
import './styles.scss'
import { LineHeight, TextStyle } from '@tiptap/extension-text-style'
import StarterKit from '@tiptap/starter-kit'
import { Editor, EditorContent } from '@tiptap/vue-3'
export default {
components: {
EditorContent,
},
data() {
return {
editor: null,
}
},
mounted() {
this.editor = new Editor({
extensions: [StarterKit, TextStyle, LineHeight],
content: `
<p>Adjusting line heights can greatly affect the readability of your text, making it easier for users to engage with your content.</p>
<p>Line height is the vertical distance between lines of text in a paragraph. It's also known as leading, which comes from the days of metal type, when strips of lead were placed between lines of type to add space.</p>
<p>Line height is expressed as a ratio, meaning the default line height is 1.0. A line height of 1.5 would be 1.5 times the height of the font, while a line height of 2.0 would be twice the height of the font.</p>
<p>It's important to choose a line height that's appropriate for your font size and line length. A line height that's too small can make text feel cramped, while a line height that's too large can make text feel disconnected.</p>
<p><span style="line-height: 1.5">This paragraph has a line height of 1.5.</span></p>
<p>This paragraph has the default line height of 1.0.</p>
<p><span style="line-height: 4.0">This paragraph has a line height of 4.0.</span></p>
`,
})
},
beforeUnmount() {
this.editor.destroy()
},
}
</script>

View File

@ -0,0 +1,18 @@
/* Basic editor styles */
.tiptap {
line-height: 1.0;
:first-child {
margin-top: 0;
}
img {
display: block;
height: auto;
margin: 1.5rem 0;
max-width: 100%;
&.ProseMirror-selectednode {
outline: 3px solid var(--purple);
}
}
}

View File

@ -1,12 +1,10 @@
import './styles.scss'
import Bold from '@tiptap/extension-bold'
import { Color } from '@tiptap/extension-color'
import Document from '@tiptap/extension-document'
import { FontFamily } from '@tiptap/extension-font-family'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
import TextStyle from '@tiptap/extension-text-style'
import { Color , FontFamily , TextStyle } from '@tiptap/extension-text-style'
import { EditorContent, useEditor } from '@tiptap/react'
import React from 'react'

View File

@ -6,12 +6,10 @@
<script>
import Bold from '@tiptap/extension-bold'
import { Color } from '@tiptap/extension-color'
import Document from '@tiptap/extension-document'
import { FontFamily } from '@tiptap/extension-font-family'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
import TextStyle from '@tiptap/extension-text-style'
import { Color, FontFamily , TextStyle } from '@tiptap/extension-text-style'
import { Editor, EditorContent } from '@tiptap/vue-3'
export default {

View File

@ -22,6 +22,16 @@ const getPackageDependencies = () => {
replacement: resolve(`../packages/${name}/${subPkgName}/index.ts`),
})
})
} else if (name === 'extension-text-style') {
fg.sync(`../packages/${name}/src/*`, { onlyDirectories: true }).forEach(subName => {
const subPkgName = subName.replace(`../packages/${name}/src/`, '')
paths.push({
find: `@tiptap/${name}/${subPkgName}`,
replacement: resolve(`../packages/${name}/src/${subPkgName}/index.ts`),
})
})
paths.push({ find: `@tiptap/${name}`, replacement: resolve(`../packages/${name}/src/index.ts`) })
} else {
paths.push({ find: `@tiptap/${name}`, replacement: resolve(`../packages/${name}/src/index.ts`) })
}

View File

@ -31,11 +31,9 @@
"dist"
],
"devDependencies": {
"@tiptap/core": "^3.0.0-next.3",
"@tiptap/extension-text-style": "^3.0.0-next.3"
},
"peerDependencies": {
"@tiptap/core": "^3.0.0-next.1",
"@tiptap/extension-text-style": "^3.0.0-next.1"
},
"repository": {

View File

@ -1,5 +1,6 @@
import { Color } from './color.js'
// @ts-ignore
import { Color, ColorOptions } from '@tiptap/extension-text-style/color'
export * from './color.js'
export { Color, ColorOptions }
export default Color

View File

@ -31,12 +31,10 @@
"dist"
],
"devDependencies": {
"@tiptap/core": "^3.0.0-next.3",
"@tiptap/extension-text-style": "^3.0.0-next.3"
},
"peerDependencies": {
"@tiptap/core": "^3.0.0-next.1",
"@tiptap/extension-text-style": "^3.0.0-next.1"
"@tiptap/extension-text-style": "^3.0.0-next.3"
},
"repository": {
"type": "git",

View File

@ -1,5 +1,6 @@
import { FontFamily } from './font-family.js'
// @ts-ignore
import { FontFamily, FontFamilyOptions } from '@tiptap/extension-text-style/font-family'
export * from './font-family.js'
export { FontFamily, FontFamilyOptions }
export default FontFamily

View File

@ -1,3 +0,0 @@
# Change Log
## 2.10.2

View File

@ -1,18 +0,0 @@
# @tiptap/extension-font-size
[![Version](https://img.shields.io/npm/v/@tiptap/extension-font-size.svg?label=version)](https://www.npmjs.com/package/@tiptap/extension-font-size)
[![Downloads](https://img.shields.io/npm/dm/@tiptap/extension-font-size.svg)](https://npmcharts.com/compare/tiptap?minimal=true)
[![License](https://img.shields.io/npm/l/@tiptap/extension-font-size.svg)](https://www.npmjs.com/package/@tiptap/extension-font-size)
[![Sponsor](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub)](https://github.com/sponsors/ueberdosis)
## Introduction
Tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) a toolkit for building rich text WYSIWYG editors, which is already in use at many well-known companies such as _New York Times_, _The Guardian_ or _Atlassian_.
## Official Documentation
Documentation can be found on the [Tiptap website](https://tiptap.dev).
## License
Tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap/blob/main/LICENSE.md).

View File

@ -1,50 +0,0 @@
{
"name": "@tiptap/extension-font-size",
"description": "font size extension for tiptap",
"version": "3.0.0-next.3",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
"tiptap extension"
],
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"type": "module",
"exports": {
".": {
"types": {
"import": "./dist/index.d.ts",
"require": "./dist/index.d.cts"
},
"import": "./dist/index.js",
"require": "./dist/index.cjs"
}
},
"main": "dist/index.cjs",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"src",
"dist"
],
"devDependencies": {
"@tiptap/core": "^3.0.0-next.3",
"@tiptap/extension-text-style": "^3.0.0-next.3"
},
"peerDependencies": {
"@tiptap/core": "^3.0.0-next.3",
"@tiptap/extension-text-style": "^3.0.0-next.3"
},
"repository": {
"type": "git",
"url": "https://github.com/ueberdosis/tiptap",
"directory": "packages/extension-font-size"
},
"scripts": {
"build": "tsup",
"lint": "prettier ./src/ --check && eslint --cache --quiet --no-error-on-unmatched-pattern ./src/"
}
}

View File

@ -1,11 +0,0 @@
import { defineConfig } from 'tsup'
export default defineConfig({
entry: ['src/index.ts'],
tsconfig: '../../tsconfig.build.json',
outDir: 'dist',
dts: true,
clean: true,
sourcemap: true,
format: ['esm', 'cjs'],
})

View File

@ -21,6 +21,62 @@
},
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"./background-color": {
"types": {
"import": "./dist/background-color/index.d.ts",
"require": "./dist/background-color/index.d.cts"
},
"import": "./dist/background-color/index.js",
"require": "./dist/background-color/index.cjs"
},
"./color": {
"types": {
"import": "./dist/color/index.d.ts",
"require": "./dist/color/index.d.cts"
},
"import": "./dist/color/index.js",
"require": "./dist/color/index.cjs"
},
"./font-family": {
"types": {
"import": "./dist/font-family/index.d.ts",
"require": "./dist/font-family/index.d.cts"
},
"import": "./dist/font-family/index.js",
"require": "./dist/font-family/index.cjs"
},
"./font-size": {
"types": {
"import": "./dist/font-size/index.d.ts",
"require": "./dist/font-size/index.d.cts"
},
"import": "./dist/font-size/index.js",
"require": "./dist/font-size/index.cjs"
},
"./line-height": {
"types": {
"import": "./dist/line-height/index.d.ts",
"require": "./dist/line-height/index.d.cts"
},
"import": "./dist/line-height/index.js",
"require": "./dist/line-height/index.cjs"
},
"./text-style": {
"types": {
"import": "./dist/text-style/index.d.ts",
"require": "./dist/text-style/index.d.cts"
},
"import": "./dist/text-style/index.js",
"require": "./dist/text-style/index.cjs"
},
"./text-style-kit": {
"types": {
"import": "./dist/text-style-kit/index.d.ts",
"require": "./dist/text-style-kit/index.d.cts"
},
"import": "./dist/text-style-kit/index.js",
"require": "./dist/text-style-kit/index.cjs"
}
},
"main": "dist/index.cjs",

View File

@ -0,0 +1,90 @@
import '../text-style/index.js'
import { Extension } from '@tiptap/core'
export type BackgroundColorOptions = {
/**
* The types where the color can be applied
* @default ['textStyle']
* @example ['heading', 'paragraph']
*/
types: string[]
}
declare module '@tiptap/core' {
interface Commands<ReturnType> {
backgroundColor: {
/**
* Set the text color
* @param backgroundColor The color to set
* @example editor.commands.setColor('red')
*/
setBackgroundColor: (backgroundColor: string) => ReturnType
/**
* Unset the text backgroundColor
* @example editor.commands.unsetBackgroundColor()
*/
unsetBackgroundColor: () => ReturnType
}
}
}
// @ts-ignore because the module is not found during dts build
declare module '@tiptap/extension-text-style' {
interface TextStyleAttributes {
backgroundColor?: string | null
}
}
/**
* This extension allows you to color your text.
* @see https://tiptap.dev/api/extensions/background-color
*/
export const BackgroundColor = Extension.create<BackgroundColorOptions>({
name: 'backgroundColor',
addOptions() {
return {
types: ['textStyle'],
}
},
addGlobalAttributes() {
return [
{
types: this.options.types,
attributes: {
backgroundColor: {
default: null,
parseHTML: element => element.style.backgroundColor?.replace(/['"]+/g, ''),
renderHTML: attributes => {
if (!attributes.backgroundColor) {
return {}
}
return {
style: `background-color: ${attributes.backgroundColor}`,
}
},
},
},
},
]
},
addCommands() {
return {
setBackgroundColor:
backgroundColor =>
({ chain }) => {
return chain().setMark('textStyle', { backgroundColor }).run()
},
unsetBackgroundColor:
() =>
({ chain }) => {
return chain().setMark('textStyle', { backgroundColor: null }).removeEmptyTextStyle().run()
},
}
},
})

View File

@ -0,0 +1,5 @@
import { BackgroundColor } from './background-color.js'
export * from './background-color.js'
export default BackgroundColor

View File

@ -1,4 +1,4 @@
import '@tiptap/extension-text-style'
import '../text-style/index.js'
import { Extension } from '@tiptap/core'
@ -30,6 +30,13 @@ declare module '@tiptap/core' {
}
}
// @ts-ignore because the module is not found during dts build
declare module '@tiptap/extension-text-style' {
interface TextStyleAttributes {
color?: string | null
}
}
/**
* This extension allows you to color your text.
* @see https://tiptap.dev/api/extensions/color

View File

@ -0,0 +1,5 @@
import { Color } from './color.js'
export * from './color.js'
export default Color

View File

@ -1,4 +1,4 @@
import '@tiptap/extension-text-style'
import '../text-style/index.js'
import { Extension } from '@tiptap/core'
@ -29,6 +29,13 @@ declare module '@tiptap/core' {
}
}
// @ts-ignore because the module is not found during dts build
declare module '@tiptap/extension-text-style' {
interface TextStyleAttributes {
fontFamily?: string | null
}
}
/**
* This extension allows you to set a font family for text.
* @see https://www.tiptap.dev/api/extensions/font-family

View File

@ -0,0 +1,5 @@
import { FontFamily } from './font-family.js'
export * from './font-family.js'
export default FontFamily

View File

@ -1,4 +1,4 @@
import '@tiptap/extension-text-style'
import '../text-style/index.js'
import { Extension } from '@tiptap/core'
@ -29,6 +29,13 @@ declare module '@tiptap/core' {
}
}
// @ts-ignore because the module is not found during dts build
declare module '@tiptap/extension-text-style' {
interface TextStyleAttributes {
fontSize?: string | null
}
}
/**
* This extension allows you to set a font size for text.
* @see https://www.tiptap.dev/api/extensions/font-size

View File

@ -1,5 +1,14 @@
import { TextStyle } from './text-style.js'
/* eslint-disable @typescript-eslint/no-empty-object-type */
export * from './text-style.js'
export * from './background-color/index.js'
export * from './color/index.js'
export * from './font-family/index.js'
export * from './font-size/index.js'
export * from './line-height/index.js'
export * from './text-style/index.js'
export * from './text-style-kit/index.js'
export default TextStyle
/**
* The available text style attributes.
*/
export interface TextStyleAttributes extends Record<string, any> {}

View File

@ -0,0 +1,5 @@
import { LineHeight } from './line-height.js'
export * from './line-height.js'
export default LineHeight

View File

@ -0,0 +1,89 @@
import '../text-style/index.js'
import { Extension } from '@tiptap/core'
export type LineHeightOptions = {
/**
* A list of node names where the line height can be applied.
* @default ['textStyle']
* @example ['heading', 'paragraph']
*/
types: string[]
}
declare module '@tiptap/core' {
interface Commands<ReturnType> {
lineHeight: {
/**
* Set the line height
* @param lineHeight The line height
* @example editor.commands.setLineHeight('1.5')
*/
setLineHeight: (lineHeight: string) => ReturnType
/**
* Unset the line height
* @example editor.commands.unsetLineHeight()
*/
unsetLineHeight: () => ReturnType
}
}
}
// @ts-ignore because the module is not found during dts build
declare module '@tiptap/extension-text-style' {
interface TextStyleAttributes {
lineHeight?: string | null
}
}
/**
* This extension allows you to set the line-height for text.
* @see https://www.tiptap.dev/api/extensions/line-height
*/
export const LineHeight = Extension.create<LineHeightOptions>({
name: 'lineHeight',
addOptions() {
return {
types: ['textStyle'],
}
},
addGlobalAttributes() {
return [
{
types: this.options.types,
attributes: {
lineHeight: {
default: null,
parseHTML: element => element.style.lineHeight,
renderHTML: attributes => {
if (!attributes.lineHeight) {
return {}
}
return {
style: `line-height: ${attributes.lineHeight}`,
}
},
},
},
},
]
},
addCommands() {
return {
setLineHeight:
lineHeight =>
({ chain }) => {
return chain().setMark('textStyle', { lineHeight }).run()
},
unsetLineHeight:
() =>
({ chain }) => {
return chain().setMark('textStyle', { lineHeight: null }).removeEmptyTextStyle().run()
},
}
},
})

View File

@ -0,0 +1,80 @@
import { Extension } from '@tiptap/core'
import { BackgroundColor, BackgroundColorOptions } from '../background-color/index.js'
import { Color, ColorOptions } from '../color/index.js'
import { FontFamily, FontFamilyOptions } from '../font-family/index.js'
import { FontSize, FontSizeOptions } from '../font-size/index.js'
import { LineHeight, LineHeightOptions } from '../line-height/index.js'
import { TextStyle, TextStyleOptions } from '../text-style/index.js'
export interface TextStyleKitOptions {
/**
* If set to false, the background color extension will not be registered
* @example backgroundColor: false
*/
backgroundColor: Partial<BackgroundColorOptions> | false
/**
* If set to false, the color extension will not be registered
* @example color: false
*/
color: Partial<ColorOptions> | false
/**
* If set to false, the font family extension will not be registered
* @example fontFamily: false
*/
fontFamily: Partial<FontFamilyOptions> | false
/**
* If set to false, the font size extension will not be registered
* @example fontSize: false
*/
fontSize: Partial<FontSizeOptions> | false
/**
* If set to false, the line height extension will not be registered
* @example lineHeight: false
*/
lineHeight: Partial<LineHeightOptions> | false
/**
* If set to false, the text style extension will not be registered (required for other text style extensions)
* @example textStyle: false
*/
textStyle: Partial<TextStyleOptions> | false
}
/**
* The table kit is a collection of table editor extensions.
*
* Its a good starting point for building your own table in Tiptap.
*/
export const TextStyleKit = Extension.create<TextStyleKitOptions>({
name: 'textStyleKit',
addExtensions() {
const extensions = []
if (this.options.backgroundColor !== false) {
extensions.push(BackgroundColor.configure(this.options.backgroundColor))
}
if (this.options.color !== false) {
extensions.push(Color.configure(this.options.color))
}
if (this.options.fontFamily !== false) {
extensions.push(FontFamily.configure(this.options.fontFamily))
}
if (this.options.fontSize !== false) {
extensions.push(FontSize.configure(this.options.fontSize))
}
if (this.options.lineHeight !== false) {
extensions.push(LineHeight.configure(this.options.lineHeight))
}
if (this.options.textStyle !== false) {
extensions.push(TextStyle.configure(this.options.textStyle))
}
return extensions
},
})

View File

@ -1,5 +1,7 @@
import { Mark, mergeAttributes } from '@tiptap/core'
import type { TextStyleAttributes } from '../index.js'
export interface TextStyleOptions {
/**
* HTML attributes to add to the span element.
@ -25,6 +27,12 @@ declare module '@tiptap/core' {
* @example editor.commands.removeEmptyTextStyle()
*/
removeEmptyTextStyle: () => ReturnType
/**
* Toggle a text style
* @param attributes The text style attributes
* @example editor.commands.toggleTextStyle({ fontWeight: 'bold' })
*/
toggleTextStyle: (attributes?: TextStyleAttributes) => ReturnType
}
}
}
@ -49,7 +57,7 @@ const mergeNestedSpanStyles = (element: HTMLElement) => {
/**
* This extension allows you to create text styles. It is required by default
* for the `textColor` and `backgroundColor` extensions.
* for the `text-color` and `font-family` extensions.
* @see https://www.tiptap.dev/api/marks/text-style
*/
export const TextStyle = Mark.create<TextStyleOptions>({
@ -92,6 +100,11 @@ export const TextStyle = Mark.create<TextStyleOptions>({
addCommands() {
return {
toggleTextStyle:
attributes =>
({ commands }) => {
return commands.toggleMark(this.name, attributes)
},
removeEmptyTextStyle:
() =>
({ tr }) => {

View File

@ -1,11 +1,22 @@
import { defineConfig } from 'tsup'
export default defineConfig({
entry: ['src/index.ts'],
tsconfig: '../../tsconfig.build.json',
outDir: 'dist',
dts: true,
clean: true,
sourcemap: true,
format: ['esm', 'cjs'],
})
export default defineConfig(
[
'src/background-color/index.ts',
'src/color/index.ts',
'src/font-family/index.ts',
'src/font-size/index.ts',
'src/line-height/index.ts',
'src/text-style/index.ts',
'src/text-style-kit/index.ts',
'src/index.ts',
].map(entry => ({
entry: [entry],
tsconfig: '../../tsconfig.build.json',
outDir: `dist${entry.replace('src', '').split('/').slice(0, -1).join('/')}`,
dts: true,
sourcemap: true,
format: ['esm', 'cjs'],
external: [/^[^./]/],
})),
)

View File

@ -345,9 +345,6 @@ importers:
packages/extension-color:
devDependencies:
'@tiptap/core':
specifier: ^3.0.0-next.3
version: link:../core
'@tiptap/extension-text-style':
specifier: ^3.0.0-next.3
version: link:../extension-text-style
@ -384,18 +381,6 @@ importers:
packages/extension-font-family:
devDependencies:
'@tiptap/core':
specifier: ^3.0.0-next.3
version: link:../core
'@tiptap/extension-text-style':
specifier: ^3.0.0-next.3
version: link:../extension-text-style
packages/extension-font-size:
devDependencies:
'@tiptap/core':
specifier: ^3.0.0-next.3
version: link:../core
'@tiptap/extension-text-style':
specifier: ^3.0.0-next.3
version: link:../extension-text-style
@ -454,6 +439,15 @@ importers:
specifier: ^3.0.0-next.3
version: link:../core
packages/extension-line-height:
devDependencies:
'@tiptap/core':
specifier: ^3.0.0-next.3
version: link:../core
'@tiptap/extension-text-style':
specifier: ^3.0.0-next.3
version: link:../extension-text-style
packages/extension-link:
dependencies:
linkifyjs:

View File

@ -1,12 +1,10 @@
/// <reference types="cypress" />
import { Editor } from '@tiptap/core'
import Color from '@tiptap/extension-color'
import Document from '@tiptap/extension-document'
import FontFamily from '@tiptap/extension-font-family'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
import TextStyle from '@tiptap/extension-text-style'
import { Color, FontFamily , TextStyle } from '@tiptap/extension-text-style'
describe('isActive', () => {
it('should check the current node', () => {