diff options
author | Julius Härtl <jus@bitgrid.net> | 2019-06-15 13:39:11 +0300 |
---|---|---|
committer | Julius Härtl <jus@bitgrid.net> | 2019-06-15 13:39:11 +0300 |
commit | 1824f1065c953b0f2f178cf553a6797508659633 (patch) | |
tree | 8bae6c7ea2271c1d9887fff6934f1694f336d9d2 | |
parent | 1bf1660e84c84039036851cea627573cf77c9494 (diff) |
Implement custom image node with placeholder and alt text editing
Signed-off-by: Julius Härtl <jus@bitgrid.net>
-rw-r--r-- | src/EditorFactory.js | 2 | ||||
-rw-r--r-- | src/components/EditorWrapper.vue | 2 | ||||
-rw-r--r-- | src/components/MenuBar.vue | 2 | ||||
-rw-r--r-- | src/nodes/Image.js | 39 | ||||
-rw-r--r-- | src/nodes/ImageView.vue | 157 | ||||
-rw-r--r-- | src/nodes/index.js | 27 |
6 files changed, 226 insertions, 3 deletions
diff --git a/src/EditorFactory.js b/src/EditorFactory.js index 167b3a6dc..468a9bf99 100644 --- a/src/EditorFactory.js +++ b/src/EditorFactory.js @@ -31,10 +31,10 @@ import { Blockquote, CodeBlock, HorizontalRule, - Image, History } from 'tiptap-extensions' import { Strong, Italic, Strike } from './marks' +import { Image } from './nodes' import MarkdownIt from 'markdown-it' import { MarkdownSerializer, defaultMarkdownSerializer } from 'prosemirror-markdown' diff --git a/src/components/EditorWrapper.vue b/src/components/EditorWrapper.vue index 8d6f0e97f..591fecc27 100644 --- a/src/components/EditorWrapper.vue +++ b/src/components/EditorWrapper.vue @@ -269,7 +269,7 @@ export default { }) this.syncService.state = this.tiptap.state this.$emit('update:loaded', true) - this.tiptap.focus('end') + this.tiptap.focus('start') }) .on('sync', ({ steps, document }) => { try { diff --git a/src/components/MenuBar.vue b/src/components/MenuBar.vue index e1ce9c8ee..f37ee054c 100644 --- a/src/components/MenuBar.vue +++ b/src/components/MenuBar.vue @@ -100,7 +100,7 @@ export default { const _command = command OC.dialogs.filepicker('Insert an image', (file) => { const src = OC.generateUrl('/core/preview.png?') + `file=${file}&x=1024&y=1024&a=true` - _command({ src }) + _command({ src, alt: file }) // TODO: check permissions // TODO: check for available preview }, false, false, true) diff --git a/src/nodes/Image.js b/src/nodes/Image.js new file mode 100644 index 000000000..bd0e39c25 --- /dev/null +++ b/src/nodes/Image.js @@ -0,0 +1,39 @@ +/* + * @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> + * + * @author Julius Härtl <jus@bitgrid.net> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +import { Image as TiptapImage } from 'tiptap-extensions' +import ImageView from './ImageView' + +export default class Image extends TiptapImage { + + get view() { + return ImageView + } + + get schema() { + return { + ...super.schema, + selectable: false + } + } + +} diff --git a/src/nodes/ImageView.vue b/src/nodes/ImageView.vue new file mode 100644 index 000000000..3b837c5b2 --- /dev/null +++ b/src/nodes/ImageView.vue @@ -0,0 +1,157 @@ +<!-- + - @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> + - + - @author Julius Härtl <jus@bitgrid.net> + - + - @license GNU AGPL version 3 or any later version + - + - This program is free software: you can redistribute it and/or modify + - it under the terms of the GNU Affero General Public License as + - published by the Free Software Foundation, either version 3 of the + - License, or (at your option) any later version. + - + - This program is distributed in the hope that it will be useful, + - but WITHOUT ANY WARRANTY; without even the implied warranty of + - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + - GNU Affero General Public License for more details. + - + - You should have received a copy of the GNU Affero General Public License + - along with this program. If not, see <http://www.gnu.org/licenses/>. + - + --> + +<template> + <div class="image" :class="{'icon-loading': !loaded}"> + <div v-if="imageLoaded"> + <transition name="fade"> + <img v-show="loaded" :src="src" + class="image__main" @load="onLoaded"> + </transition> + <transition name="fade"> + <div v-show="loaded" class="image__caption"> + <input ref="altInput" type="text" :value="alt" + @keyup.enter="updateAlt()"> + </div> + </transition> + </div> + <div v-else class="image__placeholder"> + <transition name="fade"> + <div v-show="loaded" class="image__main"> + <div class="icon-image" /> + <p>{{ t('text', 'Insufficient permissions to view image') }}</p> + </div> + </transition> + <transition name="fade"> + <div v-show="loaded" class="image__caption"> + <input ref="altInput" type="text" :value="alt" + @keyup.enter="updateAlt()"> + </div> + </transition> + </div> + </div> +</template> + +<script> +export default { + name: 'ImageView', + props: ['node', 'updateAttrs', 'view'], // eslint-disable-line + data() { + return { + imageLoaded: false, + loaded: false, + failed: false + } + }, + computed: { + src: { + get() { + return this.node.attrs.src + }, + set(src) { + this.updateAttrs({ + src + }) + } + }, + alt: { + get() { + return this.node.attrs.alt ? this.node.attrs.alt : '' + }, + set(alt) { + this.updateAttrs({ + alt + }) + } + }, + t() { + return (a, s) => window.t(a, s) + } + }, + beforeMount() { + var img = new Image() + img.src = this.node.attrs.src + img.onload = () => { + this.imageLoaded = true + } + img.onerror = () => { + this.failed = true + this.imageLoaded = false + this.loaded = true + } + }, + methods: { + updateAlt() { + this.alt = this.$refs.altInput.value + }, + onLoaded() { + this.loaded = true + } + } +} +</script> + +<style scoped lang="scss"> + .image { + border: 1px solid transparent !important; + &:hover { + border: 1px solid var(--color-background-darker) !important; + } + } + .image__caption { + text-align: center; + color: var(--color-text-lighter); + input[type="text"] { + width: 100%; + border: none; + text-align: center; + } + } + .icon-image { + margin-top: 10px; + height: 32px; + padding: 20px; + } + .image__loading { + height: 100px; + } + .image__placeholder .image__main { + background-color: var(--color-background-dark); + text-align: center; + padding: 20px; + .icon-image { + opacity: 0.7; + } + } + + .fade-enter-active { + transition: opacity .3s ease-in-out; + } + + .fade-enter-to { + opacity: 1; + } + + .fade-enter { + opacity: 0; + } +</style> diff --git a/src/nodes/index.js b/src/nodes/index.js new file mode 100644 index 000000000..b51a01d03 --- /dev/null +++ b/src/nodes/index.js @@ -0,0 +1,27 @@ +/* + * @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> + * + * @author Julius Härtl <jus@bitgrid.net> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +import Image from './Image' + +export { + Image +} |