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>2021-09-20 16:18:24 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-09-20 16:18:24 +0300
commit0653e08efd039a5905f3fa4f6e9cef9f5d2f799c (patch)
tree4dcc884cf6d81db44adae4aa99f8ec1233a41f55 /app/assets/javascripts/content_editor/extensions
parent744144d28e3e7fddc117924fef88de5d9674fe4c (diff)
Add latest changes from gitlab-org/gitlab@14-3-stable-eev14.3.0-rc42
Diffstat (limited to 'app/assets/javascripts/content_editor/extensions')
-rw-r--r--app/assets/javascripts/content_editor/extensions/audio.js9
-rw-r--r--app/assets/javascripts/content_editor/extensions/blockquote.js34
-rw-r--r--app/assets/javascripts/content_editor/extensions/bullet_list.js20
-rw-r--r--app/assets/javascripts/content_editor/extensions/code_block_highlight.js6
-rw-r--r--app/assets/javascripts/content_editor/extensions/description_item.js49
-rw-r--r--app/assets/javascripts/content_editor/extensions/description_list.js23
-rw-r--r--app/assets/javascripts/content_editor/extensions/division.js17
-rw-r--r--app/assets/javascripts/content_editor/extensions/emoji.js18
-rw-r--r--app/assets/javascripts/content_editor/extensions/figure.js16
-rw-r--r--app/assets/javascripts/content_editor/extensions/figure_caption.js16
-rw-r--r--app/assets/javascripts/content_editor/extensions/html_marks.js66
-rw-r--r--app/assets/javascripts/content_editor/extensions/image.js23
-rw-r--r--app/assets/javascripts/content_editor/extensions/inline_diff.js6
-rw-r--r--app/assets/javascripts/content_editor/extensions/link.js16
-rw-r--r--app/assets/javascripts/content_editor/extensions/ordered_list.js16
-rw-r--r--app/assets/javascripts/content_editor/extensions/playable.js66
-rw-r--r--app/assets/javascripts/content_editor/extensions/reference.js41
-rw-r--r--app/assets/javascripts/content_editor/extensions/subscript.js10
-rw-r--r--app/assets/javascripts/content_editor/extensions/superscript.js10
-rw-r--r--app/assets/javascripts/content_editor/extensions/table_cell.js9
-rw-r--r--app/assets/javascripts/content_editor/extensions/table_header.js8
-rw-r--r--app/assets/javascripts/content_editor/extensions/task_item.js6
-rw-r--r--app/assets/javascripts/content_editor/extensions/task_list.js28
-rw-r--r--app/assets/javascripts/content_editor/extensions/video.js10
24 files changed, 432 insertions, 91 deletions
diff --git a/app/assets/javascripts/content_editor/extensions/audio.js b/app/assets/javascripts/content_editor/extensions/audio.js
new file mode 100644
index 00000000000..25d4068c93f
--- /dev/null
+++ b/app/assets/javascripts/content_editor/extensions/audio.js
@@ -0,0 +1,9 @@
+import Playable from './playable';
+
+export default Playable.extend({
+ name: 'audio',
+ defaultOptions: {
+ ...Playable.options,
+ mediaType: 'audio',
+ },
+});
diff --git a/app/assets/javascripts/content_editor/extensions/blockquote.js b/app/assets/javascripts/content_editor/extensions/blockquote.js
index 45f53fe230b..4512ead44bc 100644
--- a/app/assets/javascripts/content_editor/extensions/blockquote.js
+++ b/app/assets/javascripts/content_editor/extensions/blockquote.js
@@ -1 +1,33 @@
-export { Blockquote as default } from '@tiptap/extension-blockquote';
+import { Blockquote } from '@tiptap/extension-blockquote';
+import { wrappingInputRule } from 'prosemirror-inputrules';
+import { getParents } from '~/lib/utils/dom_utils';
+import { getMarkdownSource } from '../services/markdown_sourcemap';
+
+export const multilineInputRegex = /^\s*>>>\s$/gm;
+
+export default Blockquote.extend({
+ addAttributes() {
+ return {
+ ...this.parent?.(),
+
+ multiline: {
+ default: false,
+ parseHTML: (element) => {
+ const source = getMarkdownSource(element);
+ const parentsIncludeBlockquote = getParents(element).some(
+ (p) => p.nodeName.toLowerCase() === 'blockquote',
+ );
+
+ return source && !source.startsWith('>') && !parentsIncludeBlockquote;
+ },
+ },
+ };
+ },
+
+ addInputRules() {
+ return [
+ ...this.parent?.(),
+ wrappingInputRule(multilineInputRegex, this.type, () => ({ multiline: true })),
+ ];
+ },
+});
diff --git a/app/assets/javascripts/content_editor/extensions/bullet_list.js b/app/assets/javascripts/content_editor/extensions/bullet_list.js
index 01ead571fe1..8d0faf7a9fe 100644
--- a/app/assets/javascripts/content_editor/extensions/bullet_list.js
+++ b/app/assets/javascripts/content_editor/extensions/bullet_list.js
@@ -1 +1,19 @@
-export { BulletList as default } from '@tiptap/extension-bullet-list';
+import { BulletList } from '@tiptap/extension-bullet-list';
+import { getMarkdownSource } from '../services/markdown_sourcemap';
+
+export default BulletList.extend({
+ addAttributes() {
+ return {
+ ...this.parent?.(),
+
+ bullet: {
+ default: '*',
+ parseHTML(element) {
+ const bullet = getMarkdownSource(element)?.charAt(0);
+
+ return '*+-'.includes(bullet) ? bullet : '*';
+ },
+ },
+ };
+ },
+});
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 c6d32fb8547..25f5837d2a6 100644
--- a/app/assets/javascripts/content_editor/extensions/code_block_highlight.js
+++ b/app/assets/javascripts/content_editor/extensions/code_block_highlight.js
@@ -8,11 +8,7 @@ export default CodeBlockLowlight.extend({
return {
language: {
default: null,
- parseHTML: (element) => {
- return {
- language: extractLanguage(element),
- };
- },
+ parseHTML: (element) => extractLanguage(element),
},
class: {
default: 'code highlight js-syntax-highlight',
diff --git a/app/assets/javascripts/content_editor/extensions/description_item.js b/app/assets/javascripts/content_editor/extensions/description_item.js
new file mode 100644
index 00000000000..957fdede27b
--- /dev/null
+++ b/app/assets/javascripts/content_editor/extensions/description_item.js
@@ -0,0 +1,49 @@
+import { Node, mergeAttributes } from '@tiptap/core';
+
+export default Node.create({
+ name: 'descriptionItem',
+ content: 'block+',
+ defining: true,
+
+ addAttributes() {
+ return {
+ isTerm: {
+ default: true,
+ parseHTML: (element) => element.tagName.toLowerCase() === 'dt',
+ },
+ };
+ },
+
+ parseHTML() {
+ return [{ tag: 'dt' }, { tag: 'dd' }];
+ },
+
+ renderHTML({ HTMLAttributes: { isTerm, ...HTMLAttributes } }) {
+ return [
+ 'li',
+ mergeAttributes(HTMLAttributes, { class: isTerm ? 'dl-term' : 'dl-description' }),
+ 0,
+ ];
+ },
+
+ addKeyboardShortcuts() {
+ return {
+ Enter: () => {
+ return this.editor.commands.splitListItem('descriptionItem');
+ },
+ Tab: () => {
+ const { isTerm } = this.editor.getAttributes('descriptionItem');
+ if (isTerm)
+ return this.editor.commands.updateAttributes('descriptionItem', { isTerm: !isTerm });
+
+ return false;
+ },
+ 'Shift-Tab': () => {
+ const { isTerm } = this.editor.getAttributes('descriptionItem');
+ if (isTerm) return this.editor.commands.liftListItem('descriptionItem');
+
+ return this.editor.commands.updateAttributes('descriptionItem', { isTerm: true });
+ },
+ };
+ },
+});
diff --git a/app/assets/javascripts/content_editor/extensions/description_list.js b/app/assets/javascripts/content_editor/extensions/description_list.js
new file mode 100644
index 00000000000..a516dfad2b8
--- /dev/null
+++ b/app/assets/javascripts/content_editor/extensions/description_list.js
@@ -0,0 +1,23 @@
+import { Node, mergeAttributes } from '@tiptap/core';
+import { wrappingInputRule } from 'prosemirror-inputrules';
+
+export const inputRegex = /^\s*(<dl>)$/;
+
+export default Node.create({
+ name: 'descriptionList',
+ // eslint-disable-next-line @gitlab/require-i18n-strings
+ group: 'block list',
+ content: 'descriptionItem+',
+
+ parseHTML() {
+ return [{ tag: 'dl' }];
+ },
+
+ renderHTML({ HTMLAttributes }) {
+ return ['ul', mergeAttributes(HTMLAttributes, { class: 'dl-content' }), 0];
+ },
+
+ addInputRules() {
+ return [wrappingInputRule(inputRegex, this.type)];
+ },
+});
diff --git a/app/assets/javascripts/content_editor/extensions/division.js b/app/assets/javascripts/content_editor/extensions/division.js
new file mode 100644
index 00000000000..c70d1700941
--- /dev/null
+++ b/app/assets/javascripts/content_editor/extensions/division.js
@@ -0,0 +1,17 @@
+import { Node } from '@tiptap/core';
+import { PARSE_HTML_PRIORITY_LOWEST } from '../constants';
+
+export default Node.create({
+ name: 'division',
+ content: 'block*',
+ group: 'block',
+ defining: true,
+
+ parseHTML() {
+ return [{ tag: 'div', priority: PARSE_HTML_PRIORITY_LOWEST }];
+ },
+
+ renderHTML({ HTMLAttributes }) {
+ return ['div', HTMLAttributes, 0];
+ },
+});
diff --git a/app/assets/javascripts/content_editor/extensions/emoji.js b/app/assets/javascripts/content_editor/extensions/emoji.js
index d88b9f92215..de608c3aaa2 100644
--- a/app/assets/javascripts/content_editor/extensions/emoji.js
+++ b/app/assets/javascripts/content_editor/extensions/emoji.js
@@ -17,30 +17,18 @@ export default Node.create({
return {
moji: {
default: null,
- parseHTML: (element) => {
- return {
- moji: element.textContent,
- };
- },
+ parseHTML: (element) => element.textContent,
},
name: {
default: null,
- parseHTML: (element) => {
- return {
- name: element.dataset.name,
- };
- },
+ parseHTML: (element) => element.dataset.name,
},
title: {
default: null,
},
unicodeVersion: {
default: '6.0',
- parseHTML: (element) => {
- return {
- unicodeVersion: element.dataset.unicodeVersion,
- };
- },
+ parseHTML: (element) => element.dataset.unicodeVersion,
},
};
},
diff --git a/app/assets/javascripts/content_editor/extensions/figure.js b/app/assets/javascripts/content_editor/extensions/figure.js
new file mode 100644
index 00000000000..b2076894412
--- /dev/null
+++ b/app/assets/javascripts/content_editor/extensions/figure.js
@@ -0,0 +1,16 @@
+import { Node } from '@tiptap/core';
+
+export default Node.create({
+ name: 'figure',
+ content: 'block+',
+ group: 'block',
+ defining: true,
+
+ parseHTML() {
+ return [{ tag: 'figure' }];
+ },
+
+ renderHTML({ HTMLAttributes }) {
+ return ['figure', HTMLAttributes, 0];
+ },
+});
diff --git a/app/assets/javascripts/content_editor/extensions/figure_caption.js b/app/assets/javascripts/content_editor/extensions/figure_caption.js
new file mode 100644
index 00000000000..ffd1b474f03
--- /dev/null
+++ b/app/assets/javascripts/content_editor/extensions/figure_caption.js
@@ -0,0 +1,16 @@
+import { Node } from '@tiptap/core';
+
+export default Node.create({
+ name: 'figureCaption',
+ content: 'inline*',
+ group: 'block',
+ defining: true,
+
+ parseHTML() {
+ return [{ tag: 'figcaption' }];
+ },
+
+ renderHTML({ HTMLAttributes }) {
+ return ['figcaption', HTMLAttributes, 0];
+ },
+});
diff --git a/app/assets/javascripts/content_editor/extensions/html_marks.js b/app/assets/javascripts/content_editor/extensions/html_marks.js
new file mode 100644
index 00000000000..54adb9efa0c
--- /dev/null
+++ b/app/assets/javascripts/content_editor/extensions/html_marks.js
@@ -0,0 +1,66 @@
+import { Mark, mergeAttributes, markInputRule } from '@tiptap/core';
+import { PARSE_HTML_PRIORITY_LOWEST } from '../constants';
+import { markInputRegex, extractMarkAttributesFromMatch } from '../services/mark_utils';
+
+const marks = [
+ 'ins',
+ 'abbr',
+ 'bdo',
+ 'cite',
+ 'dfn',
+ 'mark',
+ 'small',
+ 'span',
+ 'time',
+ 'kbd',
+ 'q',
+ 'samp',
+ 'var',
+ 'ruby',
+ 'rp',
+ 'rt',
+];
+
+const attrs = {
+ time: ['datetime'],
+ abbr: ['title'],
+ span: ['dir'],
+ bdo: ['dir'],
+};
+
+export default marks.map((name) =>
+ Mark.create({
+ name,
+
+ inclusive: false,
+
+ defaultOptions: {
+ HTMLAttributes: {},
+ },
+
+ addAttributes() {
+ return (attrs[name] || []).reduce(
+ (acc, attr) => ({
+ ...acc,
+ [attr]: {
+ default: null,
+ parseHTML: (element) => element.getAttribute(attr),
+ },
+ }),
+ {},
+ );
+ },
+
+ parseHTML() {
+ return [{ tag: name, priority: PARSE_HTML_PRIORITY_LOWEST }];
+ },
+
+ renderHTML({ HTMLAttributes }) {
+ return [name, mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
+ },
+
+ addInputRules() {
+ return [markInputRule(markInputRegex(name), this.type, extractMarkAttributesFromMatch)];
+ },
+ }),
+);
diff --git a/app/assets/javascripts/content_editor/extensions/image.js b/app/assets/javascripts/content_editor/extensions/image.js
index c9e8dfa4ad9..837fab0585f 100644
--- a/app/assets/javascripts/content_editor/extensions/image.js
+++ b/app/assets/javascripts/content_editor/extensions/image.js
@@ -1,6 +1,7 @@
import { Image } from '@tiptap/extension-image';
import { VueNodeViewRenderer } from '@tiptap/vue-2';
import ImageWrapper from '../components/wrappers/image.vue';
+import { PARSE_HTML_PRIORITY_HIGHEST } from '../constants';
const resolveImageEl = (element) =>
element.nodeName === 'IMG' ? element : element.querySelector('img');
@@ -27,27 +28,27 @@ export default Image.extend({
parseHTML: (element) => {
const img = resolveImageEl(element);
- return {
- src: img.dataset.src || img.getAttribute('src'),
- };
+ return img.dataset.src || img.getAttribute('src');
},
},
canonicalSrc: {
default: null,
+ parseHTML: (element) => element.dataset.canonicalSrc,
+ },
+ alt: {
+ default: null,
parseHTML: (element) => {
- return {
- canonicalSrc: element.dataset.canonicalSrc,
- };
+ const img = resolveImageEl(element);
+
+ return img.getAttribute('alt');
},
},
- alt: {
+ title: {
default: null,
parseHTML: (element) => {
const img = resolveImageEl(element);
- return {
- alt: img.getAttribute('alt'),
- };
+ return img.getAttribute('title');
},
},
};
@@ -55,7 +56,7 @@ export default Image.extend({
parseHTML() {
return [
{
- priority: 100,
+ priority: PARSE_HTML_PRIORITY_HIGHEST,
tag: 'a.no-attachment-icon',
},
{
diff --git a/app/assets/javascripts/content_editor/extensions/inline_diff.js b/app/assets/javascripts/content_editor/extensions/inline_diff.js
index 9471d324764..3bd328958df 100644
--- a/app/assets/javascripts/content_editor/extensions/inline_diff.js
+++ b/app/assets/javascripts/content_editor/extensions/inline_diff.js
@@ -14,11 +14,7 @@ export default Mark.create({
return {
type: {
default: 'addition',
- parseHTML: (element) => {
- return {
- type: element.classList.contains('deletion') ? 'deletion' : 'addition',
- };
- },
+ parseHTML: (element) => (element.classList.contains('deletion') ? 'deletion' : 'addition'),
},
};
},
diff --git a/app/assets/javascripts/content_editor/extensions/link.js b/app/assets/javascripts/content_editor/extensions/link.js
index 53104fe07a3..fc0f38e6935 100644
--- a/app/assets/javascripts/content_editor/extensions/link.js
+++ b/app/assets/javascripts/content_editor/extensions/link.js
@@ -36,19 +36,15 @@ export default Link.extend({
...this.parent?.(),
href: {
default: null,
- parseHTML: (element) => {
- return {
- href: element.getAttribute('href'),
- };
- },
+ parseHTML: (element) => element.getAttribute('href'),
+ },
+ title: {
+ title: null,
+ parseHTML: (element) => element.getAttribute('title'),
},
canonicalSrc: {
default: null,
- parseHTML: (element) => {
- return {
- canonicalSrc: element.dataset.canonicalSrc,
- };
- },
+ parseHTML: (element) => element.dataset.canonicalSrc,
},
};
},
diff --git a/app/assets/javascripts/content_editor/extensions/ordered_list.js b/app/assets/javascripts/content_editor/extensions/ordered_list.js
index 9a79187d9c1..57d5bd6ebf8 100644
--- a/app/assets/javascripts/content_editor/extensions/ordered_list.js
+++ b/app/assets/javascripts/content_editor/extensions/ordered_list.js
@@ -1 +1,15 @@
-export { OrderedList as default } from '@tiptap/extension-ordered-list';
+import { OrderedList } from '@tiptap/extension-ordered-list';
+import { getMarkdownSource } from '../services/markdown_sourcemap';
+
+export default OrderedList.extend({
+ addAttributes() {
+ return {
+ ...this.parent?.(),
+
+ parens: {
+ default: false,
+ parseHTML: (element) => /^[0-9]+\)/.test(getMarkdownSource(element)),
+ },
+ };
+ },
+});
diff --git a/app/assets/javascripts/content_editor/extensions/playable.js b/app/assets/javascripts/content_editor/extensions/playable.js
new file mode 100644
index 00000000000..0062bc563db
--- /dev/null
+++ b/app/assets/javascripts/content_editor/extensions/playable.js
@@ -0,0 +1,66 @@
+/* eslint-disable @gitlab/require-i18n-strings */
+
+import { Node } from '@tiptap/core';
+
+const queryPlayableElement = (element, mediaType) => element.querySelector(mediaType);
+
+export default Node.create({
+ group: 'inline',
+ inline: true,
+ draggable: true,
+
+ addAttributes() {
+ return {
+ src: {
+ default: null,
+ parseHTML: (element) => {
+ const playable = queryPlayableElement(element, this.options.mediaType);
+
+ return playable.src;
+ },
+ },
+ canonicalSrc: {
+ default: null,
+ parseHTML: (element) => {
+ const playable = queryPlayableElement(element, this.options.mediaType);
+
+ return playable.dataset.canonicalSrc;
+ },
+ },
+ alt: {
+ default: null,
+ parseHTML: (element) => {
+ const playable = queryPlayableElement(element, this.options.mediaType);
+
+ return playable.dataset.title;
+ },
+ },
+ };
+ },
+
+ parseHTML() {
+ return [
+ {
+ tag: `.${this.options.mediaType}-container`,
+ },
+ ];
+ },
+
+ renderHTML({ node }) {
+ return [
+ 'span',
+ { class: `media-container ${this.options.mediaType}-container` },
+ [
+ this.options.mediaType,
+ {
+ src: node.attrs.src,
+ controls: true,
+ 'data-setup': '{}',
+ 'data-title': node.attrs.alt,
+ ...this.extraElementAttrs,
+ },
+ ],
+ ['a', { href: node.attrs.src }, node.attrs.alt],
+ ];
+ },
+});
diff --git a/app/assets/javascripts/content_editor/extensions/reference.js b/app/assets/javascripts/content_editor/extensions/reference.js
index 5f4484af9c8..5e459e65de2 100644
--- a/app/assets/javascripts/content_editor/extensions/reference.js
+++ b/app/assets/javascripts/content_editor/extensions/reference.js
@@ -1,4 +1,10 @@
import { Node } from '@tiptap/core';
+import { PARSE_HTML_PRIORITY_HIGHEST } from '../constants';
+
+const getAnchor = (element) => {
+ if (element.nodeName === 'A') return element;
+ return element.querySelector('a');
+};
export default Node.create({
name: 'reference',
@@ -13,43 +19,23 @@ export default Node.create({
return {
className: {
default: null,
- parseHTML: (element) => {
- return {
- className: element.className,
- };
- },
+ parseHTML: (element) => getAnchor(element).className,
},
referenceType: {
default: null,
- parseHTML: (element) => {
- return {
- referenceType: element.dataset.referenceType,
- };
- },
+ parseHTML: (element) => getAnchor(element).dataset.referenceType,
},
originalText: {
default: null,
- parseHTML: (element) => {
- return {
- originalText: element.dataset.original,
- };
- },
+ parseHTML: (element) => getAnchor(element).dataset.original,
},
href: {
default: null,
- parseHTML: (element) => {
- return {
- href: element.getAttribute('href'),
- };
- },
+ parseHTML: (element) => getAnchor(element).getAttribute('href'),
},
text: {
default: null,
- parseHTML: (element) => {
- return {
- text: element.textContent,
- };
- },
+ parseHTML: (element) => getAnchor(element).textContent,
},
};
},
@@ -58,7 +44,10 @@ export default Node.create({
return [
{
tag: 'a.gfm:not([data-link=true])',
- priority: 51,
+ priority: PARSE_HTML_PRIORITY_HIGHEST,
+ },
+ {
+ tag: 'span.gl-label',
},
];
},
diff --git a/app/assets/javascripts/content_editor/extensions/subscript.js b/app/assets/javascripts/content_editor/extensions/subscript.js
index 4bf89796efe..d0766f42308 100644
--- a/app/assets/javascripts/content_editor/extensions/subscript.js
+++ b/app/assets/javascripts/content_editor/extensions/subscript.js
@@ -1 +1,9 @@
-export { Subscript as default } from '@tiptap/extension-subscript';
+import { markInputRule } from '@tiptap/core';
+import { Subscript } from '@tiptap/extension-subscript';
+import { markInputRegex, extractMarkAttributesFromMatch } from '../services/mark_utils';
+
+export default Subscript.extend({
+ addInputRules() {
+ return [markInputRule(markInputRegex('sub'), this.type, extractMarkAttributesFromMatch)];
+ },
+});
diff --git a/app/assets/javascripts/content_editor/extensions/superscript.js b/app/assets/javascripts/content_editor/extensions/superscript.js
index 3eb7d86d90d..6cd814977ea 100644
--- a/app/assets/javascripts/content_editor/extensions/superscript.js
+++ b/app/assets/javascripts/content_editor/extensions/superscript.js
@@ -1 +1,9 @@
-export { Superscript as default } from '@tiptap/extension-superscript';
+import { markInputRule } from '@tiptap/core';
+import { Superscript } from '@tiptap/extension-superscript';
+import { markInputRegex, extractMarkAttributesFromMatch } from '../services/mark_utils';
+
+export default Superscript.extend({
+ addInputRules() {
+ return [markInputRule(markInputRegex('sup'), this.type, extractMarkAttributesFromMatch)];
+ },
+});
diff --git a/app/assets/javascripts/content_editor/extensions/table_cell.js b/app/assets/javascripts/content_editor/extensions/table_cell.js
index 5bdc39231a1..befc33e669f 100644
--- a/app/assets/javascripts/content_editor/extensions/table_cell.js
+++ b/app/assets/javascripts/content_editor/extensions/table_cell.js
@@ -1,5 +1,12 @@
import { TableCell } from '@tiptap/extension-table-cell';
+import { VueNodeViewRenderer } from '@tiptap/vue-2';
+import TableCellBodyWrapper from '../components/wrappers/table_cell_body.vue';
+import { isBlockTablesFeatureEnabled } from '../services/feature_flags';
export default TableCell.extend({
- content: 'inline*',
+ content: isBlockTablesFeatureEnabled() ? 'block+' : 'inline*',
+
+ addNodeView() {
+ return VueNodeViewRenderer(TableCellBodyWrapper);
+ },
});
diff --git a/app/assets/javascripts/content_editor/extensions/table_header.js b/app/assets/javascripts/content_editor/extensions/table_header.js
index 23509706e4b..829b06fc14b 100644
--- a/app/assets/javascripts/content_editor/extensions/table_header.js
+++ b/app/assets/javascripts/content_editor/extensions/table_header.js
@@ -1,5 +1,11 @@
import { TableHeader } from '@tiptap/extension-table-header';
+import { VueNodeViewRenderer } from '@tiptap/vue-2';
+import TableCellHeaderWrapper from '../components/wrappers/table_cell_header.vue';
+import { isBlockTablesFeatureEnabled } from '../services/feature_flags';
export default TableHeader.extend({
- content: 'inline*',
+ content: isBlockTablesFeatureEnabled() ? 'block+' : 'inline*',
+ addNodeView() {
+ return VueNodeViewRenderer(TableCellHeaderWrapper);
+ },
});
diff --git a/app/assets/javascripts/content_editor/extensions/task_item.js b/app/assets/javascripts/content_editor/extensions/task_item.js
index 6163c0e043b..9b050edcb28 100644
--- a/app/assets/javascripts/content_editor/extensions/task_item.js
+++ b/app/assets/javascripts/content_editor/extensions/task_item.js
@@ -1,4 +1,5 @@
import { TaskItem } from '@tiptap/extension-task-item';
+import { PARSE_HTML_PRIORITY_HIGHEST } from '../constants';
export default TaskItem.extend({
defaultOptions: {
@@ -12,7 +13,8 @@ export default TaskItem.extend({
default: false,
parseHTML: (element) => {
const checkbox = element.querySelector('input[type=checkbox].task-list-item-checkbox');
- return { checked: checkbox?.checked };
+
+ return checkbox?.checked;
},
renderHTML: (attributes) => ({
'data-checked': attributes.checked,
@@ -26,7 +28,7 @@ export default TaskItem.extend({
return [
{
tag: 'li.task-list-item',
- priority: 100,
+ priority: PARSE_HTML_PRIORITY_HIGHEST,
},
];
},
diff --git a/app/assets/javascripts/content_editor/extensions/task_list.js b/app/assets/javascripts/content_editor/extensions/task_list.js
index b7f6c857bc7..72c6e020102 100644
--- a/app/assets/javascripts/content_editor/extensions/task_list.js
+++ b/app/assets/javascripts/content_editor/extensions/task_list.js
@@ -1,16 +1,24 @@
import { mergeAttributes } from '@tiptap/core';
import { TaskList } from '@tiptap/extension-task-list';
+import { PARSE_HTML_PRIORITY_HIGHEST } from '../constants';
+import { getMarkdownSource } from '../services/markdown_sourcemap';
export default TaskList.extend({
addAttributes() {
return {
- type: {
- default: 'ul',
- parseHTML: (element) => {
- return {
- type: element.tagName.toLowerCase() === 'ol' ? 'ol' : 'ul',
- };
- },
+ numeric: {
+ default: false,
+ parseHTML: (element) => element.tagName.toLowerCase() === 'ol',
+ },
+ start: {
+ default: 1,
+ parseHTML: (element) =>
+ element.hasAttribute('start') ? parseInt(element.getAttribute('start') || '', 10) : 1,
+ },
+
+ parens: {
+ default: false,
+ parseHTML: (element) => /^[0-9]+\)/.test(getMarkdownSource(element)),
},
};
},
@@ -19,12 +27,12 @@ export default TaskList.extend({
return [
{
tag: '.task-list',
- priority: 100,
+ priority: PARSE_HTML_PRIORITY_HIGHEST,
},
];
},
- renderHTML({ HTMLAttributes: { type, ...HTMLAttributes } }) {
- return [type, mergeAttributes(HTMLAttributes, { 'data-type': 'taskList' }), 0];
+ renderHTML({ HTMLAttributes: { numeric, ...HTMLAttributes } }) {
+ return [numeric ? 'ol' : 'ul', mergeAttributes(HTMLAttributes, { 'data-type': 'taskList' }), 0];
},
});
diff --git a/app/assets/javascripts/content_editor/extensions/video.js b/app/assets/javascripts/content_editor/extensions/video.js
new file mode 100644
index 00000000000..9923b7c04cd
--- /dev/null
+++ b/app/assets/javascripts/content_editor/extensions/video.js
@@ -0,0 +1,10 @@
+import Playable from './playable';
+
+export default Playable.extend({
+ name: 'video',
+ defaultOptions: {
+ ...Playable.options,
+ mediaType: 'video',
+ extraElementAttrs: { width: '400' },
+ },
+});