Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-04-14 18:08:59 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-04-14 18:08:59 +0300
commit9b762f50fee09b50b97b5ab208a9a62522447c8c (patch)
tree4dbd16c66f6aeacc1b88c1e3350df09ce4f91183 /app/assets/javascripts/content_editor
parent9769ccf613ec45634ee32efaf1c39763a759a917 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/content_editor')
-rw-r--r--app/assets/javascripts/content_editor/components/wrappers/image.vue32
-rw-r--r--app/assets/javascripts/content_editor/components/wrappers/media.vue51
-rw-r--r--app/assets/javascripts/content_editor/extensions/image.js4
-rw-r--r--app/assets/javascripts/content_editor/extensions/playable.js11
-rw-r--r--app/assets/javascripts/content_editor/services/upload_helpers.js24
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 });