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 179c71cc1..0c4506268 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 8b994cc6d..39cfc1b58 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 a57ac9040..6c3d4e575 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 74c29e60c..fb2c6a40c 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 035a4b8b8..77f70dacf 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 1896a5219..620f121d2 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 7656c215b..b3fcba202 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 e4f3c7541..e590043e4 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 0a86ef3d6..a3364693e 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 1dff324ad..d1cc1cb42 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 2f41c2d0a..47c464f52 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 61608d238..69f925d77 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 04f9209b5..d3205eb37 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 ff2b4b721..d39b48b7f 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 1d51b72c5..6c8b3c489 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 3183e0436..c20668c4c 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 576a2a3fd..1816d781e 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