diff --git a/demos/src/Examples/Tables/React/index.jsx b/demos/src/Examples/Tables/React/index.jsx
index 179c71cc..0c450626 100644
--- a/demos/src/Examples/Tables/React/index.jsx
+++ b/demos/src/Examples/Tables/React/index.jsx
@@ -16,11 +16,7 @@ const CustomTableCell = TableCell.extend({
// and add a new one …
backgroundColor: {
default: null,
- parseHTML: element => {
- return {
- backgroundColor: element.getAttribute('data-background-color'),
- }
- },
+ parseHTML: element => element.getAttribute('data-background-color'),
renderHTML: attributes => {
return {
'data-background-color': attributes.backgroundColor,
@@ -36,7 +32,7 @@ export const tableHTML = `
| Firstname |
- Lastname |
+ Lastname |
Age |
diff --git a/demos/src/Examples/Tables/Vue/index.vue b/demos/src/Examples/Tables/Vue/index.vue
index 8b994cc6..39cfc1b5 100644
--- a/demos/src/Examples/Tables/Vue/index.vue
+++ b/demos/src/Examples/Tables/Vue/index.vue
@@ -76,11 +76,7 @@ const CustomTableCell = TableCell.extend({
// and add a new one …
backgroundColor: {
default: null,
- parseHTML: element => {
- return {
- backgroundColor: element.getAttribute('data-background-color'),
- }
- },
+ parseHTML: element => element.getAttribute('data-background-color'),
renderHTML: attributes => {
return {
'data-background-color': attributes.backgroundColor,
diff --git a/demos/src/Experiments/Embeds/Vue/iframe.ts b/demos/src/Experiments/Embeds/Vue/iframe.ts
index a57ac904..6c3d4e57 100644
--- a/demos/src/Experiments/Embeds/Vue/iframe.ts
+++ b/demos/src/Experiments/Embeds/Vue/iframe.ts
@@ -42,11 +42,7 @@ export default Node.create({
},
allowfullscreen: {
default: this.options.allowFullscreen,
- parseHTML: () => {
- return {
- allowfullscreen: this.options.allowFullscreen,
- }
- },
+ parseHTML: () => this.options.allowFullscreen,
},
}
},
diff --git a/demos/src/Experiments/Figure/Vue/figure.ts b/demos/src/Experiments/Figure/Vue/figure.ts
index 74c29e60..fb2c6a40 100644
--- a/demos/src/Experiments/Figure/Vue/figure.ts
+++ b/demos/src/Experiments/Figure/Vue/figure.ts
@@ -57,29 +57,17 @@ export const Figure = Node.create({
return {
src: {
default: null,
- parseHTML: element => {
- return {
- src: element.querySelector('img')?.getAttribute('src'),
- }
- },
+ parseHTML: element => element.querySelector('img')?.getAttribute('src'),
},
alt: {
default: null,
- parseHTML: element => {
- return {
- alt: element.querySelector('img')?.getAttribute('alt'),
- }
- },
+ parseHTML: element => element.querySelector('img')?.getAttribute('alt'),
},
title: {
default: null,
- parseHTML: element => {
- return {
- title: element.querySelector('img')?.getAttribute('title'),
- }
- },
+ parseHTML: element => element.querySelector('img')?.getAttribute('title'),
},
}
},
diff --git a/docs/src/docPages/guide/custom-extensions.md b/docs/src/docPages/guide/custom-extensions.md
index 035a4b8b..77f70dac 100644
--- a/docs/src/docPages/guide/custom-extensions.md
+++ b/docs/src/docPages/guide/custom-extensions.md
@@ -163,11 +163,7 @@ const CustomParagraph = Paragraph.extend({
color: {
default: null,
// Customize the HTML parsing (for example, to load the initial content)
- parseHTML: element => {
- return {
- color: element.getAttribute('data-color'),
- }
- },
+ parseHTML: element => element.getAttribute('data-color'),
// … and customize the HTML rendering.
renderHTML: attributes => {
return {
@@ -187,7 +183,7 @@ const CustomParagraph = Paragraph.extend({
You can completly disable the rendering of attributes with `rendered: false`.
#### Extend existing attributes
-If you want to add an attribute to an extension and keep existing attributes, you can access them through `this.parent()`.
+If you want to add an attribute to an extension and keep existing attributes, you can access them through `this.parent()`.
In some cases, it is undefined, so make sure to check for that case, or use optional chaining `this.parent?.()`
@@ -228,9 +224,7 @@ const TextAlign = Extension.create({
renderHTML: attributes => ({
style: `text-align: ${attributes.textAlign}`,
}),
- parseHTML: element => ({
- textAlign: element.style.textAlign || 'left',
- }),
+ parseHTML: element => element.style.textAlign || 'left',
},
},
},
diff --git a/packages/core/src/helpers/injectExtensionAttributesToParseRule.ts b/packages/core/src/helpers/injectExtensionAttributesToParseRule.ts
index 1896a521..620f121d 100644
--- a/packages/core/src/helpers/injectExtensionAttributesToParseRule.ts
+++ b/packages/core/src/helpers/injectExtensionAttributesToParseRule.ts
@@ -1,6 +1,7 @@
import { ParseRule } from 'prosemirror-model'
import { ExtensionAttribute } from '../types'
import fromString from '../utilities/fromString'
+import isObject from '../utilities/isObject'
/**
* This function merges extension attributes into parserule attributes (`attrs` or `getAttrs`).
@@ -27,18 +28,21 @@ export default function injectExtensionAttributesToParseRule(parseRule: ParseRul
const newAttributes = extensionAttributes
.filter(item => item.attribute.rendered)
.reduce((items, item) => {
- const attributes = item.attribute.parseHTML
- ? item.attribute.parseHTML(node as HTMLElement) || {}
- : {
- [item.name]: fromString((node as HTMLElement).getAttribute(item.name)),
- }
+ const value = item.attribute.parseHTML
+ ? item.attribute.parseHTML(node as HTMLElement)
+ : fromString((node as HTMLElement).getAttribute(item.name))
- const filteredAttributes = Object.fromEntries(Object.entries(attributes)
- .filter(([, value]) => value !== undefined && value !== null))
+ if (isObject(value)) {
+ console.warn(`[tiptap warn]: BREAKING CHANGE: "parseHTML" for your attribute "${item.name}" returns an object but should return the value itself. If this is expected you can ignore this message. This warning will be removed in one of the next releases. Further information: https://github.com/ueberdosis/tiptap/issues/1863`)
+ }
+
+ if (value === null || value === undefined) {
+ return items
+ }
return {
...items,
- ...filteredAttributes,
+ [item.name]: value,
}
}, {})
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index 7656c215..b3fcba20 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -98,7 +98,7 @@ export type Attribute = {
default: any,
rendered?: boolean,
renderHTML?: ((attributes: Record) => Record | null) | null,
- parseHTML?: ((element: HTMLElement) => Record | null) | null,
+ parseHTML?: ((element: HTMLElement) => any | null) | null,
keepOnSplit: boolean,
}
diff --git a/packages/extension-code-block/src/code-block.ts b/packages/extension-code-block/src/code-block.ts
index e4f3c754..e590043e 100644
--- a/packages/extension-code-block/src/code-block.ts
+++ b/packages/extension-code-block/src/code-block.ts
@@ -58,9 +58,7 @@ export const CodeBlock = Node.create({
return null
}
- return {
- language,
- }
+ return language
},
renderHTML: attributes => {
if (!attributes.language) {
diff --git a/packages/extension-color/src/color.ts b/packages/extension-color/src/color.ts
index 0a86ef3d..a3364693 100644
--- a/packages/extension-color/src/color.ts
+++ b/packages/extension-color/src/color.ts
@@ -34,6 +34,7 @@ export const Color = Extension.create({
attributes: {
color: {
default: null,
+ parseHTML: element => element.style.color.replace(/['"]+/g, ''),
renderHTML: attributes => {
if (!attributes.color) {
return {}
@@ -43,11 +44,6 @@ export const Color = Extension.create({
style: `color: ${attributes.color}`,
}
},
- parseHTML: element => {
- return {
- color: element.style.color.replace(/['"]+/g, ''),
- }
- },
},
},
},
diff --git a/packages/extension-font-family/src/font-family.ts b/packages/extension-font-family/src/font-family.ts
index 1dff324a..d1cc1cb4 100644
--- a/packages/extension-font-family/src/font-family.ts
+++ b/packages/extension-font-family/src/font-family.ts
@@ -34,6 +34,7 @@ export const FontFamily = Extension.create({
attributes: {
fontFamily: {
default: null,
+ parseHTML: element => element.style.fontFamily.replace(/['"]+/g, ''),
renderHTML: attributes => {
if (!attributes.fontFamily) {
return {}
@@ -43,9 +44,6 @@ export const FontFamily = Extension.create({
style: `font-family: ${attributes.fontFamily}`,
}
},
- parseHTML: element => ({
- fontFamily: element.style.fontFamily.replace(/['"]+/g, ''),
- }),
},
},
},
diff --git a/packages/extension-highlight/src/highlight.ts b/packages/extension-highlight/src/highlight.ts
index 2f41c2d0..47c464f5 100644
--- a/packages/extension-highlight/src/highlight.ts
+++ b/packages/extension-highlight/src/highlight.ts
@@ -48,11 +48,7 @@ export const Highlight = Mark.create({
return {
color: {
default: null,
- parseHTML: element => {
- return {
- color: element.getAttribute('data-color') || element.style.backgroundColor,
- }
- },
+ parseHTML: element => element.getAttribute('data-color') || element.style.backgroundColor,
renderHTML: attributes => {
if (!attributes.color) {
return {}
diff --git a/packages/extension-mention/src/mention.ts b/packages/extension-mention/src/mention.ts
index 61608d23..69f925d7 100644
--- a/packages/extension-mention/src/mention.ts
+++ b/packages/extension-mention/src/mention.ts
@@ -68,11 +68,7 @@ export const Mention = Node.create({
return {
id: {
default: null,
- parseHTML: element => {
- return {
- id: element.getAttribute('data-id'),
- }
- },
+ parseHTML: element => element.getAttribute('data-id'),
renderHTML: attributes => {
if (!attributes.id) {
return {}
@@ -86,11 +82,7 @@ export const Mention = Node.create({
label: {
default: null,
- parseHTML: element => {
- return {
- label: element.getAttribute('data-label'),
- }
- },
+ parseHTML: element => element.getAttribute('data-label'),
renderHTML: attributes => {
if (!attributes.label) {
return {}
diff --git a/packages/extension-ordered-list/src/ordered-list.ts b/packages/extension-ordered-list/src/ordered-list.ts
index 04f9209b..d3205eb3 100644
--- a/packages/extension-ordered-list/src/ordered-list.ts
+++ b/packages/extension-ordered-list/src/ordered-list.ts
@@ -33,11 +33,11 @@ export const OrderedList = Node.create({
return {
start: {
default: 1,
- parseHTML: element => ({
- start: element.hasAttribute('start')
+ parseHTML: element => {
+ return element.hasAttribute('start')
? parseInt(element.getAttribute('start') || '', 10)
- : 1,
- }),
+ : 1
+ },
},
}
},
diff --git a/packages/extension-table-cell/src/table-cell.ts b/packages/extension-table-cell/src/table-cell.ts
index ff2b4b72..d39b48b7 100644
--- a/packages/extension-table-cell/src/table-cell.ts
+++ b/packages/extension-table-cell/src/table-cell.ts
@@ -29,9 +29,7 @@ export const TableCell = Node.create({
? [parseInt(colwidth, 10)]
: null
- return {
- colwidth: value,
- }
+ return value
},
},
}
diff --git a/packages/extension-table-header/src/table-header.ts b/packages/extension-table-header/src/table-header.ts
index 1d51b72c..6c8b3c48 100644
--- a/packages/extension-table-header/src/table-header.ts
+++ b/packages/extension-table-header/src/table-header.ts
@@ -28,9 +28,7 @@ export const TableHeader = Node.create({
? [parseInt(colwidth, 10)]
: null
- return {
- colwidth: value,
- }
+ return value
},
},
}
diff --git a/packages/extension-task-item/src/task-item.ts b/packages/extension-task-item/src/task-item.ts
index 3183e043..c20668c4 100644
--- a/packages/extension-task-item/src/task-item.ts
+++ b/packages/extension-task-item/src/task-item.ts
@@ -26,13 +26,11 @@ export const TaskItem = Node.create({
return {
checked: {
default: false,
- parseHTML: element => ({
- checked: element.getAttribute('data-checked') === 'true',
- }),
+ keepOnSplit: false,
+ parseHTML: element => element.getAttribute('data-checked') === 'true',
renderHTML: attributes => ({
'data-checked': attributes.checked,
}),
- keepOnSplit: false,
},
}
},
diff --git a/packages/extension-text-align/src/text-align.ts b/packages/extension-text-align/src/text-align.ts
index 576a2a3f..1816d781 100644
--- a/packages/extension-text-align/src/text-align.ts
+++ b/packages/extension-text-align/src/text-align.ts
@@ -37,9 +37,7 @@ export const TextAlign = Extension.create({
attributes: {
textAlign: {
default: this.options.defaultAlignment,
- parseHTML: element => ({
- textAlign: element.style.textAlign || this.options.defaultAlignment,
- }),
+ parseHTML: element => element.style.textAlign || this.options.defaultAlignment,
renderHTML: attributes => {
if (attributes.textAlign === this.options.defaultAlignment) {
return {}