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:
Diffstat (limited to 'app/assets/javascripts/content_editor')
-rw-r--r--app/assets/javascripts/content_editor/components/bubble_menus/code_block_bubble_menu.vue4
-rw-r--r--app/assets/javascripts/content_editor/components/content_editor.vue14
-rw-r--r--app/assets/javascripts/content_editor/components/formatting_toolbar.vue (renamed from app/assets/javascripts/content_editor/components/top_toolbar.vue)4
-rw-r--r--app/assets/javascripts/content_editor/components/suggestions_dropdown.vue4
-rw-r--r--app/assets/javascripts/content_editor/components/toolbar_more_dropdown.vue3
-rw-r--r--app/assets/javascripts/content_editor/extensions/code_block_highlight.js4
-rw-r--r--app/assets/javascripts/content_editor/extensions/comment.js49
-rw-r--r--app/assets/javascripts/content_editor/extensions/image.js18
-rw-r--r--app/assets/javascripts/content_editor/extensions/reference_label.js2
-rw-r--r--app/assets/javascripts/content_editor/services/create_content_editor.js2
-rw-r--r--app/assets/javascripts/content_editor/services/gl_api_markdown_deserializer.js5
-rw-r--r--app/assets/javascripts/content_editor/services/markdown_serializer.js3
-rw-r--r--app/assets/javascripts/content_editor/services/serialization_helpers.js27
13 files changed, 122 insertions, 17 deletions
diff --git a/app/assets/javascripts/content_editor/components/bubble_menus/code_block_bubble_menu.vue b/app/assets/javascripts/content_editor/components/bubble_menus/code_block_bubble_menu.vue
index a9668ebdb69..98b7203778f 100644
--- a/app/assets/javascripts/content_editor/components/bubble_menus/code_block_bubble_menu.vue
+++ b/app/assets/javascripts/content_editor/components/bubble_menus/code_block_bubble_menu.vue
@@ -166,9 +166,7 @@ export default {
icon="arrow-left"
@click.prevent.stop="showCustomLanguageInput = false"
/>
- <p
- class="gl-text-center gl-new-dropdown-header-top gl-mb-0! gl-border-none! gl-pb-1!"
- >
+ <p class="gl-text-center gl-dropdown-header-top gl-mb-0! gl-border-none! gl-pb-1!">
{{ __('Create custom type') }}
</p>
</div>
diff --git a/app/assets/javascripts/content_editor/components/content_editor.vue b/app/assets/javascripts/content_editor/components/content_editor.vue
index 22381377389..53a37fc0c51 100644
--- a/app/assets/javascripts/content_editor/components/content_editor.vue
+++ b/app/assets/javascripts/content_editor/components/content_editor.vue
@@ -11,7 +11,7 @@ import FormattingBubbleMenu from './bubble_menus/formatting_bubble_menu.vue';
import CodeBlockBubbleMenu from './bubble_menus/code_block_bubble_menu.vue';
import LinkBubbleMenu from './bubble_menus/link_bubble_menu.vue';
import MediaBubbleMenu from './bubble_menus/media_bubble_menu.vue';
-import TopToolbar from './top_toolbar.vue';
+import FormattingToolbar from './formatting_toolbar.vue';
import LoadingIndicator from './loading_indicator.vue';
export default {
@@ -20,7 +20,7 @@ export default {
ContentEditorAlert,
ContentEditorProvider,
TiptapEditorContent,
- TopToolbar,
+ FormattingToolbar,
FormattingBubbleMenu,
CodeBlockBubbleMenu,
LinkBubbleMenu,
@@ -57,6 +57,11 @@ export default {
default: false,
validator: (autofocus) => TIPTAP_AUTOFOCUS_OPTIONS.includes(autofocus),
},
+ useBottomToolbar: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -163,8 +168,8 @@ export default {
class="md-area"
:class="{ 'is-focused': focused }"
>
- <top-toolbar ref="toolbar" class="gl-mb-4" />
- <div class="gl-relative">
+ <formatting-toolbar v-if="!useBottomToolbar" ref="toolbar" class="gl-border-b" />
+ <div class="gl-relative gl-mt-4">
<formatting-bubble-menu />
<code-block-bubble-menu />
<link-bubble-menu />
@@ -176,6 +181,7 @@ export default {
/>
<loading-indicator v-if="isLoading" />
</div>
+ <formatting-toolbar v-if="useBottomToolbar" ref="toolbar" class="gl-border-t" />
</div>
</div>
</content-editor-provider>
diff --git a/app/assets/javascripts/content_editor/components/top_toolbar.vue b/app/assets/javascripts/content_editor/components/formatting_toolbar.vue
index 460368b6a11..8a25ad3fd96 100644
--- a/app/assets/javascripts/content_editor/components/top_toolbar.vue
+++ b/app/assets/javascripts/content_editor/components/formatting_toolbar.vue
@@ -24,9 +24,7 @@ export default {
};
</script>
<template>
- <div
- class="gl-display-flex gl-flex-wrap gl-pb-3 gl-pt-3 gl-border-b-solid gl-border-b-1 gl-border-b-gray-200"
- >
+ <div class="gl-display-flex gl-flex-wrap gl-pb-3 gl-pt-3">
<toolbar-text-style-dropdown
data-testid="text-styles"
class="gl-mr-3"
diff --git a/app/assets/javascripts/content_editor/components/suggestions_dropdown.vue b/app/assets/javascripts/content_editor/components/suggestions_dropdown.vue
index 001b34a00fa..37e6ef61d50 100644
--- a/app/assets/javascripts/content_editor/components/suggestions_dropdown.vue
+++ b/app/assets/javascripts/content_editor/components/suggestions_dropdown.vue
@@ -210,10 +210,10 @@ export default {
<template>
<ul
:class="{ show: items.length > 0 }"
- class="gl-new-dropdown dropdown-menu gl-relative"
+ class="gl-dropdown dropdown-menu gl-relative"
data-testid="content-editor-suggestions-dropdown"
>
- <div class="gl-new-dropdown-inner gl-overflow-y-auto">
+ <div class="gl-dropdown-inner gl-overflow-y-auto">
<gl-dropdown-item
v-for="(item, index) in items"
ref="dropdownItems"
diff --git a/app/assets/javascripts/content_editor/components/toolbar_more_dropdown.vue b/app/assets/javascripts/content_editor/components/toolbar_more_dropdown.vue
index 6bb122153ef..93b31ea7d20 100644
--- a/app/assets/javascripts/content_editor/components/toolbar_more_dropdown.vue
+++ b/app/assets/javascripts/content_editor/components/toolbar_more_dropdown.vue
@@ -58,6 +58,9 @@ export default {
right
lazy
>
+ <gl-dropdown-item @click="insert('comment')">
+ {{ __('Comment') }}
+ </gl-dropdown-item>
<gl-dropdown-item @click="insert('codeBlock')">
{{ __('Code block') }}
</gl-dropdown-item>
diff --git a/app/assets/javascripts/content_editor/extensions/code_block_highlight.js b/app/assets/javascripts/content_editor/extensions/code_block_highlight.js
index 27432b1e18b..1d85bfcc965 100644
--- a/app/assets/javascripts/content_editor/extensions/code_block_highlight.js
+++ b/app/assets/javascripts/content_editor/extensions/code_block_highlight.js
@@ -23,6 +23,10 @@ export default CodeBlockLowlight.extend({
// eslint-disable-next-line @gitlab/require-i18n-strings
default: 'code highlight',
},
+ langParams: {
+ default: null,
+ parseHTML: (element) => element.dataset.langParams,
+ },
};
},
addInputRules() {
diff --git a/app/assets/javascripts/content_editor/extensions/comment.js b/app/assets/javascripts/content_editor/extensions/comment.js
new file mode 100644
index 00000000000..8e247e552a3
--- /dev/null
+++ b/app/assets/javascripts/content_editor/extensions/comment.js
@@ -0,0 +1,49 @@
+import { Node, textblockTypeInputRule } from '@tiptap/core';
+
+export const commentInputRegex = /^<!--[\s\n]$/;
+
+export default Node.create({
+ name: 'comment',
+ content: 'text*',
+ marks: '',
+ group: 'block',
+ code: true,
+ isolating: true,
+ defining: true,
+
+ parseHTML() {
+ return [
+ {
+ tag: 'comment',
+ preserveWhitespace: 'full',
+ getContent(element, schema) {
+ const node = schema.node('paragraph', {}, [
+ schema.text(
+ element.textContent.replace(/&#x([0-9A-F]{2,4});/gi, (_, code) =>
+ String.fromCharCode(parseInt(code, 16)),
+ ) || ' ',
+ ),
+ ]);
+ return node.content;
+ },
+ },
+ ];
+ },
+
+ renderHTML() {
+ return [
+ 'pre',
+ { class: 'gl-p-0 gl-border-0 gl-bg-transparent gl-text-gray-300' },
+ ['span', { class: 'content-editor-comment' }, 0],
+ ];
+ },
+
+ addInputRules() {
+ return [
+ textblockTypeInputRule({
+ find: commentInputRegex,
+ type: this.type,
+ }),
+ ];
+ },
+});
diff --git a/app/assets/javascripts/content_editor/extensions/image.js b/app/assets/javascripts/content_editor/extensions/image.js
index 65849ec4d0d..fc4c108b773 100644
--- a/app/assets/javascripts/content_editor/extensions/image.js
+++ b/app/assets/javascripts/content_editor/extensions/image.js
@@ -52,6 +52,22 @@ export default Image.extend({
return img.getAttribute('title');
},
},
+ width: {
+ default: null,
+ parseHTML: (element) => {
+ const img = resolveImageEl(element);
+
+ return img.getAttribute('width');
+ },
+ },
+ height: {
+ default: null,
+ parseHTML: (element) => {
+ const img = resolveImageEl(element);
+
+ return img.getAttribute('height');
+ },
+ },
isReference: {
default: false,
renderHTML: () => '',
@@ -76,6 +92,8 @@ export default Image.extend({
src: HTMLAttributes.src,
alt: HTMLAttributes.alt,
title: HTMLAttributes.title,
+ width: HTMLAttributes.width,
+ height: HTMLAttributes.height,
},
];
},
diff --git a/app/assets/javascripts/content_editor/extensions/reference_label.js b/app/assets/javascripts/content_editor/extensions/reference_label.js
index 716e191c3d5..9dff0b7a689 100644
--- a/app/assets/javascripts/content_editor/extensions/reference_label.js
+++ b/app/assets/javascripts/content_editor/extensions/reference_label.js
@@ -1,5 +1,5 @@
import { VueNodeViewRenderer } from '@tiptap/vue-2';
-import { SCOPED_LABEL_DELIMITER } from '~/vue_shared/components/sidebar/labels_select_widget/constants';
+import { SCOPED_LABEL_DELIMITER } from '~/sidebar/components/labels/labels_select_widget/constants';
import LabelWrapper from '../components/wrappers/label.vue';
import Reference from './reference';
diff --git a/app/assets/javascripts/content_editor/services/create_content_editor.js b/app/assets/javascripts/content_editor/services/create_content_editor.js
index ba9ce705c62..61c6be574d0 100644
--- a/app/assets/javascripts/content_editor/services/create_content_editor.js
+++ b/app/assets/javascripts/content_editor/services/create_content_editor.js
@@ -10,6 +10,7 @@ import BulletList from '../extensions/bullet_list';
import Code from '../extensions/code';
import CodeBlockHighlight from '../extensions/code_block_highlight';
import ColorChip from '../extensions/color_chip';
+import Comment from '../extensions/comment';
import DescriptionItem from '../extensions/description_item';
import DescriptionList from '../extensions/description_list';
import Details from '../extensions/details';
@@ -100,6 +101,7 @@ export const createContentEditor = ({
BulletList,
Code,
ColorChip,
+ Comment,
CodeBlockHighlight,
DescriptionItem,
DescriptionList,
diff --git a/app/assets/javascripts/content_editor/services/gl_api_markdown_deserializer.js b/app/assets/javascripts/content_editor/services/gl_api_markdown_deserializer.js
index fa46bd9ff81..796dc06ad93 100644
--- a/app/assets/javascripts/content_editor/services/gl_api_markdown_deserializer.js
+++ b/app/assets/javascripts/content_editor/services/gl_api_markdown_deserializer.js
@@ -1,4 +1,5 @@
import { DOMParser as ProseMirrorDOMParser } from 'prosemirror-model';
+import { replaceCommentsWith } from '~/lib/utils/dom_utils';
export default ({ render }) => {
/**
@@ -22,7 +23,9 @@ export default ({ render }) => {
if (!html) return {};
const parser = new DOMParser();
- const { body } = parser.parseFromString(html, 'text/html');
+ const { body } = parser.parseFromString(`<body>${html}</body>`, 'text/html');
+
+ replaceCommentsWith(body, 'comment');
// append original source as a comment that nodes can access
body.append(document.createComment(markdown));
diff --git a/app/assets/javascripts/content_editor/services/markdown_serializer.js b/app/assets/javascripts/content_editor/services/markdown_serializer.js
index 958c27c281a..4e29f85004b 100644
--- a/app/assets/javascripts/content_editor/services/markdown_serializer.js
+++ b/app/assets/javascripts/content_editor/services/markdown_serializer.js
@@ -12,6 +12,7 @@ import DescriptionItem from '../extensions/description_item';
import DescriptionList from '../extensions/description_list';
import Details from '../extensions/details';
import DetailsContent from '../extensions/details_content';
+import Comment from '../extensions/comment';
import Diagram from '../extensions/diagram';
import Emoji from '../extensions/emoji';
import Figure from '../extensions/figure';
@@ -50,6 +51,7 @@ import Text from '../extensions/text';
import Video from '../extensions/video';
import WordBreak from '../extensions/word_break';
import {
+ renderComment,
renderCodeBlock,
renderHardBreak,
renderTable,
@@ -130,6 +132,7 @@ const defaultSerializerConfig = {
}),
[BulletList.name]: preserveUnchanged(renderBulletList),
[CodeBlockHighlight.name]: preserveUnchanged(renderCodeBlock),
+ [Comment.name]: renderComment,
[Diagram.name]: preserveUnchanged(renderCodeBlock),
[DescriptionList.name]: renderHTMLNode('dl', true),
[DescriptionItem.name]: (state, node, parent, index) => {
diff --git a/app/assets/javascripts/content_editor/services/serialization_helpers.js b/app/assets/javascripts/content_editor/services/serialization_helpers.js
index 5c0cb21075a..131c79357bf 100644
--- a/app/assets/javascripts/content_editor/services/serialization_helpers.js
+++ b/app/assets/javascripts/content_editor/services/serialization_helpers.js
@@ -308,7 +308,7 @@ export function renderHardBreak(state, node, parent, index) {
}
export function renderImage(state, node) {
- const { alt, canonicalSrc, src, title, isReference } = node.attrs;
+ const { alt, canonicalSrc, src, title, width, height, isReference } = node.attrs;
if (isString(src) || isString(canonicalSrc)) {
const quotedTitle = title ? ` ${state.quote(title)}` : '';
@@ -316,7 +316,17 @@ export function renderImage(state, node) {
? `[${canonicalSrc}]`
: `(${state.esc(canonicalSrc || src)}${quotedTitle})`;
- state.write(`![${state.esc(alt || '')}]${sourceExpression}`);
+ const sizeAttributes = [];
+ if (width) {
+ sizeAttributes.push(`width=${JSON.stringify(width)}`);
+ }
+ if (height) {
+ sizeAttributes.push(`height=${JSON.stringify(height)}`);
+ }
+
+ const attributes = sizeAttributes.length ? `{${sizeAttributes.join(' ')}}` : '';
+
+ state.write(`![${state.esc(alt || '')}]${sourceExpression}${attributes}`);
}
}
@@ -324,8 +334,19 @@ export function renderPlayable(state, node) {
renderImage(state, node);
}
+export function renderComment(state, node) {
+ state.text('<!--');
+ state.text(node.textContent);
+ state.text('-->');
+ state.closeBlock(node);
+}
+
export function renderCodeBlock(state, node) {
- state.write(`\`\`\`${node.attrs.language || ''}\n`);
+ state.write(
+ `\`\`\`${
+ (node.attrs.language || '') + (node.attrs.langParams ? `:${node.attrs.langParams}` : '')
+ }\n`,
+ );
state.text(node.textContent, false);
state.ensureNewLine();
state.write('```');