Files
Website/resources/js/editor/TipTap.js
2026-01-01 09:01:15 +10:00

175 lines
6.2 KiB
JavaScript

import Link from "@tiptap/extension-link";
import {Editor} from "@tiptap/core";
import StarterKit from "@tiptap/starter-kit";
import Underline from "@tiptap/extension-underline";
import Highlight from "@tiptap/extension-highlight";
import TextAlign from "@tiptap/extension-text-align";
import Typography from "@tiptap/extension-typography";
import {ColorHighlighter} from "./ColourHighter.js";
import {SmileyReplacer} from "./SmileyReplacer.js";
import {Small} from "./Small.js";
import {ExtraSmall} from "./ExtraSmall.js";
import {Box} from "./Box.js";
const editorToggleLink = (editor) => {
const previousUrl = editor.getAttributes('link').href
const url = window.prompt('URL', previousUrl)
if (url === null) {
return
}
if (url === '') {
editor.chain().focus().extendMarkRange('link').unsetLink().run()
return
}
editor.chain().focus().extendMarkRange('link').setLink({ href: url }).run()
}
const CustomLink = Link.extend({
addKeyboardShortcuts() {
return {
'Mod-k': () => editorToggleLink(this.editor),
'Mod-Alt-4': () => this.editor.chain().setSmall().focus().run(),
'Mod-Alt-5': () => this.editor.chain().setExtraSmall().focus().run(),
}
}
});
document.addEventListener('alpine:init', () => {
Alpine.data('editor', (content) => {
let editor // Alpine's reactive engine automatically wraps component properties in proxy objects. Attempting to use a proxied editor instance to apply a transaction will cause a "Range Error: Applying a mismatched transaction", so be sure to unwrap it using Alpine.raw(), or simply avoid storing your editor as a component property, as shown in this example.
return {
updatedAt: Date.now(), // force Alpine to rerender on selection change
content: SM.decodeHtml(content),
init() {
const _this = this
editor = new Editor({
element: this.$refs.element,
extensions: [
StarterKit.configure({
link: false,
underline: false,
}),
Highlight,
CustomLink.configure({
rel: 'noopener noreferrer',
openOnClick: 'whenNotEditable',
}),
Underline,
TextAlign.configure({
types: ['heading', 'paragraph', 'small', 'extraSmall'],
}),
Typography,
ColorHighlighter,
SmileyReplacer,
Small,
ExtraSmall,
Box
],
editorProps: {
attributes: {
class: 'tiptap content',
},
},
content: content,
onCreate({/* editor */}) {
_this.updatedAt = Date.now()
},
onUpdate({editor}) {
_this.updatedAt = Date.now()
_this.content = editor.getHTML()
},
onSelectionUpdate({/* editor */}) {
_this.updatedAt = Date.now()
}
})
},
isLoaded() {
return editor
},
isActive(type, opts = {}) {
return editor.isActive(type, opts)
},
toggleHeading(opts) {
editor.chain().toggleHeading(opts).focus().run()
},
toggleBold() {
editor.chain().toggleBold().focus().run()
},
toggleItalic() {
editor.chain().toggleItalic().focus().run()
},
toggleUnderline() {
editor.chain().toggleUnderline().focus().run()
},
toggleStrike() {
editor.chain().toggleStrike().focus().run()
},
setParagraph() {
editor.chain().setParagraph().focus().run()
},
toggleCode() {
editor.chain().toggleCode().focus().run()
},
toggleBulletList() {
editor.chain().toggleBulletList().focus().run()
},
toggleOrderedList() {
editor.chain().toggleOrderedList().focus().run()
},
toggleBlockquote() {
editor.chain().toggleBlockquote().focus().run()
},
toggleCodeBlock() {
editor.chain().toggleCodeBlock().focus().run()
},
toggleLink() {
editorToggleLink(editor)
},
clearLink() {
editor.chain().focus().extendMarkRange('link').unsetLink().run()
},
toggleHighlight() {
editor.chain().toggleHighlight().focus().run()
},
toggleSubscript() {
editor.chain().toggleSubscript().focus().run()
},
toggleSuperscript() {
editor.chain().toggleSuperscript().focus().run()
},
undo() {
editor.chain().undo().focus().run()
},
redo() {
editor.chain().redo().focus().run()
},
unsetAllMarks() {
editor.chain().focus().unsetAllMarks().run()
},
clearNodes() {
editor.chain().focus().clearNodes().run()
},
clearNotes() {
editor.chain().focus().clearNodes().run()
},
setTextAlign(value) {
editor.chain().setTextAlign(value).focus().run()
},
setSmall() {
editor.chain().focus().setSmall().run()
},
setExtraSmall() {
editor.chain().focus().setExtraSmall().run()
},
toggleBox(opts) {
editor.chain().toggleBox(opts).focus().run()
},
}
})
})