From 839a547152d2a758e6c3a107f0f1e93ae9bd1b92 Mon Sep 17 00:00:00 2001 From: Ricardo Amaral Date: Mon, 19 Jun 2023 16:12:56 +0100 Subject: [PATCH] fix: Extra paragraph node inserted above an Horizontal Rule --- .../rich-text/rich-text-horizontal-rule.ts | 38 +++++++++++++++++++ src/extensions/rich-text/rich-text-kit.ts | 8 ++-- .../typist-editor-decorator.module.css | 4 ++ 3 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 src/extensions/rich-text/rich-text-horizontal-rule.ts diff --git a/src/extensions/rich-text/rich-text-horizontal-rule.ts b/src/extensions/rich-text/rich-text-horizontal-rule.ts new file mode 100644 index 00000000..6746e5aa --- /dev/null +++ b/src/extensions/rich-text/rich-text-horizontal-rule.ts @@ -0,0 +1,38 @@ +import { InputRule } from '@tiptap/core' +import { HorizontalRule } from '@tiptap/extension-horizontal-rule' + +import type { HorizontalRuleOptions } from '@tiptap/extension-horizontal-rule' + +/** + * The input regex for Markdown horizontal rules. + */ +const inputRegex = /^(?:---|—-|___\s|\*\*\*\s)$/ + +/** + * Custom extension that extends the built-in `HorizontalRule` extension to fix an issue with the + * built-in input rule that adds extra paragraph node above the horizontal rule. + * + * @see https://github.com/ueberdosis/tiptap/issues/3809 + * @see https://github.com/ueberdosis/tiptap/pull/3859#issuecomment-1536799740 + */ +const RichTextHorizontalRule = HorizontalRule.extend({ + addInputRules() { + const { type } = this + + return [ + new InputRule({ + find: inputRegex, + handler({ state: { tr }, range }) { + tr.insert(range.from - 1, type.create({})).delete( + tr.mapping.map(range.from), + tr.mapping.map(range.to), + ) + }, + }), + ] + }, +}) + +export { RichTextHorizontalRule } + +export type { HorizontalRuleOptions as RichTextHorizontalRuleOptions } diff --git a/src/extensions/rich-text/rich-text-kit.ts b/src/extensions/rich-text/rich-text-kit.ts index 10d8a257..98c34fd8 100644 --- a/src/extensions/rich-text/rich-text-kit.ts +++ b/src/extensions/rich-text/rich-text-kit.ts @@ -9,7 +9,6 @@ import { Gapcursor } from '@tiptap/extension-gapcursor' import { HardBreak } from '@tiptap/extension-hard-break' import { Heading } from '@tiptap/extension-heading' import { History } from '@tiptap/extension-history' -import { HorizontalRule } from '@tiptap/extension-horizontal-rule' import { Italic } from '@tiptap/extension-italic' import { ListItem } from '@tiptap/extension-list-item' import { OrderedList } from '@tiptap/extension-ordered-list' @@ -28,6 +27,7 @@ import { CurvenoteCodemark } from './curvenote-codemark' import { PasteEmojis } from './paste-emojis' import { PasteMarkdown } from './paste-markdown' import { RichTextDocument } from './rich-text-document' +import { RichTextHorizontalRule } from './rich-text-horizontal-rule' import { RichTextImage } from './rich-text-image' import { RichTextLink } from './rich-text-link' @@ -41,13 +41,13 @@ import type { DropcursorOptions } from '@tiptap/extension-dropcursor' import type { HardBreakOptions } from '@tiptap/extension-hard-break' import type { HeadingOptions } from '@tiptap/extension-heading' import type { HistoryOptions } from '@tiptap/extension-history' -import type { HorizontalRuleOptions } from '@tiptap/extension-horizontal-rule' import type { ItalicOptions } from '@tiptap/extension-italic' import type { ListItemOptions } from '@tiptap/extension-list-item' import type { OrderedListOptions } from '@tiptap/extension-ordered-list' import type { ParagraphOptions } from '@tiptap/extension-paragraph' import type { StrikeOptions } from '@tiptap/extension-strike' import type { RichTextDocumentOptions } from './rich-text-document' +import type { RichTextHorizontalRuleOptions } from './rich-text-horizontal-rule' import type { RichTextImageOptions } from './rich-text-image' import type { RichTextLinkOptions } from './rich-text-link' @@ -113,7 +113,7 @@ type RichTextKitOptions = { /** * Set options for the `HorizontalRule` extension, or `false` to disable. */ - horizontalRule: Partial | false + horizontalRule: Partial | false /** * Set options for the `Image` extension, or `false` to disable. @@ -276,7 +276,7 @@ const RichTextKit = Extension.create({ } if (this.options.horizontalRule !== false) { - extensions.push(HorizontalRule.configure(this.options?.horizontalRule)) + extensions.push(RichTextHorizontalRule.configure(this.options?.horizontalRule)) } if (this.options.image !== false) { diff --git a/stories/typist-editor/decorators/typist-editor-decorator/typist-editor-decorator.module.css b/stories/typist-editor/decorators/typist-editor-decorator/typist-editor-decorator.module.css index 8cee3931..78ddd00c 100644 --- a/stories/typist-editor/decorators/typist-editor-decorator/typist-editor-decorator.module.css +++ b/stories/typist-editor/decorators/typist-editor-decorator/typist-editor-decorator.module.css @@ -116,6 +116,10 @@ div + .editorContainer { margin: 0; } +:global(div[data-typist-editor] p + hr) { + margin-top: 8px; +} + :global(div[data-typist-editor] :is(pre, code)) { font-family: var(--storybook-theme-fontCode); }