From 8a3b47a529d28b28b50d634c6ff69b8e5aad3080 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Wed, 8 Sep 2021 23:53:44 +0200 Subject: [PATCH] feat: parseHTML for attributes should return the value instead of an object now, fix #1863 --- demos/src/Examples/Tables/React/index.jsx | 8 ++------ demos/src/Examples/Tables/Vue/index.vue | 6 +----- demos/src/Experiments/Embeds/Vue/iframe.ts | 6 +----- demos/src/Experiments/Figure/Vue/figure.ts | 18 +++-------------- docs/src/docPages/guide/custom-extensions.md | 12 +++-------- .../injectExtensionAttributesToParseRule.ts | 20 +++++++++++-------- packages/core/src/types.ts | 2 +- .../extension-code-block/src/code-block.ts | 4 +--- packages/extension-color/src/color.ts | 6 +----- .../extension-font-family/src/font-family.ts | 4 +--- packages/extension-highlight/src/highlight.ts | 6 +----- packages/extension-mention/src/mention.ts | 12 ++--------- .../src/ordered-list.ts | 8 ++++---- .../extension-table-cell/src/table-cell.ts | 4 +--- .../src/table-header.ts | 4 +--- packages/extension-task-item/src/task-item.ts | 6 ++---- .../extension-text-align/src/text-align.ts | 4 +--- 17 files changed, 38 insertions(+), 92 deletions(-) diff --git a/demos/src/Examples/Tables/React/index.jsx b/demos/src/Examples/Tables/React/index.jsx index 179c71cc18..0c45062681 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 = ` - + diff --git a/demos/src/Examples/Tables/Vue/index.vue b/demos/src/Examples/Tables/Vue/index.vue index 8b994cc6d4..39cfc1b58a 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 a57ac90407..6c3d4e575d 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 74c29e60c2..fb2c6a40c4 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 035a4b8b80..77f70dacfc 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 1896a52192..620f121d2a 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 7656c215bf..b3fcba2023 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 e4f3c7541c..e590043e45 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 0a86ef3d67..a3364693ed 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 1dff324ad1..d1cc1cb428 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 2f41c2d0ac..47c464f52f 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 61608d2385..69f925d778 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 04f9209b57..d3205eb37b 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 ff2b4b7216..d39b48b7ff 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 1d51b72c55..6c8b3c4896 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 3183e04361..c20668c4ca 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 576a2a3fdf..1816d781e3 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 {}
FirstnameLastnameLastname Age