Merge branch 'main' of github.com:ueberdosis/tiptap-next into main
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="editor">
|
<div v-if="editor">
|
||||||
|
<button @click="addImage">
|
||||||
|
image
|
||||||
|
</button>
|
||||||
<editor-content :editor="editor" />
|
<editor-content :editor="editor" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -23,6 +26,14 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
addImage() {
|
||||||
|
const url = window.prompt('URL')
|
||||||
|
|
||||||
|
this.editor.chain().focus().image({ src: url }).run()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.editor = new Editor({
|
this.editor = new Editor({
|
||||||
extensions: [
|
extensions: [
|
||||||
@@ -33,7 +44,7 @@ export default {
|
|||||||
],
|
],
|
||||||
content: `
|
content: `
|
||||||
<p>This is basic example of implementing images. Try to drop new images here. Reordering also works.</p>
|
<p>This is basic example of implementing images. Try to drop new images here. Reordering also works.</p>
|
||||||
<img src="https://66.media.tumblr.com/dcd3d24b79d78a3ee0f9192246e727f1/tumblr_o00xgqMhPM1qak053o1_400.gif" />
|
<img src="https://source.unsplash.com/8xznAGy4HcY/800x600" />
|
||||||
`,
|
`,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -109,6 +109,11 @@ code {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
margin: 1rem 0;
|
margin: 1rem 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Command, createNode, nodeInputRule } from '@tiptap/core'
|
import { Command, createNode, nodeInputRule } from '@tiptap/core'
|
||||||
import { Plugin } from 'prosemirror-state'
|
|
||||||
|
|
||||||
const IMAGE_INPUT_REGEX = /!\[(.+|:?)]\((\S+)(?:(?:\s+)["'](\S+)["'])?\)/
|
export const inputRegex = /!\[(.+|:?)]\((\S+)(?:(?:\s+)["'](\S+)["'])?\)/
|
||||||
|
|
||||||
const Image = createNode({
|
const Image = createNode({
|
||||||
name: 'image',
|
name: 'image',
|
||||||
@@ -10,6 +9,8 @@ const Image = createNode({
|
|||||||
|
|
||||||
group: 'inline',
|
group: 'inline',
|
||||||
|
|
||||||
|
draggable: true,
|
||||||
|
|
||||||
addAttributes() {
|
addAttributes() {
|
||||||
return {
|
return {
|
||||||
src: {
|
src: {
|
||||||
@@ -38,13 +39,11 @@ const Image = createNode({
|
|||||||
|
|
||||||
addCommands() {
|
addCommands() {
|
||||||
return {
|
return {
|
||||||
image: (attrs: any): Command => ({ tr }) => {
|
image: (options: { src: string, alt?: string, title?: string }): Command => ({ tr }) => {
|
||||||
const { selection } = tr
|
const { selection } = tr
|
||||||
console.log({ selection })
|
const node = this.type.create(options)
|
||||||
// const position = selection.$cursor ? selection.$cursor.pos : selection.$to.pos
|
|
||||||
const position = selection.$anchor ? selection.$anchor.pos : selection.$to.pos
|
tr.replaceRangeWith(selection.from, selection.to, node)
|
||||||
const node = this.type.create(attrs)
|
|
||||||
tr.insert(position, node)
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
@@ -53,64 +52,10 @@ const Image = createNode({
|
|||||||
|
|
||||||
addInputRules() {
|
addInputRules() {
|
||||||
return [
|
return [
|
||||||
nodeInputRule(IMAGE_INPUT_REGEX, this.type, match => {
|
nodeInputRule(inputRegex, this.type, match => {
|
||||||
const [, alt, src, title] = match
|
const [, alt, src, title] = match
|
||||||
return {
|
|
||||||
src,
|
|
||||||
alt,
|
|
||||||
title,
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
addProseMirrorPlugins() {
|
return { src, alt, title }
|
||||||
return [
|
|
||||||
new Plugin({
|
|
||||||
props: {
|
|
||||||
handleDOMEvents: {
|
|
||||||
drop(view, event) {
|
|
||||||
const hasFiles = event.dataTransfer
|
|
||||||
&& event.dataTransfer.files
|
|
||||||
&& event.dataTransfer.files.length
|
|
||||||
|
|
||||||
if (!hasFiles) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const images = Array
|
|
||||||
// @ts-ignore
|
|
||||||
.from(event.dataTransfer.files)
|
|
||||||
.filter(file => (/image/i).test(file.type))
|
|
||||||
|
|
||||||
if (images.length === 0) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
event.preventDefault()
|
|
||||||
|
|
||||||
const { schema } = view.state
|
|
||||||
const coordinates = view.posAtCoords({ left: event.clientX, top: event.clientY })
|
|
||||||
|
|
||||||
images.forEach(image => {
|
|
||||||
const reader = new FileReader()
|
|
||||||
|
|
||||||
reader.onload = readerEvent => {
|
|
||||||
const node = schema.nodes.image.create({
|
|
||||||
// @ts-ignore
|
|
||||||
src: readerEvent.target.result,
|
|
||||||
})
|
|
||||||
// @ts-ignore
|
|
||||||
const transaction = view.state.tr.insert(coordinates.pos, node)
|
|
||||||
view.dispatch(transaction)
|
|
||||||
}
|
|
||||||
reader.readAsDataURL(image)
|
|
||||||
})
|
|
||||||
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user