Star
You must be signed in to star a gist
Resizable images using TipTap Editor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<div class="tiptap-content"> | |
<editor-content :editor="editor" /> | |
</div> | |
</template> | |
<script> | |
import { | |
Editor, | |
EditorContent | |
} from 'tiptap'; | |
import TipTapCustomImage from './TipTapImage.js'; | |
export default { | |
components: { | |
EditorContent | |
}, | |
data() { | |
return { | |
editor: null | |
} | |
}, | |
mounted() { | |
this.editor = new Editor({ | |
content: `<p>This is a paragraph</p><p><img src="https://i.ibb.co/nbRN3S2/undraw-upload-87y9.png" /></p>`, | |
extensions: [ | |
new TipTapCustomImage() | |
] | |
}); | |
}, | |
beforeDestroy() { | |
this.editor.destroy(); | |
} | |
} | |
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Node, Plugin } from 'tiptap'; | |
import { nodeInputRule } from 'tiptap-commands'; | |
import TipTapImageComponent from '~/components/editor/TipTapImageComponent'; | |
const IMAGE_INPUT_REGEX = /!\[(.+|:?)]\((\S+)(?:(?:\s+)["'](\S+)["'])?\)/; | |
export default class CustomImage extends Node { | |
get name() { | |
return 'image' | |
} | |
get schema() { | |
return { | |
inline: true, | |
attrs: { | |
src: {}, | |
alt: { | |
default: null, | |
}, | |
title: { | |
default: null, | |
}, | |
width: { | |
default: 300, | |
}, | |
height: { | |
default: 300 | |
} | |
}, | |
group: 'inline', | |
content: 'inline*', | |
draggable: false, | |
parseDOM: [ | |
{ | |
tag: 'img[src]', | |
getAttrs: dom => ({ | |
src: dom.getAttribute('src'), | |
title: dom.getAttribute('title'), | |
alt: dom.getAttribute('alt'), | |
height: dom.getAttribute('height') || 300, | |
width: dom.getAttribute('width') || 300 | |
}), | |
}, | |
], | |
toDOM: (node) => { | |
return ['img', { | |
src: node.attrs.src, | |
height: node.attrs.height, | |
width: node.attrs.width, | |
alt: node.attrs.alt, | |
title: node.attrs.title | |
}, 0]; | |
}, | |
} | |
} | |
commands({ type }) { | |
return attrs => (state, dispatch) => { | |
const { selection } = state; | |
const position = selection.$cursor ? selection.$cursor.pos : selection.$to.pos; | |
const node = type.create(attrs); | |
const transaction = state.tr.insert(position, node); | |
dispatch(transaction); | |
} | |
} | |
inputRules(context) { | |
const { type } = context; | |
return [ | |
nodeInputRule(IMAGE_INPUT_REGEX, type, match => { | |
const [, alt, src, title, height, width] = match; | |
return { | |
src, | |
alt, | |
title, | |
height, | |
width | |
} | |
}), | |
]; | |
} | |
get plugins() { | |
return [ | |
new Plugin({ | |
props: { | |
handleDOMEvents: { | |
drop(view, event) { | |
// I don't want to allow this | |
return false; | |
} | |
} | |
}, | |
}), | |
] | |
} | |
get view() { | |
return TipTapImageComponent; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<div class="tiptap-custom-image-container"> | |
<vue-draggable-resizable :w="width" :h="height" @resizestop="onResize" :draggable="false" :lock-aspect-ratio="true"> | |
<div :style="`background-image:url('${src}');background-size:cover;background-repeat:no-repeat;position:absolute;top:0;left:0;right:0;bottom:0;`"></div> | |
</vue-draggable-resizable> | |
</div> | |
</template> | |
<script> | |
import VueDraggableResizable from 'vue-draggable-resizable'; | |
import 'vue-draggable-resizable/dist/VueDraggableResizable.css'; | |
export default { | |
props: ['node', 'updateAttrs', 'view', 'selected', 'getPos', 'options'], | |
components: { | |
'vue-draggable-resizable': VueDraggableResizable | |
}, | |
computed: { | |
src: { | |
get() { | |
return this.node.attrs.src; | |
}, | |
set(src) { | |
this.updateAttrs({src}); | |
} | |
}, | |
width: { | |
get() { | |
return parseInt(this.node.attrs.width); | |
}, | |
set(width) { | |
this.updateAttrs({ | |
width: width | |
}); | |
} | |
}, | |
height: { | |
get() { | |
return parseInt(this.node.attrs.height); | |
}, | |
set(height) { | |
this.updateAttrs({ | |
height: height | |
}); | |
} | |
} | |
}, | |
methods: { | |
onResize(x, y, width, height) { | |
this.width = width; | |
this.height = height; | |
} | |
} | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment