Created
June 29, 2020 12:56
-
-
Save zachjharris/a5442efbdff11948d085b6b1406dfbe6 to your computer and use it in GitHub Desktop.
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