diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-04-14 18:08:59 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-04-14 18:08:59 +0300 |
commit | 9b762f50fee09b50b97b5ab208a9a62522447c8c (patch) | |
tree | 4dbd16c66f6aeacc1b88c1e3350df09ce4f91183 /app/assets/javascripts/content_editor | |
parent | 9769ccf613ec45634ee32efaf1c39763a759a917 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/content_editor')
5 files changed, 81 insertions, 41 deletions
diff --git a/app/assets/javascripts/content_editor/components/wrappers/image.vue b/app/assets/javascripts/content_editor/components/wrappers/image.vue deleted file mode 100644 index 5b81e5fddcc..00000000000 --- a/app/assets/javascripts/content_editor/components/wrappers/image.vue +++ /dev/null @@ -1,32 +0,0 @@ -<script> -import { GlLoadingIcon } from '@gitlab/ui'; -import { NodeViewWrapper } from '@tiptap/vue-2'; - -export default { - name: 'ImageWrapper', - components: { - NodeViewWrapper, - GlLoadingIcon, - }, - props: { - node: { - type: Object, - required: true, - }, - }, -}; -</script> -<template> - <node-view-wrapper class="gl-display-inline-block"> - <span class="gl-relative"> - <img - data-testid="image" - class="gl-max-w-full gl-h-auto" - :title="node.attrs.title" - :class="{ 'gl-opacity-5': node.attrs.uploading }" - :src="node.attrs.src" - /> - <gl-loading-icon v-if="node.attrs.uploading" class="gl-absolute gl-left-50p gl-top-half" /> - </span> - </node-view-wrapper> -</template> diff --git a/app/assets/javascripts/content_editor/components/wrappers/media.vue b/app/assets/javascripts/content_editor/components/wrappers/media.vue new file mode 100644 index 00000000000..37119bdd066 --- /dev/null +++ b/app/assets/javascripts/content_editor/components/wrappers/media.vue @@ -0,0 +1,51 @@ +<script> +import { GlLoadingIcon } from '@gitlab/ui'; +import { NodeViewWrapper } from '@tiptap/vue-2'; + +const tagNameMap = { + image: 'img', + video: 'video', + audio: 'audio', +}; + +export default { + name: 'MediaWrapper', + components: { + NodeViewWrapper, + GlLoadingIcon, + }, + props: { + node: { + type: Object, + required: true, + }, + }, + computed: { + tagName() { + return tagNameMap[this.node.type.name] || 'img'; + }, + }, +}; +</script> +<template> + <node-view-wrapper class="gl-display-inline-block"> + <span class="gl-relative" :class="{ [`media-container ${tagName}-container`]: true }"> + <gl-loading-icon v-if="node.attrs.uploading" class="gl-absolute gl-left-50p gl-top-half" /> + <component + :is="tagName" + data-testid="media" + :class="{ + 'gl-max-w-full gl-h-auto': tagName !== 'audio', + 'gl-opacity-5': node.attrs.uploading, + }" + :title="node.attrs.title || node.attrs.alt" + :alt="node.attrs.alt" + :src="node.attrs.src" + controls="true" + /> + <a v-if="tagName !== 'img'" :href="node.attrs.canonicalSrc || node.attrs.src" @click.prevent> + {{ node.attrs.title || node.attrs.alt }} + </a> + </span> + </node-view-wrapper> +</template> diff --git a/app/assets/javascripts/content_editor/extensions/image.js b/app/assets/javascripts/content_editor/extensions/image.js index 519f7f168ce..311db8151cb 100644 --- a/app/assets/javascripts/content_editor/extensions/image.js +++ b/app/assets/javascripts/content_editor/extensions/image.js @@ -1,6 +1,6 @@ import { Image } from '@tiptap/extension-image'; import { VueNodeViewRenderer } from '@tiptap/vue-2'; -import ImageWrapper from '../components/wrappers/image.vue'; +import MediaWrapper from '../components/wrappers/media.vue'; import { PARSE_HTML_PRIORITY_HIGHEST } from '../constants'; const resolveImageEl = (element) => @@ -78,6 +78,6 @@ export default Image.extend({ ]; }, addNodeView() { - return VueNodeViewRenderer(ImageWrapper); + return VueNodeViewRenderer(MediaWrapper); }, }); diff --git a/app/assets/javascripts/content_editor/extensions/playable.js b/app/assets/javascripts/content_editor/extensions/playable.js index 0062bc563db..2c5269377c5 100644 --- a/app/assets/javascripts/content_editor/extensions/playable.js +++ b/app/assets/javascripts/content_editor/extensions/playable.js @@ -1,6 +1,8 @@ /* eslint-disable @gitlab/require-i18n-strings */ import { Node } from '@tiptap/core'; +import { VueNodeViewRenderer } from '@tiptap/vue-2'; +import MediaWrapper from '../components/wrappers/media.vue'; const queryPlayableElement = (element, mediaType) => element.querySelector(mediaType); @@ -11,6 +13,9 @@ export default Node.create({ addAttributes() { return { + uploading: { + default: false, + }, src: { default: null, parseHTML: (element) => { @@ -60,7 +65,11 @@ export default Node.create({ ...this.extraElementAttrs, }, ], - ['a', { href: node.attrs.src }, node.attrs.alt], + ['a', { href: node.attrs.src }, node.attrs.title || node.attrs.alt || ''], ]; }, + + addNodeView() { + return VueNodeViewRenderer(MediaWrapper); + }, }); diff --git a/app/assets/javascripts/content_editor/services/upload_helpers.js b/app/assets/javascripts/content_editor/services/upload_helpers.js index 1abecb8f414..ed2c4b39131 100644 --- a/app/assets/javascripts/content_editor/services/upload_helpers.js +++ b/app/assets/javascripts/content_editor/services/upload_helpers.js @@ -5,6 +5,16 @@ import { extractFilename, readFileAsDataURL } from './utils'; export const acceptedMimes = { image: ['image/jpeg', 'image/png', 'image/gif', 'image/jpg'], + audio: [ + 'audio/basic', + 'audio/mid', + 'audio/mpeg', + 'audio/x-aiff', + 'audio/ogg', + 'audio/vorbis', + 'audio/vnd.wav', + ], + video: ['video/mp4', 'video/quicktime'], }; const extractAttachmentLinkUrl = (html) => { @@ -50,11 +60,11 @@ export const uploadFile = async ({ uploadsPath, renderMarkdown, file }) => { return extractAttachmentLinkUrl(rendered); }; -const uploadImage = async ({ editor, file, uploadsPath, renderMarkdown, eventHub }) => { +const uploadContent = async ({ type, editor, file, uploadsPath, renderMarkdown, eventHub }) => { const encodedSrc = await readFileAsDataURL(file); const { view } = editor; - editor.commands.setImage({ uploading: true, src: encodedSrc }); + editor.commands.insertContent({ type, attrs: { uploading: true, src: encodedSrc } }); const { state } = view; const position = state.selection.from - 1; @@ -74,7 +84,7 @@ const uploadImage = async ({ editor, file, uploadsPath, renderMarkdown, eventHub } catch (e) { editor.commands.deleteRange({ from: position, to: position + 1 }); eventHub.$emit('alert', { - message: __('An error occurred while uploading the image. Please try again.'), + message: __('An error occurred while uploading the file. Please try again.'), variant: VARIANT_DANGER, }); } @@ -114,10 +124,12 @@ const uploadAttachment = async ({ editor, file, uploadsPath, renderMarkdown, eve export const handleFileEvent = ({ editor, file, uploadsPath, renderMarkdown, eventHub }) => { if (!file) return false; - if (acceptedMimes.image.includes(file?.type)) { - uploadImage({ editor, file, uploadsPath, renderMarkdown, eventHub }); + for (const [type, mimes] of Object.entries(acceptedMimes)) { + if (mimes.includes(file?.type)) { + uploadContent({ type, editor, file, uploadsPath, renderMarkdown, eventHub }); - return true; + return true; + } } uploadAttachment({ editor, file, uploadsPath, renderMarkdown, eventHub }); |