Update tiptap link insert (#368)

Update tiptap link insert
This commit is contained in:
Ilya Y 2024-01-22 13:45:46 +03:00 committed by GitHub
parent 8a8abd3652
commit df8ee62112
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 116 additions and 4 deletions

View File

@ -46,6 +46,8 @@ import { Figcaption } from './extensions/Figcaption'
import { Figure } from './extensions/Figure' import { Figure } from './extensions/Figure'
import { Footnote } from './extensions/Footnote' import { Footnote } from './extensions/Footnote'
import { Iframe } from './extensions/Iframe' import { Iframe } from './extensions/Iframe'
import { Span } from './extensions/Span'
import { ToggleTextWrap } from './extensions/ToggleTextWrap'
import { TrailingNode } from './extensions/TrailingNode' import { TrailingNode } from './extensions/TrailingNode'
import { TextBubbleMenu } from './TextBubbleMenu' import { TextBubbleMenu } from './TextBubbleMenu'
@ -201,6 +203,8 @@ export const Editor = (props: Props) => {
CustomBlockquote, CustomBlockquote,
Bold, Bold,
Italic, Italic,
Span,
ToggleTextWrap,
Strike, Strike,
HorizontalRule.configure({ HorizontalRule.configure({
HTMLAttributes: { HTMLAttributes: {
@ -208,7 +212,10 @@ export const Editor = (props: Props) => {
}, },
}), }),
Underline, Underline,
Link.configure({ Link.extend({
inclusive: false,
}).configure({
autolink: true,
openOnClick: false, openOnClick: false,
}), }),
Heading.configure({ Heading.configure({
@ -244,6 +251,7 @@ export const Editor = (props: Props) => {
Figure, Figure,
Figcaption, Figcaption,
Footnote, Footnote,
ToggleTextWrap,
CharacterCount.configure(), // https://github.com/ueberdosis/tiptap/issues/2589#issuecomment-1093084689 CharacterCount.configure(), // https://github.com/ueberdosis/tiptap/issues/2589#issuecomment-1093084689
BubbleMenu.configure({ BubbleMenu.configure({
pluginKey: 'textBubbleMenu', pluginKey: 'textBubbleMenu',
@ -252,6 +260,9 @@ export const Editor = (props: Props) => {
const { doc, selection } = state const { doc, selection } = state
const { empty } = selection const { empty } = selection
const isEmptyTextBlock = doc.textBetween(from, to).length === 0 && isTextSelection(selection) const isEmptyTextBlock = doc.textBetween(from, to).length === 0 && isTextSelection(selection)
if (isEmptyTextBlock) {
e.chain().focus().removeTextWrap({ class: 'highlight-fake-selection' }).run()
}
setIsCommonMarkup(e.isActive('figcaption')) setIsCommonMarkup(e.isActive('figcaption'))
const result = const result =
(view.hasFocus() && (view.hasFocus() &&

View File

@ -311,3 +311,10 @@ footnote {
background-color: unset; background-color: unset;
} }
} }
.highlight-fake-selection {
background: var(--selection-background);
color: var(--selection-color);
border: solid var(--selection-background);
border-width: 5px 0;
}

View File

@ -117,7 +117,10 @@ const SimplifiedEditor = (props: Props) => {
Paragraph, Paragraph,
Bold, Bold,
Italic, Italic,
Link.configure({ Link.extend({
inclusive: false,
}).configure({
autolink: true,
openOnClick: false, openOnClick: false,
}), }),
CharacterCount.configure({ CharacterCount.configure({

View File

@ -129,11 +129,21 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
}) })
}) })
const handleOpenLinkForm = () => {
props.editor.chain().focus().addTextWrap({ class: 'highlight-fake-selection' }).run()
setLinkEditorOpen(true)
}
const handleCloseLinkForm = () => {
setLinkEditorOpen(false)
props.editor.chain().focus().removeTextWrap({ class: 'highlight-fake-selection' }).run()
}
return ( return (
<div ref={props.ref} class={clsx(styles.TextBubbleMenu, { [styles.growWidth]: footnoteEditorOpen() })}> <div ref={props.ref} class={clsx(styles.TextBubbleMenu, { [styles.growWidth]: footnoteEditorOpen() })}>
<Switch> <Switch>
<Match when={linkEditorOpen()}> <Match when={linkEditorOpen()}>
<InsertLinkForm editor={props.editor} onClose={() => setLinkEditorOpen(false)} /> <InsertLinkForm editor={props.editor} onClose={handleCloseLinkForm} />
</Match> </Match>
<Match when={footnoteEditorOpen()}> <Match when={footnoteEditorOpen()}>
<SimplifiedEditor <SimplifiedEditor
@ -329,7 +339,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
<button <button
ref={triggerRef} ref={triggerRef}
type="button" type="button"
onClick={() => setLinkEditorOpen(true)} onClick={handleOpenLinkForm}
class={clsx(styles.bubbleMenuButton, { class={clsx(styles.bubbleMenuButton, {
[styles.bubbleMenuButtonActive]: isLink(), [styles.bubbleMenuButtonActive]: isLink(),
})} })}

View File

@ -0,0 +1,31 @@
import { Mark, mergeAttributes } from '@tiptap/core'
export const Span = Mark.create({
name: 'span',
parseHTML() {
return [
{
tag: 'span[class]',
getAttrs: (dom) => {
if (dom instanceof HTMLElement) {
return { class: dom.getAttribute('class') }
}
return false
},
},
]
},
renderHTML({ HTMLAttributes }) {
return ['span', mergeAttributes(HTMLAttributes), 0]
},
addAttributes() {
return {
class: {
default: null,
},
}
},
})

View File

@ -0,0 +1,50 @@
import { Extension } from '@tiptap/core'
declare module '@tiptap/core' {
interface Commands<ReturnType> {
toggleSpanWrap: {
addTextWrap: (attributes: { class: string }) => ReturnType
removeTextWrap: (attributes: { class: string }) => ReturnType
}
}
}
export const ToggleTextWrap = Extension.create({
name: 'toggleTextWrap',
addCommands() {
return {
addTextWrap:
(attributes) =>
({ commands, state }) => {
return commands.setMark('span', attributes)
},
removeTextWrap:
(attributes) =>
({ state, dispatch }) => {
let tr = state.tr
let changesApplied = false
state.doc.descendants((node, pos) => {
if (node.isInline) {
node.marks.forEach((mark) => {
if (mark.type.name === 'span' && mark.attrs.class === attributes.class) {
const end = pos + node.nodeSize
tr = tr.removeMark(pos, end, mark.type)
changesApplied = true
}
})
}
})
if (changesApplied) {
dispatch(tr)
return true
} else {
return false
}
},
}
},
})