From 9afadeb7fe368f95064f84424d6a3dd6cd85b43d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20K=C3=BChn?= Date: Tue, 26 Oct 2021 18:31:13 +0200 Subject: [PATCH] feat!: Replace `defaultOptions` with `addOptions` (#2088) * add new addOptions option * replace defaultOptions with addOptions for all extensions * replace defaultOptions with addOptions for all demos * replace defaultOptions with addOptions in docs * refactoring * refactoring * drop object support for addOptions * fix optional options * fix tests --- .../Vue/extension/collaboration-annotation.ts | 22 ++- .../src/Experiments/Commands/Vue/commands.js | 17 +- .../Details/Vue/details-summary.ts | 6 +- demos/src/Experiments/Details/Vue/details.ts | 6 +- demos/src/Experiments/Embeds/Vue/iframe.ts | 14 +- demos/src/Experiments/Figure/Vue/figure.ts | 6 +- .../GenericFigure/Vue/figcaption.ts | 6 +- .../Experiments/GenericFigure/Vue/figure.ts | 6 +- .../Linter/Vue/extension/Linter.ts | 8 +- .../TrailingNode/Vue/trailing-node.ts | 12 +- docs/guide/custom-extensions.md | 8 +- docs/guide/typescript.md | 6 +- docs/overview/upgrade-guide.md | 2 +- packages/core/src/Extension.ts | 44 ++++- packages/core/src/Mark.ts | 46 ++++- packages/core/src/Node.ts | 44 ++++- .../extension-blockquote/src/blockquote.ts | 6 +- packages/extension-bold/src/bold.ts | 6 +- .../extension-bubble-menu/src/bubble-menu.ts | 12 +- .../extension-bullet-list/src/bullet-list.ts | 6 +- .../src/character-count.ts | 6 +- .../src/code-block-lowlight.ts | 8 +- .../extension-code-block/src/code-block.ts | 8 +- packages/extension-code/src/code.ts | 6 +- .../src/collaboration-cursor.ts | 38 ++-- .../src/collaboration.ts | 10 +- packages/extension-color/src/color.ts | 6 +- .../extension-dropcursor/src/dropcursor.ts | 10 +- .../src/floating-menu.ts | 12 +- packages/extension-focus/src/focus.ts | 8 +- .../extension-font-family/src/font-family.ts | 6 +- .../extension-hard-break/src/hard-break.ts | 8 +- packages/extension-heading/src/heading.ts | 8 +- packages/extension-highlight/src/highlight.ts | 8 +- packages/extension-history/src/history.ts | 8 +- .../src/horizontal-rule.ts | 6 +- packages/extension-image/src/image.ts | 8 +- packages/extension-italic/src/italic.ts | 6 +- packages/extension-link/src/link.ts | 16 +- packages/extension-list-item/src/list-item.ts | 6 +- packages/extension-mention/src/mention.ts | 78 ++++---- .../src/ordered-list.ts | 6 +- packages/extension-paragraph/src/paragraph.ts | 6 +- .../extension-placeholder/src/placeholder.ts | 16 +- packages/extension-strike/src/strike.ts | 6 +- packages/extension-subscript/src/subscript.ts | 6 +- .../extension-superscript/src/superscript.ts | 6 +- .../extension-table-cell/src/table-cell.ts | 6 +- .../src/table-header.ts | 6 +- packages/extension-table-row/src/table-row.ts | 6 +- packages/extension-table/src/table.ts | 22 ++- packages/extension-task-item/src/task-item.ts | 8 +- packages/extension-task-list/src/task-list.ts | 6 +- .../extension-text-align/src/text-align.ts | 10 +- .../extension-text-style/src/text-style.ts | 6 +- packages/extension-underline/src/underline.ts | 6 +- .../integration/core/extensionOptions.spec.ts | 180 +++++++++++++++++- 57 files changed, 622 insertions(+), 233 deletions(-) diff --git a/demos/src/Experiments/CollaborationAnnotation/Vue/extension/collaboration-annotation.ts b/demos/src/Experiments/CollaborationAnnotation/Vue/extension/collaboration-annotation.ts index 9c5c7a20..267a75c0 100644 --- a/demos/src/Experiments/CollaborationAnnotation/Vue/extension/collaboration-annotation.ts +++ b/demos/src/Experiments/CollaborationAnnotation/Vue/extension/collaboration-annotation.ts @@ -59,20 +59,22 @@ declare module '@tiptap/core' { } } -export const CollaborationAnnotation = Extension.create({ +export const CollaborationAnnotation = Extension.create({ name: 'annotation', priority: 1000, - defaultOptions: { - HTMLAttributes: { - class: 'annotation', - }, - onUpdate: decorations => decorations, - document: null, - field: 'annotations', - map: null, - instance: '', + addOptions() { + return { + HTMLAttributes: { + class: 'annotation', + }, + onUpdate: decorations => decorations, + document: null, + field: 'annotations', + map: null, + instance: '', + } }, onCreate() { diff --git a/demos/src/Experiments/Commands/Vue/commands.js b/demos/src/Experiments/Commands/Vue/commands.js index 53aac19e..ea3f57f6 100644 --- a/demos/src/Experiments/Commands/Vue/commands.js +++ b/demos/src/Experiments/Commands/Vue/commands.js @@ -2,16 +2,17 @@ import { Extension } from '@tiptap/core' import Suggestion from '@tiptap/suggestion' export default Extension.create({ - name: 'mention', + name: 'commands', - defaultOptions: { - suggestion: { - char: '/', - startOfLine: false, - command: ({ editor, range, props }) => { - props.command({ editor, range }) + addOptions() { + return { + suggestion: { + char: '/', + command: ({ editor, range, props }) => { + props.command({ editor, range }) + }, }, - }, + } }, addProseMirrorPlugins() { diff --git a/demos/src/Experiments/Details/Vue/details-summary.ts b/demos/src/Experiments/Details/Vue/details-summary.ts index 9b4b403d..71819c7a 100644 --- a/demos/src/Experiments/Details/Vue/details-summary.ts +++ b/demos/src/Experiments/Details/Vue/details-summary.ts @@ -17,8 +17,10 @@ export default Node.create({ isolating: true, - defaultOptions: { - HTMLAttributes: {}, + addOptions() { + return { + HTMLAttributes: {}, + } }, parseHTML() { diff --git a/demos/src/Experiments/Details/Vue/details.ts b/demos/src/Experiments/Details/Vue/details.ts index bf43ccbc..a6359c80 100644 --- a/demos/src/Experiments/Details/Vue/details.ts +++ b/demos/src/Experiments/Details/Vue/details.ts @@ -34,8 +34,10 @@ export default Node.create({ // defining: true, - defaultOptions: { - HTMLAttributes: {}, + addOptions() { + return { + HTMLAttributes: {}, + } }, parseHTML() { diff --git a/demos/src/Experiments/Embeds/Vue/iframe.ts b/demos/src/Experiments/Embeds/Vue/iframe.ts index 6c3d4e57..85392e24 100644 --- a/demos/src/Experiments/Embeds/Vue/iframe.ts +++ b/demos/src/Experiments/Embeds/Vue/iframe.ts @@ -18,18 +18,20 @@ declare module '@tiptap/core' { } } -export default Node.create({ +export default Node.create({ name: 'iframe', group: 'block', atom: true, - defaultOptions: { - allowFullscreen: true, - HTMLAttributes: { - class: 'iframe-wrapper', - }, + addOptions() { + return { + allowFullscreen: true, + HTMLAttributes: { + class: 'iframe-wrapper', + }, + } }, addAttributes() { diff --git a/demos/src/Experiments/Figure/Vue/figure.ts b/demos/src/Experiments/Figure/Vue/figure.ts index fb2c6a40..70bd462f 100644 --- a/demos/src/Experiments/Figure/Vue/figure.ts +++ b/demos/src/Experiments/Figure/Vue/figure.ts @@ -41,8 +41,10 @@ export const inputRegex = /!\[(.+|:?)]\((\S+)(?:(?:\s+)["'](\S+)["'])?\)/ export const Figure = Node.create({ name: 'figure', - defaultOptions: { - HTMLAttributes: {}, + addOptions() { + return { + HTMLAttributes: {}, + } }, group: 'block', diff --git a/demos/src/Experiments/GenericFigure/Vue/figcaption.ts b/demos/src/Experiments/GenericFigure/Vue/figcaption.ts index dd9fb473..2d13fc4e 100644 --- a/demos/src/Experiments/GenericFigure/Vue/figcaption.ts +++ b/demos/src/Experiments/GenericFigure/Vue/figcaption.ts @@ -3,8 +3,10 @@ import { Node, mergeAttributes } from '@tiptap/core' export const Figcaption = Node.create({ name: 'figcaption', - defaultOptions: { - HTMLAttributes: {}, + addOptions() { + return { + HTMLAttributes: {}, + } }, content: 'inline*', diff --git a/demos/src/Experiments/GenericFigure/Vue/figure.ts b/demos/src/Experiments/GenericFigure/Vue/figure.ts index 198c4933..107a95bc 100644 --- a/demos/src/Experiments/GenericFigure/Vue/figure.ts +++ b/demos/src/Experiments/GenericFigure/Vue/figure.ts @@ -4,8 +4,10 @@ import { Plugin } from 'prosemirror-state' export const Figure = Node.create({ name: 'figure', - defaultOptions: { - HTMLAttributes: {}, + addOptions() { + return { + HTMLAttributes: {}, + } }, group: 'block', diff --git a/demos/src/Experiments/Linter/Vue/extension/Linter.ts b/demos/src/Experiments/Linter/Vue/extension/Linter.ts index ab5edc5b..75de9ed7 100644 --- a/demos/src/Experiments/Linter/Vue/extension/Linter.ts +++ b/demos/src/Experiments/Linter/Vue/extension/Linter.ts @@ -39,11 +39,13 @@ export interface LinterOptions { plugins: Array, } -export const Linter = Extension.create({ +export const Linter = Extension.create({ name: 'linter', - defaultOptions: { - plugins: [], + addOptions() { + return { + plugins: [], + } }, addProseMirrorPlugins() { diff --git a/demos/src/Experiments/TrailingNode/Vue/trailing-node.ts b/demos/src/Experiments/TrailingNode/Vue/trailing-node.ts index e2c47c09..0643b6e9 100644 --- a/demos/src/Experiments/TrailingNode/Vue/trailing-node.ts +++ b/demos/src/Experiments/TrailingNode/Vue/trailing-node.ts @@ -20,11 +20,13 @@ export interface TrailingNodeOptions { export const TrailingNode = Extension.create({ name: 'trailingNode', - defaultOptions: { - node: 'paragraph', - notAfter: [ - 'paragraph', - ], + addOptions() { + return { + node: 'paragraph', + notAfter: [ + 'paragraph', + ], + } }, addProseMirrorPlugins() { diff --git a/docs/guide/custom-extensions.md b/docs/guide/custom-extensions.md index eeb2e210..4bca05e9 100644 --- a/docs/guide/custom-extensions.md +++ b/docs/guide/custom-extensions.md @@ -71,9 +71,11 @@ All settings can be configured through the extension anyway, but if you want to import Heading from '@tiptap/extension-heading' const CustomHeading = Heading.extend({ - defaultOptions: { - ...Heading.options, - levels: [1, 2, 3], + addOptions() { + return { + ...this.parent(), + levels: [1, 2, 3], + } }, }) ``` diff --git a/docs/guide/typescript.md b/docs/guide/typescript.md index c329a354..cab5c9a9 100644 --- a/docs/guide/typescript.md +++ b/docs/guide/typescript.md @@ -26,8 +26,10 @@ export interface CustomExtensionOptions { } const CustomExtension = Extension.create({ - defaultOptions: { - awesomeness: 100, + addOptions() { + return { + awesomeness: 100, + } }, }) ``` diff --git a/docs/overview/upgrade-guide.md b/docs/overview/upgrade-guide.md index 00890a3d..8218703d 100644 --- a/docs/overview/upgrade-guide.md +++ b/docs/overview/upgrade-guide.md @@ -103,7 +103,7 @@ import { Node } from '@tiptap/core' const CustomExtension = Node.create({ name: 'custom_extension', - defaultOptions: { + addOptions() { … }, addAttributes() { diff --git a/packages/core/src/Extension.ts b/packages/core/src/Extension.ts index 51199d02..48d4a0b0 100644 --- a/packages/core/src/Extension.ts +++ b/packages/core/src/Extension.ts @@ -36,13 +36,21 @@ declare module '@tiptap/core' { */ defaultOptions?: Options, + /** + * Default Options + */ + addOptions?: (this: { + name: string, + parent: Exclude>['addOptions'], undefined>, + }) => Options, + /** * Default Storage */ addStorage?: (this: { name: string, options: Options, - parent: ParentConfig>['addGlobalAttributes'], + parent: Exclude>['addStorage'], undefined>, }) => Storage, /** @@ -278,7 +286,24 @@ export class Extension { } this.name = this.config.name + + if (config.defaultOptions) { + console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`) + } + + // TODO: remove `addOptions` fallback this.options = this.config.defaultOptions + + if (this.config.addOptions) { + this.options = callOrReturn(getExtensionField( + this, + 'addOptions', + { + name: this.name, + }, + )) + } + this.storage = callOrReturn(getExtensionField( this, 'addStorage', @@ -286,7 +311,7 @@ export class Extension { name: this.name, options: this.options, }, - )) + )) || {} } static create(config: Partial> = {}) { @@ -323,10 +348,25 @@ export class Extension { ? extendedConfig.name : extension.parent.name + if (extendedConfig.defaultOptions) { + console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`) + } + + // TODO: remove `addOptions` fallback extension.options = extendedConfig.defaultOptions ? extendedConfig.defaultOptions : extension.parent.options + if (extendedConfig.addOptions) { + extension.options = callOrReturn(getExtensionField( + extension, + 'addOptions', + { + name: extension.name, + }, + )) + } + extension.storage = callOrReturn(getExtensionField( extension, 'addStorage', diff --git a/packages/core/src/Mark.ts b/packages/core/src/Mark.ts index 0947f69f..4cd38816 100644 --- a/packages/core/src/Mark.ts +++ b/packages/core/src/Mark.ts @@ -42,13 +42,21 @@ declare module '@tiptap/core' { */ defaultOptions?: Options, + /** + * Default Options + */ + addOptions?: (this: { + name: string, + parent: Exclude>['addOptions'], undefined>, + }) => Options, + /** * Default Storage */ - addStorage?: (this: { + addStorage?: (this: { name: string, options: Options, - parent: ParentConfig>['addGlobalAttributes'], + parent: Exclude>['addStorage'], undefined>, }) => Storage, /** @@ -392,7 +400,24 @@ export class Mark { } this.name = this.config.name + + if (config.defaultOptions) { + console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`) + } + + // TODO: remove `addOptions` fallback this.options = this.config.defaultOptions + + if (this.config.addOptions) { + this.options = callOrReturn(getExtensionField( + this, + 'addOptions', + { + name: this.name, + }, + )) + } + this.storage = callOrReturn(getExtensionField( this, 'addStorage', @@ -400,7 +425,7 @@ export class Mark { name: this.name, options: this.options, }, - )) + )) || {} } static create(config: Partial> = {}) { @@ -437,10 +462,25 @@ export class Mark { ? extendedConfig.name : extension.parent.name + if (extendedConfig.defaultOptions) { + console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`) + } + + // TODO: remove `addOptions` fallback extension.options = extendedConfig.defaultOptions ? extendedConfig.defaultOptions : extension.parent.options + if (extendedConfig.addOptions) { + extension.options = callOrReturn(getExtensionField( + extension, + 'addOptions', + { + name: extension.name, + }, + )) + } + extension.storage = callOrReturn(getExtensionField( extension, 'addStorage', diff --git a/packages/core/src/Node.ts b/packages/core/src/Node.ts index 14e28117..fe51bd8b 100644 --- a/packages/core/src/Node.ts +++ b/packages/core/src/Node.ts @@ -42,13 +42,21 @@ declare module '@tiptap/core' { */ defaultOptions?: Options, + /** + * Default Options + */ + addOptions?: (this: { + name: string, + parent: Exclude>['addOptions'], undefined>, + }) => Options, + /** * Default Storage */ addStorage?: (this: { name: string, options: Options, - parent: ParentConfig>['addGlobalAttributes'], + parent: Exclude>['addStorage'], undefined>, }) => Storage, /** @@ -472,7 +480,24 @@ export class Node { } this.name = this.config.name + + if (config.defaultOptions) { + console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`) + } + + // TODO: remove `addOptions` fallback this.options = this.config.defaultOptions + + if (this.config.addOptions) { + this.options = callOrReturn(getExtensionField( + this, + 'addOptions', + { + name: this.name, + }, + )) + } + this.storage = callOrReturn(getExtensionField( this, 'addStorage', @@ -480,7 +505,7 @@ export class Node { name: this.name, options: this.options, }, - )) + )) || {} } static create(config: Partial> = {}) { @@ -517,10 +542,25 @@ export class Node { ? extendedConfig.name : extension.parent.name + if (extendedConfig.defaultOptions) { + console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`) + } + + // TODO: remove `addOptions` fallback extension.options = extendedConfig.defaultOptions ? extendedConfig.defaultOptions : extension.parent.options + if (extendedConfig.addOptions) { + extension.options = callOrReturn(getExtensionField( + extension, + 'addOptions', + { + name: extension.name, + }, + )) + } + extension.storage = callOrReturn(getExtensionField( extension, 'addStorage', diff --git a/packages/extension-blockquote/src/blockquote.ts b/packages/extension-blockquote/src/blockquote.ts index 13a23333..57916a8f 100644 --- a/packages/extension-blockquote/src/blockquote.ts +++ b/packages/extension-blockquote/src/blockquote.ts @@ -29,8 +29,10 @@ export const Blockquote = Node.create({ name: 'blockquote', - defaultOptions: { - HTMLAttributes: {}, + addOptions() { + return { + HTMLAttributes: {}, + } }, content: 'block*', diff --git a/packages/extension-bold/src/bold.ts b/packages/extension-bold/src/bold.ts index 728ba00e..71c26e17 100644 --- a/packages/extension-bold/src/bold.ts +++ b/packages/extension-bold/src/bold.ts @@ -36,8 +36,10 @@ export const underscorePasteRegex = /(?:^|\s)((?:__)((?:[^__]+))(?:__))/g export const Bold = Mark.create({ name: 'bold', - defaultOptions: { - HTMLAttributes: {}, + addOptions() { + return { + HTMLAttributes: {}, + } }, parseHTML() { diff --git a/packages/extension-bubble-menu/src/bubble-menu.ts b/packages/extension-bubble-menu/src/bubble-menu.ts index 46094ab0..18ebe05a 100644 --- a/packages/extension-bubble-menu/src/bubble-menu.ts +++ b/packages/extension-bubble-menu/src/bubble-menu.ts @@ -8,11 +8,13 @@ export type BubbleMenuOptions = Omit({ name: 'bubbleMenu', - defaultOptions: { - element: null, - tippyOptions: {}, - pluginKey: 'bubbleMenu', - shouldShow: null, + addOptions() { + return { + element: null, + tippyOptions: {}, + pluginKey: 'bubbleMenu', + shouldShow: null, + } }, addProseMirrorPlugins() { diff --git a/packages/extension-bullet-list/src/bullet-list.ts b/packages/extension-bullet-list/src/bullet-list.ts index eb214435..08b05b66 100644 --- a/packages/extension-bullet-list/src/bullet-list.ts +++ b/packages/extension-bullet-list/src/bullet-list.ts @@ -20,8 +20,10 @@ export const inputRegex = /^\s*([-+*])\s$/ export const BulletList = Node.create({ name: 'bulletList', - defaultOptions: { - HTMLAttributes: {}, + addOptions() { + return { + HTMLAttributes: {}, + } }, group: 'block list', diff --git a/packages/extension-character-count/src/character-count.ts b/packages/extension-character-count/src/character-count.ts index bdad6d7a..5e4c4a46 100644 --- a/packages/extension-character-count/src/character-count.ts +++ b/packages/extension-character-count/src/character-count.ts @@ -10,8 +10,10 @@ export interface CharacterCountOptions { export const CharacterCount = Extension.create({ name: 'characterCount', - defaultOptions: { - limit: 0, + addOptions() { + return { + limit: 0, + } }, addProseMirrorPlugins() { diff --git a/packages/extension-code-block-lowlight/src/code-block-lowlight.ts b/packages/extension-code-block-lowlight/src/code-block-lowlight.ts index 86eadecd..5d369eb0 100644 --- a/packages/extension-code-block-lowlight/src/code-block-lowlight.ts +++ b/packages/extension-code-block-lowlight/src/code-block-lowlight.ts @@ -7,9 +7,11 @@ export interface CodeBlockLowlightOptions extends CodeBlockOptions { } export const CodeBlockLowlight = CodeBlock.extend({ - defaultOptions: { - ...CodeBlock.options, - lowlight, + addOptions() { + return { + ...this.parent?.(), + lowlight, + } }, addProseMirrorPlugins() { diff --git a/packages/extension-code-block/src/code-block.ts b/packages/extension-code-block/src/code-block.ts index 085a7fec..b19085a5 100644 --- a/packages/extension-code-block/src/code-block.ts +++ b/packages/extension-code-block/src/code-block.ts @@ -27,9 +27,11 @@ export const tildeInputRegex = /^~~~(?[a-z]*)?[\s\n]$/ export const CodeBlock = Node.create({ name: 'codeBlock', - defaultOptions: { - languageClassPrefix: 'language-', - HTMLAttributes: {}, + addOptions() { + return { + languageClassPrefix: 'language-', + HTMLAttributes: {}, + } }, content: 'text*', diff --git a/packages/extension-code/src/code.ts b/packages/extension-code/src/code.ts index 25581a72..ea99ca67 100644 --- a/packages/extension-code/src/code.ts +++ b/packages/extension-code/src/code.ts @@ -34,8 +34,10 @@ export const pasteRegex = /(?:^|\s)((?:`)((?:[^`]+))(?:`))/g export const Code = Mark.create({ name: 'code', - defaultOptions: { - HTMLAttributes: {}, + addOptions() { + return { + HTMLAttributes: {}, + } }, excludes: '_', diff --git a/packages/extension-collaboration-cursor/src/collaboration-cursor.ts b/packages/extension-collaboration-cursor/src/collaboration-cursor.ts index 53870a56..3efa1525 100644 --- a/packages/extension-collaboration-cursor/src/collaboration-cursor.ts +++ b/packages/extension-collaboration-cursor/src/collaboration-cursor.ts @@ -31,26 +31,28 @@ const awarenessStatesToArray = (states: Map>) => { export const CollaborationCursor = Extension.create({ name: 'collaborationCursor', - defaultOptions: { - provider: null, - user: { - name: null, - color: null, - }, - render: user => { - const cursor = document.createElement('span') - cursor.classList.add('collaboration-cursor__caret') - cursor.setAttribute('style', `border-color: ${user.color}`) + addOptions() { + return { + provider: null, + user: { + name: null, + color: null, + }, + render: user => { + const cursor = document.createElement('span') + cursor.classList.add('collaboration-cursor__caret') + cursor.setAttribute('style', `border-color: ${user.color}`) - const label = document.createElement('div') - label.classList.add('collaboration-cursor__label') - label.setAttribute('style', `background-color: ${user.color}`) - label.insertBefore(document.createTextNode(user.name), null) - cursor.insertBefore(label, null) + const label = document.createElement('div') + label.classList.add('collaboration-cursor__label') + label.setAttribute('style', `background-color: ${user.color}`) + label.insertBefore(document.createTextNode(user.name), null) + cursor.insertBefore(label, null) - return cursor - }, - onUpdate: () => null, + return cursor + }, + onUpdate: () => null, + } }, addCommands() { diff --git a/packages/extension-collaboration/src/collaboration.ts b/packages/extension-collaboration/src/collaboration.ts index eed8078c..8f04ade0 100644 --- a/packages/extension-collaboration/src/collaboration.ts +++ b/packages/extension-collaboration/src/collaboration.ts @@ -43,10 +43,12 @@ export const Collaboration = Extension.create({ priority: 1000, - defaultOptions: { - document: null, - field: 'default', - fragment: null, + addOptions() { + return { + document: null, + field: 'default', + fragment: null, + } }, onCreate() { diff --git a/packages/extension-color/src/color.ts b/packages/extension-color/src/color.ts index a3364693..57630e04 100644 --- a/packages/extension-color/src/color.ts +++ b/packages/extension-color/src/color.ts @@ -23,8 +23,10 @@ declare module '@tiptap/core' { export const Color = Extension.create({ name: 'color', - defaultOptions: { - types: ['textStyle'], + addOptions() { + return { + types: ['textStyle'], + } }, addGlobalAttributes() { diff --git a/packages/extension-dropcursor/src/dropcursor.ts b/packages/extension-dropcursor/src/dropcursor.ts index 214f523e..303e4308 100644 --- a/packages/extension-dropcursor/src/dropcursor.ts +++ b/packages/extension-dropcursor/src/dropcursor.ts @@ -10,10 +10,12 @@ export interface DropcursorOptions { export const Dropcursor = Extension.create({ name: 'dropCursor', - defaultOptions: { - color: 'currentColor', - width: 1, - class: null, + addOptions() { + return { + color: 'currentColor', + width: 1, + class: null, + } }, addProseMirrorPlugins() { diff --git a/packages/extension-floating-menu/src/floating-menu.ts b/packages/extension-floating-menu/src/floating-menu.ts index 3e4fd57a..bcd871ca 100644 --- a/packages/extension-floating-menu/src/floating-menu.ts +++ b/packages/extension-floating-menu/src/floating-menu.ts @@ -8,11 +8,13 @@ export type FloatingMenuOptions = Omit({ name: 'floatingMenu', - defaultOptions: { - element: null, - tippyOptions: {}, - pluginKey: 'floatingMenu', - shouldShow: null, + addOptions() { + return { + element: null, + tippyOptions: {}, + pluginKey: 'floatingMenu', + shouldShow: null, + } }, addProseMirrorPlugins() { diff --git a/packages/extension-focus/src/focus.ts b/packages/extension-focus/src/focus.ts index 2bbb3337..7a068bbe 100644 --- a/packages/extension-focus/src/focus.ts +++ b/packages/extension-focus/src/focus.ts @@ -10,9 +10,11 @@ export interface FocusOptions { export const FocusClasses = Extension.create({ name: 'focus', - defaultOptions: { - className: 'has-focus', - mode: 'all', + addOptions() { + return { + className: 'has-focus', + mode: 'all', + } }, addProseMirrorPlugins() { diff --git a/packages/extension-font-family/src/font-family.ts b/packages/extension-font-family/src/font-family.ts index d1cc1cb4..453f4dc1 100644 --- a/packages/extension-font-family/src/font-family.ts +++ b/packages/extension-font-family/src/font-family.ts @@ -23,8 +23,10 @@ declare module '@tiptap/core' { export const FontFamily = Extension.create({ name: 'fontFamily', - defaultOptions: { - types: ['textStyle'], + addOptions() { + return { + types: ['textStyle'], + } }, addGlobalAttributes() { diff --git a/packages/extension-hard-break/src/hard-break.ts b/packages/extension-hard-break/src/hard-break.ts index 93bc7c36..0d853b75 100644 --- a/packages/extension-hard-break/src/hard-break.ts +++ b/packages/extension-hard-break/src/hard-break.ts @@ -19,9 +19,11 @@ declare module '@tiptap/core' { export const HardBreak = Node.create({ name: 'hardBreak', - defaultOptions: { - keepMarks: true, - HTMLAttributes: {}, + addOptions() { + return { + keepMarks: true, + HTMLAttributes: {}, + } }, inline: true, diff --git a/packages/extension-heading/src/heading.ts b/packages/extension-heading/src/heading.ts index 9d84e1a4..346bcb9f 100644 --- a/packages/extension-heading/src/heading.ts +++ b/packages/extension-heading/src/heading.ts @@ -25,9 +25,11 @@ declare module '@tiptap/core' { export const Heading = Node.create({ name: 'heading', - defaultOptions: { - levels: [1, 2, 3, 4, 5, 6], - HTMLAttributes: {}, + addOptions() { + return { + levels: [1, 2, 3, 4, 5, 6], + HTMLAttributes: {}, + } }, content: 'inline*', diff --git a/packages/extension-highlight/src/highlight.ts b/packages/extension-highlight/src/highlight.ts index 06d8febb..8f8a8374 100644 --- a/packages/extension-highlight/src/highlight.ts +++ b/packages/extension-highlight/src/highlight.ts @@ -35,9 +35,11 @@ export const pasteRegex = /(?:^|\s)((?:==)((?:[^~]+))(?:==))/g export const Highlight = Mark.create({ name: 'highlight', - defaultOptions: { - multicolor: false, - HTMLAttributes: {}, + addOptions() { + return { + multicolor: false, + HTMLAttributes: {}, + } }, addAttributes() { diff --git a/packages/extension-history/src/history.ts b/packages/extension-history/src/history.ts index 02f66a03..c3dd6852 100644 --- a/packages/extension-history/src/history.ts +++ b/packages/extension-history/src/history.ts @@ -24,9 +24,11 @@ declare module '@tiptap/core' { export const History = Extension.create({ name: 'history', - defaultOptions: { - depth: 100, - newGroupDelay: 500, + addOptions() { + return { + depth: 100, + newGroupDelay: 500, + } }, addCommands() { diff --git a/packages/extension-horizontal-rule/src/horizontal-rule.ts b/packages/extension-horizontal-rule/src/horizontal-rule.ts index 57d938f2..c82ab053 100644 --- a/packages/extension-horizontal-rule/src/horizontal-rule.ts +++ b/packages/extension-horizontal-rule/src/horizontal-rule.ts @@ -23,8 +23,10 @@ declare module '@tiptap/core' { export const HorizontalRule = Node.create({ name: 'horizontalRule', - defaultOptions: { - HTMLAttributes: {}, + addOptions() { + return { + HTMLAttributes: {}, + } }, group: 'block', diff --git a/packages/extension-image/src/image.ts b/packages/extension-image/src/image.ts index c7afb83e..4d6eb7f5 100644 --- a/packages/extension-image/src/image.ts +++ b/packages/extension-image/src/image.ts @@ -25,9 +25,11 @@ export const inputRegex = /(!\[(.+|:?)]\((\S+)(?:(?:\s+)["'](\S+)["'])?\))/ export const Image = Node.create({ name: 'image', - defaultOptions: { - inline: false, - HTMLAttributes: {}, + addOptions() { + return { + inline: false, + HTMLAttributes: {}, + } }, inline() { diff --git a/packages/extension-italic/src/italic.ts b/packages/extension-italic/src/italic.ts index cb6d3f61..705f5b19 100644 --- a/packages/extension-italic/src/italic.ts +++ b/packages/extension-italic/src/italic.ts @@ -36,8 +36,10 @@ export const underscorePasteRegex = /(?:^|\s)((?:_)((?:[^_]+))(?:_))/g export const Italic = Mark.create({ name: 'italic', - defaultOptions: { - HTMLAttributes: {}, + addOptions() { + return { + HTMLAttributes: {}, + } }, parseHTML() { diff --git a/packages/extension-link/src/link.ts b/packages/extension-link/src/link.ts index 25dc4980..108806c9 100644 --- a/packages/extension-link/src/link.ts +++ b/packages/extension-link/src/link.ts @@ -47,13 +47,15 @@ export const Link = Mark.create({ inclusive: false, - defaultOptions: { - openOnClick: true, - linkOnPaste: true, - HTMLAttributes: { - target: '_blank', - rel: 'noopener noreferrer nofollow', - }, + addOptions() { + return { + openOnClick: true, + linkOnPaste: true, + HTMLAttributes: { + target: '_blank', + rel: 'noopener noreferrer nofollow', + }, + } }, addAttributes() { diff --git a/packages/extension-list-item/src/list-item.ts b/packages/extension-list-item/src/list-item.ts index 23df38b5..4845a623 100644 --- a/packages/extension-list-item/src/list-item.ts +++ b/packages/extension-list-item/src/list-item.ts @@ -7,8 +7,10 @@ export interface ListItemOptions { export const ListItem = Node.create({ name: 'listItem', - defaultOptions: { - HTMLAttributes: {}, + addOptions() { + return { + HTMLAttributes: {}, + } }, content: 'paragraph block*', diff --git a/packages/extension-mention/src/mention.ts b/packages/extension-mention/src/mention.ts index 41c768a4..a50249cb 100644 --- a/packages/extension-mention/src/mention.ts +++ b/packages/extension-mention/src/mention.ts @@ -17,47 +17,49 @@ export const MentionPluginKey = new PluginKey('mention') export const Mention = Node.create({ name: 'mention', - defaultOptions: { - HTMLAttributes: {}, - renderLabel({ options, node }) { - return `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}` - }, - suggestion: { - char: '@', - pluginKey: MentionPluginKey, - command: ({ editor, range, props }) => { - // increase range.to by one when the next node is of type "text" - // and starts with a space character - const nodeAfter = editor.view.state.selection.$to.nodeAfter - const overrideSpace = nodeAfter?.text?.startsWith(' ') - - if (overrideSpace) { - range.to += 1 - } - - editor - .chain() - .focus() - .insertContentAt(range, [ - { - type: 'mention', - attrs: props, - }, - { - type: 'text', - text: ' ', - }, - ]) - .run() + addOptions() { + return { + HTMLAttributes: {}, + renderLabel({ options, node }) { + return `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}` }, - allow: ({ editor, range }) => { - const $from = editor.state.doc.resolve(range.from) - const type = editor.schema.nodes.mention - const allow = !!$from.parent.type.contentMatch.matchType(type) + suggestion: { + char: '@', + pluginKey: MentionPluginKey, + command: ({ editor, range, props }) => { + // increase range.to by one when the next node is of type "text" + // and starts with a space character + const nodeAfter = editor.view.state.selection.$to.nodeAfter + const overrideSpace = nodeAfter?.text?.startsWith(' ') - return allow + if (overrideSpace) { + range.to += 1 + } + + editor + .chain() + .focus() + .insertContentAt(range, [ + { + type: this.name, + attrs: props, + }, + { + type: 'text', + text: ' ', + }, + ]) + .run() + }, + allow: ({ editor, range }) => { + const $from = editor.state.doc.resolve(range.from) + const type = editor.schema.nodes[this.name] + const allow = !!$from.parent.type.contentMatch.matchType(type) + + return allow + }, }, - }, + } }, group: 'inline', diff --git a/packages/extension-ordered-list/src/ordered-list.ts b/packages/extension-ordered-list/src/ordered-list.ts index 6ba22f5e..a57dae69 100644 --- a/packages/extension-ordered-list/src/ordered-list.ts +++ b/packages/extension-ordered-list/src/ordered-list.ts @@ -20,8 +20,10 @@ export const inputRegex = /^(\d+)\.\s$/ export const OrderedList = Node.create({ name: 'orderedList', - defaultOptions: { - HTMLAttributes: {}, + addOptions() { + return { + HTMLAttributes: {}, + } }, group: 'block list', diff --git a/packages/extension-paragraph/src/paragraph.ts b/packages/extension-paragraph/src/paragraph.ts index 005b5849..bf146932 100644 --- a/packages/extension-paragraph/src/paragraph.ts +++ b/packages/extension-paragraph/src/paragraph.ts @@ -20,8 +20,10 @@ export const Paragraph = Node.create({ priority: 1000, - defaultOptions: { - HTMLAttributes: {}, + addOptions() { + return { + HTMLAttributes: {}, + } }, group: 'block', diff --git a/packages/extension-placeholder/src/placeholder.ts b/packages/extension-placeholder/src/placeholder.ts index c5e25ba0..2258bd94 100644 --- a/packages/extension-placeholder/src/placeholder.ts +++ b/packages/extension-placeholder/src/placeholder.ts @@ -19,13 +19,15 @@ export interface PlaceholderOptions { export const Placeholder = Extension.create({ name: 'placeholder', - defaultOptions: { - emptyEditorClass: 'is-editor-empty', - emptyNodeClass: 'is-empty', - placeholder: 'Write something …', - showOnlyWhenEditable: true, - showOnlyCurrent: true, - includeChildren: false, + addOptions() { + return { + emptyEditorClass: 'is-editor-empty', + emptyNodeClass: 'is-empty', + placeholder: 'Write something …', + showOnlyWhenEditable: true, + showOnlyCurrent: true, + includeChildren: false, + } }, addProseMirrorPlugins() { diff --git a/packages/extension-strike/src/strike.ts b/packages/extension-strike/src/strike.ts index c0fcbf46..5d8711b2 100644 --- a/packages/extension-strike/src/strike.ts +++ b/packages/extension-strike/src/strike.ts @@ -34,8 +34,10 @@ export const pasteRegex = /(?:^|\s)((?:~~)((?:[^~]+))(?:~~))/g export const Strike = Mark.create({ name: 'strike', - defaultOptions: { - HTMLAttributes: {}, + addOptions() { + return { + HTMLAttributes: {}, + } }, parseHTML() { diff --git a/packages/extension-subscript/src/subscript.ts b/packages/extension-subscript/src/subscript.ts index 1ad35caa..e99ff222 100644 --- a/packages/extension-subscript/src/subscript.ts +++ b/packages/extension-subscript/src/subscript.ts @@ -26,8 +26,10 @@ declare module '@tiptap/core' { export const Subscript = Mark.create({ name: 'subscript', - defaultOptions: { - HTMLAttributes: {}, + addOptions() { + return { + HTMLAttributes: {}, + } }, parseHTML() { diff --git a/packages/extension-superscript/src/superscript.ts b/packages/extension-superscript/src/superscript.ts index 5c041b1b..6d3a1cba 100644 --- a/packages/extension-superscript/src/superscript.ts +++ b/packages/extension-superscript/src/superscript.ts @@ -26,8 +26,10 @@ declare module '@tiptap/core' { export const Superscript = Mark.create({ name: 'superscript', - defaultOptions: { - HTMLAttributes: {}, + addOptions() { + return { + HTMLAttributes: {}, + } }, parseHTML() { diff --git a/packages/extension-table-cell/src/table-cell.ts b/packages/extension-table-cell/src/table-cell.ts index d39b48b7..a726cb83 100644 --- a/packages/extension-table-cell/src/table-cell.ts +++ b/packages/extension-table-cell/src/table-cell.ts @@ -7,8 +7,10 @@ export interface TableCellOptions { export const TableCell = Node.create({ name: 'tableCell', - defaultOptions: { - HTMLAttributes: {}, + addOptions() { + return { + HTMLAttributes: {}, + } }, content: 'block+', diff --git a/packages/extension-table-header/src/table-header.ts b/packages/extension-table-header/src/table-header.ts index 6c8b3c48..f88301fd 100644 --- a/packages/extension-table-header/src/table-header.ts +++ b/packages/extension-table-header/src/table-header.ts @@ -6,8 +6,10 @@ export interface TableHeaderOptions { export const TableHeader = Node.create({ name: 'tableHeader', - defaultOptions: { - HTMLAttributes: {}, + addOptions() { + return { + HTMLAttributes: {}, + } }, content: 'block+', diff --git a/packages/extension-table-row/src/table-row.ts b/packages/extension-table-row/src/table-row.ts index 6bc17084..2c56c1a4 100644 --- a/packages/extension-table-row/src/table-row.ts +++ b/packages/extension-table-row/src/table-row.ts @@ -7,8 +7,10 @@ export interface TableRowOptions { export const TableRow = Node.create({ name: 'tableRow', - defaultOptions: { - HTMLAttributes: {}, + addOptions() { + return { + HTMLAttributes: {}, + } }, content: '(tableCell | tableHeader)*', diff --git a/packages/extension-table/src/table.ts b/packages/extension-table/src/table.ts index 9df7be42..436a190a 100644 --- a/packages/extension-table/src/table.ts +++ b/packages/extension-table/src/table.ts @@ -82,16 +82,18 @@ declare module '@tiptap/core' { export const Table = Node.create({ name: 'table', - defaultOptions: { - HTMLAttributes: {}, - resizable: false, - handleWidth: 5, - cellMinWidth: 25, - // TODO: fix - // @ts-ignore - View: TableView, - lastColumnResizable: true, - allowTableNodeSelection: false, + // @ts-ignore + addOptions() { + return { + HTMLAttributes: {}, + resizable: false, + handleWidth: 5, + cellMinWidth: 25, + // TODO: fix + View: TableView, + lastColumnResizable: true, + allowTableNodeSelection: false, + } }, content: 'tableRow+', diff --git a/packages/extension-task-item/src/task-item.ts b/packages/extension-task-item/src/task-item.ts index 75f5c807..5b513812 100644 --- a/packages/extension-task-item/src/task-item.ts +++ b/packages/extension-task-item/src/task-item.ts @@ -10,9 +10,11 @@ export const inputRegex = /^\s*(\[([ |x])\])\s$/ export const TaskItem = Node.create({ name: 'taskItem', - defaultOptions: { - nested: false, - HTMLAttributes: {}, + addOptions() { + return { + nested: false, + HTMLAttributes: {}, + } }, content() { diff --git a/packages/extension-task-list/src/task-list.ts b/packages/extension-task-list/src/task-list.ts index bbcc102b..2dfc0521 100644 --- a/packages/extension-task-list/src/task-list.ts +++ b/packages/extension-task-list/src/task-list.ts @@ -18,8 +18,10 @@ declare module '@tiptap/core' { export const TaskList = Node.create({ name: 'taskList', - defaultOptions: { - HTMLAttributes: {}, + addOptions() { + return { + HTMLAttributes: {}, + } }, group: 'block list', diff --git a/packages/extension-text-align/src/text-align.ts b/packages/extension-text-align/src/text-align.ts index 1816d781..ce3109d9 100644 --- a/packages/extension-text-align/src/text-align.ts +++ b/packages/extension-text-align/src/text-align.ts @@ -24,10 +24,12 @@ declare module '@tiptap/core' { export const TextAlign = Extension.create({ name: 'textAlign', - defaultOptions: { - types: [], - alignments: ['left', 'center', 'right', 'justify'], - defaultAlignment: 'left', + addOptions() { + return { + types: [], + alignments: ['left', 'center', 'right', 'justify'], + defaultAlignment: 'left', + } }, addGlobalAttributes() { diff --git a/packages/extension-text-style/src/text-style.ts b/packages/extension-text-style/src/text-style.ts index d1187cb1..0fc9daf3 100644 --- a/packages/extension-text-style/src/text-style.ts +++ b/packages/extension-text-style/src/text-style.ts @@ -22,8 +22,10 @@ declare module '@tiptap/core' { export const TextStyle = Mark.create({ name: 'textStyle', - defaultOptions: { - HTMLAttributes: {}, + addOptions() { + return { + HTMLAttributes: {}, + } }, parseHTML() { diff --git a/packages/extension-underline/src/underline.ts b/packages/extension-underline/src/underline.ts index 5e1b790e..05344ac3 100644 --- a/packages/extension-underline/src/underline.ts +++ b/packages/extension-underline/src/underline.ts @@ -26,8 +26,10 @@ declare module '@tiptap/core' { export const Underline = Mark.create({ name: 'underline', - defaultOptions: { - HTMLAttributes: {}, + addOptions() { + return { + HTMLAttributes: {}, + } }, parseHTML() { diff --git a/tests/cypress/integration/core/extensionOptions.spec.ts b/tests/cypress/integration/core/extensionOptions.spec.ts index ee5a52f9..46031526 100644 --- a/tests/cypress/integration/core/extensionOptions.spec.ts +++ b/tests/cypress/integration/core/extensionOptions.spec.ts @@ -3,7 +3,7 @@ import { Extension } from '@tiptap/core/src/Extension' describe('extension options', () => { - it('should set options', () => { + it('should set options (deprecated)', () => { const extension = Extension.create({ defaultOptions: { foo: 1, @@ -17,7 +17,23 @@ describe('extension options', () => { }) }) - it('should pass through', () => { + it('should set options', () => { + const extension = Extension.create({ + addOptions() { + return { + foo: 1, + bar: 1, + } + }, + }) + + expect(extension.options).to.deep.eq({ + foo: 1, + bar: 1, + }) + }) + + it('should pass through (deprecated)', () => { const extension = Extension .create({ defaultOptions: { @@ -34,7 +50,26 @@ describe('extension options', () => { }) }) - it('should be configurable', () => { + it('should pass through', () => { + const extension = Extension + .create({ + addOptions() { + return { + foo: 1, + bar: 1, + } + }, + }) + .extend() + .configure() + + expect(extension.options).to.deep.eq({ + foo: 1, + bar: 1, + }) + }) + + it('should be configurable (deprecated)', () => { const extension = Extension .create({ defaultOptions: { @@ -52,7 +87,27 @@ describe('extension options', () => { }) }) - it('should be extendable', () => { + it('should be configurable', () => { + const extension = Extension + .create({ + addOptions() { + return { + foo: 1, + bar: 1, + } + }, + }) + .configure({ + bar: 2, + }) + + expect(extension.options).to.deep.eq({ + foo: 1, + bar: 2, + }) + }) + + it('should be extendable (deprecated)', () => { const extension = Extension.create({ defaultOptions: { foo: 1, @@ -74,7 +129,33 @@ describe('extension options', () => { }) }) - it('should be overwritable', () => { + it('should be extendable', () => { + const extension = Extension.create({ + addOptions() { + return { + foo: 1, + bar: 1, + } + }, + }) + + const newExtension = extension.extend({ + addOptions() { + return { + ...this.parent?.(), + baz: 1, + } + }, + }) + + expect(newExtension.options).to.deep.eq({ + foo: 1, + bar: 1, + baz: 1, + }) + }) + + it('should be overwritable (deprecated)', () => { const extension = Extension .create({ defaultOptions: { @@ -93,7 +174,30 @@ describe('extension options', () => { }) }) - it('should configure nested objects', () => { + it('should be overwritable', () => { + const extension = Extension + .create({ + addOptions() { + return { + foo: 1, + bar: 1, + } + }, + }) + .extend({ + addOptions() { + return { + baz: 1, + } + }, + }) + + expect(extension.options).to.deep.eq({ + baz: 1, + }) + }) + + it('should configure nested objects (deprecated)', () => { const extension = Extension .create<{ foo: number[], @@ -122,7 +226,38 @@ describe('extension options', () => { }) }) - it('should create its own instance on configure', () => { + it('should configure nested objects', () => { + const extension = Extension + .create<{ + foo: number[], + HTMLAttributes: Record, + }>({ + addOptions() { + return { + foo: [1, 2, 3], + HTMLAttributes: { + class: 'foo', + }, + } + }, + }) + .configure({ + foo: [1], + HTMLAttributes: { + id: 'bar', + }, + }) + + expect(extension.options).to.deep.eq({ + foo: [1], + HTMLAttributes: { + class: 'foo', + id: 'bar', + }, + }) + }) + + it('should create its own instance on configure (deprecated)', () => { const extension = Extension .create({ defaultOptions: { @@ -150,4 +285,35 @@ describe('extension options', () => { bar: 2, }) }) + + it('should create its own instance on configure', () => { + const extension = Extension + .create({ + addOptions() { + return { + foo: 1, + bar: 2, + } + }, + }) + + const extension1 = extension.configure({ + foo: 2, + bar: 4, + }) + + const extension2 = extension.configure({ + foo: 3, + }) + + expect(extension1.options).to.deep.eq({ + foo: 2, + bar: 4, + }) + + expect(extension2.options).to.deep.eq({ + foo: 3, + bar: 2, + }) + }) })