Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
9fc9084df3
commit
7150920cea
73 changed files with 947 additions and 1152 deletions
|
@ -48,54 +48,48 @@ import Video from './nodes/video';
|
|||
// from GFM should have a node or mark here.
|
||||
// The GFM-to-HTML-to-GFM cycle is tested in spec/features/markdown/copy_as_gfm_spec.rb.
|
||||
|
||||
export default [
|
||||
new Doc(),
|
||||
new Paragraph(),
|
||||
new Text(),
|
||||
export default {
|
||||
nodes: [
|
||||
Doc(),
|
||||
Paragraph(),
|
||||
Text(),
|
||||
|
||||
new Blockquote(),
|
||||
new CodeBlock(),
|
||||
new HardBreak(),
|
||||
new Heading({ maxLevel: 6 }),
|
||||
new HorizontalRule(),
|
||||
new Image(),
|
||||
Blockquote(),
|
||||
CodeBlock(),
|
||||
HardBreak(),
|
||||
Heading(),
|
||||
HorizontalRule(),
|
||||
Image(),
|
||||
|
||||
new Table(),
|
||||
new TableHead(),
|
||||
new TableBody(),
|
||||
new TableHeaderRow(),
|
||||
new TableRow(),
|
||||
new TableCell(),
|
||||
Table(),
|
||||
TableHead(),
|
||||
TableBody(),
|
||||
TableHeaderRow(),
|
||||
TableRow(),
|
||||
TableCell(),
|
||||
|
||||
new Emoji(),
|
||||
new Reference(),
|
||||
Emoji(),
|
||||
Reference(),
|
||||
|
||||
new TableOfContents(),
|
||||
new Video(),
|
||||
new Audio(),
|
||||
TableOfContents(),
|
||||
Video(),
|
||||
Audio(),
|
||||
|
||||
new BulletList(),
|
||||
new OrderedList(),
|
||||
new ListItem(),
|
||||
BulletList(),
|
||||
OrderedList(),
|
||||
ListItem(),
|
||||
|
||||
new DescriptionList(),
|
||||
new DescriptionTerm(),
|
||||
new DescriptionDetails(),
|
||||
DescriptionList(),
|
||||
DescriptionTerm(),
|
||||
DescriptionDetails(),
|
||||
|
||||
new TaskList(),
|
||||
new OrderedTaskList(),
|
||||
new TaskListItem(),
|
||||
TaskList(),
|
||||
OrderedTaskList(),
|
||||
TaskListItem(),
|
||||
|
||||
new Summary(),
|
||||
new Details(),
|
||||
Summary(),
|
||||
Details(),
|
||||
],
|
||||
|
||||
new Bold(),
|
||||
new Italic(),
|
||||
new Strike(),
|
||||
new InlineDiff(),
|
||||
|
||||
new Link(),
|
||||
new Code(),
|
||||
new MathMark(),
|
||||
new InlineHTML(),
|
||||
];
|
||||
marks: [Bold(), Italic(), Strike(), InlineDiff(), Link(), Code(), MathMark(), InlineHTML()],
|
||||
};
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Bold as BaseBold } from 'tiptap-extensions';
|
||||
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
|
||||
export default class Bold extends BaseBold {
|
||||
get toMarkdown() {
|
||||
return defaultMarkdownSerializer.marks.strong;
|
||||
}
|
||||
}
|
||||
export default () => {
|
||||
return {
|
||||
name: 'bold',
|
||||
schema: {
|
||||
parseDOM: [
|
||||
{
|
||||
tag: 'strong',
|
||||
},
|
||||
],
|
||||
toDOM: () => ['strong', 0],
|
||||
},
|
||||
toMarkdown: defaultMarkdownSerializer.marks.strong,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Code as BaseCode } from 'tiptap-extensions';
|
||||
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
|
||||
export default class Code extends BaseCode {
|
||||
get toMarkdown() {
|
||||
return defaultMarkdownSerializer.marks.code;
|
||||
}
|
||||
}
|
||||
export default () => ({
|
||||
name: 'code',
|
||||
schema: {
|
||||
excludes: '_',
|
||||
parseDOM: [{ tag: 'code' }],
|
||||
toDOM: () => ['code', 0],
|
||||
},
|
||||
toMarkdown: defaultMarkdownSerializer.marks.code,
|
||||
});
|
||||
|
|
|
@ -1,41 +1,29 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Mark } from 'tiptap';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::InlineDiffFilter
|
||||
export default class InlineDiff extends Mark {
|
||||
get name() {
|
||||
return 'inline_diff';
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
attrs: {
|
||||
addition: {
|
||||
default: true,
|
||||
},
|
||||
export default () => ({
|
||||
name: 'inline_diff',
|
||||
schema: {
|
||||
attrs: {
|
||||
addition: {
|
||||
default: true,
|
||||
},
|
||||
parseDOM: [
|
||||
{ tag: 'span.idiff.addition', attrs: { addition: true } },
|
||||
{ tag: 'span.idiff.deletion', attrs: { addition: false } },
|
||||
],
|
||||
toDOM: (node) => [
|
||||
'span',
|
||||
{ class: `idiff left right ${node.attrs.addition ? 'addition' : 'deletion'}` },
|
||||
0,
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
get toMarkdown() {
|
||||
return {
|
||||
mixable: true,
|
||||
open(state, mark) {
|
||||
return mark.attrs.addition ? '{+' : '{-';
|
||||
},
|
||||
close(state, mark) {
|
||||
return mark.attrs.addition ? '+}' : '-}';
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
parseDOM: [
|
||||
{ tag: 'span.idiff.addition', attrs: { addition: true } },
|
||||
{ tag: 'span.idiff.deletion', attrs: { addition: false } },
|
||||
],
|
||||
toDOM: (node) => [
|
||||
'span',
|
||||
{ class: `idiff left right ${node.attrs.addition ? 'addition' : 'deletion'}` },
|
||||
0,
|
||||
],
|
||||
},
|
||||
toMarkdown: {
|
||||
mixable: true,
|
||||
open(_, mark) {
|
||||
return mark.attrs.addition ? '{+' : '{-';
|
||||
},
|
||||
close(_, mark) {
|
||||
return mark.attrs.addition ? '+}' : '-}';
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,46 +1,35 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { escape } from 'lodash';
|
||||
import { Mark } from 'tiptap';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
|
||||
export default class InlineHTML extends Mark {
|
||||
get name() {
|
||||
return 'inline_html';
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
excludes: '',
|
||||
attrs: {
|
||||
tag: {},
|
||||
title: { default: null },
|
||||
export default () => ({
|
||||
name: 'inline_html',
|
||||
schema: {
|
||||
excludes: '',
|
||||
attrs: {
|
||||
tag: {},
|
||||
title: { default: null },
|
||||
},
|
||||
parseDOM: [
|
||||
{
|
||||
tag: 'sup, sub, kbd, q, samp, var',
|
||||
getAttrs: (el) => ({ tag: el.nodeName.toLowerCase() }),
|
||||
},
|
||||
parseDOM: [
|
||||
{
|
||||
tag: 'sup, sub, kbd, q, samp, var',
|
||||
getAttrs: (el) => ({ tag: el.nodeName.toLowerCase() }),
|
||||
},
|
||||
{
|
||||
tag: 'abbr',
|
||||
getAttrs: (el) => ({ tag: 'abbr', title: el.getAttribute('title') }),
|
||||
},
|
||||
],
|
||||
toDOM: (node) => [node.attrs.tag, { title: node.attrs.title }, 0],
|
||||
};
|
||||
}
|
||||
|
||||
get toMarkdown() {
|
||||
return {
|
||||
mixable: true,
|
||||
open(state, mark) {
|
||||
return `<${mark.attrs.tag}${
|
||||
mark.attrs.title ? ` title="${state.esc(escape(mark.attrs.title))}"` : ''
|
||||
}>`;
|
||||
{
|
||||
tag: 'abbr',
|
||||
getAttrs: (el) => ({ tag: 'abbr', title: el.getAttribute('title') }),
|
||||
},
|
||||
close(state, mark) {
|
||||
return `</${mark.attrs.tag}>`;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
],
|
||||
toDOM: (node) => [node.attrs.tag, { title: node.attrs.title }, 0],
|
||||
},
|
||||
toMarkdown: {
|
||||
mixable: true,
|
||||
open(state, mark) {
|
||||
return `<${mark.attrs.tag}${
|
||||
mark.attrs.title ? ` title="${state.esc(escape(mark.attrs.title))}"` : ''
|
||||
}>`;
|
||||
},
|
||||
close(_, mark) {
|
||||
return `</${mark.attrs.tag}>`;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Italic as BaseItalic } from 'tiptap-extensions';
|
||||
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
|
||||
export default class Italic extends BaseItalic {
|
||||
get toMarkdown() {
|
||||
return defaultMarkdownSerializer.marks.em;
|
||||
}
|
||||
}
|
||||
export default () => ({
|
||||
name: 'italic',
|
||||
schema: {
|
||||
parseDOM: [{ tag: 'em' }],
|
||||
toDOM: () => ['em', 0],
|
||||
},
|
||||
toMarkdown: defaultMarkdownSerializer.marks.em,
|
||||
});
|
||||
|
|
|
@ -1,21 +1,47 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Link as BaseLink } from 'tiptap-extensions';
|
||||
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
|
||||
export default class Link extends BaseLink {
|
||||
get toMarkdown() {
|
||||
return {
|
||||
mixable: true,
|
||||
open(state, mark, parent, index) {
|
||||
const open = defaultMarkdownSerializer.marks.link.open(state, mark, parent, index);
|
||||
return open === '<' ? '' : open;
|
||||
export default () => ({
|
||||
name: 'link',
|
||||
schema: {
|
||||
attrs: {
|
||||
href: {
|
||||
default: null,
|
||||
},
|
||||
close(state, mark, parent, index) {
|
||||
const close = defaultMarkdownSerializer.marks.link.close(state, mark, parent, index);
|
||||
return close === '>' ? '' : close;
|
||||
target: {
|
||||
default: null,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
inclusive: false,
|
||||
parseDOM: [
|
||||
{
|
||||
tag: 'a[href]',
|
||||
getAttrs: (dom) => ({
|
||||
href: dom.getAttribute('href'),
|
||||
target: dom.getAttribute('target'),
|
||||
}),
|
||||
},
|
||||
],
|
||||
toDOM: (node) => [
|
||||
'a',
|
||||
{
|
||||
...node.attrs,
|
||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
rel: 'noopener noreferrer nofollow',
|
||||
target: node.attrs.target,
|
||||
},
|
||||
0,
|
||||
],
|
||||
},
|
||||
toMarkdown: {
|
||||
mixable: true,
|
||||
open(state, mark, parent, index) {
|
||||
const open = defaultMarkdownSerializer.marks.link.open(state, mark, parent, index);
|
||||
return open === '<' ? '' : open;
|
||||
},
|
||||
close(state, mark, parent, index) {
|
||||
const close = defaultMarkdownSerializer.marks.link.close(state, mark, parent, index);
|
||||
return close === '>' ? '' : close;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,42 +1,31 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Mark } from 'tiptap';
|
||||
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
|
||||
import { HIGHER_PARSE_RULE_PRIORITY } from '../constants';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::MathFilter
|
||||
export default class MathMark extends Mark {
|
||||
get name() {
|
||||
return 'math';
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
parseDOM: [
|
||||
// Matches HTML generated by Banzai::Filter::MathFilter
|
||||
{
|
||||
tag: 'code.code.math[data-math-style=inline]',
|
||||
priority: HIGHER_PARSE_RULE_PRIORITY,
|
||||
},
|
||||
// Matches HTML after being transformed by app/assets/javascripts/behaviors/markdown/render_math.js
|
||||
{
|
||||
tag: 'span.katex',
|
||||
contentElement: 'annotation[encoding="application/x-tex"]',
|
||||
},
|
||||
],
|
||||
toDOM: () => ['code', { class: 'code math', 'data-math-style': 'inline' }, 0],
|
||||
};
|
||||
}
|
||||
|
||||
get toMarkdown() {
|
||||
return {
|
||||
escape: false,
|
||||
open(state, mark, parent, index) {
|
||||
return `$${defaultMarkdownSerializer.marks.code.open(state, mark, parent, index)}`;
|
||||
export default () => ({
|
||||
name: 'math',
|
||||
schema: {
|
||||
parseDOM: [
|
||||
// Matches HTML generated by Banzai::Filter::MathFilter
|
||||
{
|
||||
tag: 'code.code.math[data-math-style=inline]',
|
||||
priority: HIGHER_PARSE_RULE_PRIORITY,
|
||||
},
|
||||
close(state, mark, parent, index) {
|
||||
return `${defaultMarkdownSerializer.marks.code.close(state, mark, parent, index)}$`;
|
||||
// Matches HTML after being transformed by app/assets/javascripts/behaviors/markdown/render_math.js
|
||||
{
|
||||
tag: 'span.katex',
|
||||
contentElement: 'annotation[encoding="application/x-tex"]',
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
],
|
||||
toDOM: () => ['code', { class: 'code math', 'data-math-style': 'inline' }, 0],
|
||||
},
|
||||
toMarkdown: {
|
||||
escape: false,
|
||||
open(state, mark, parent, index) {
|
||||
return `$${defaultMarkdownSerializer.marks.code.open(state, mark, parent, index)}`;
|
||||
},
|
||||
close(state, mark, parent, index) {
|
||||
return `${defaultMarkdownSerializer.marks.code.close(state, mark, parent, index)}$`;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Strike as BaseStrike } from 'tiptap-extensions';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
|
||||
export default class Strike extends BaseStrike {
|
||||
get toMarkdown() {
|
||||
return {
|
||||
open: '~~',
|
||||
close: '~~',
|
||||
mixable: true,
|
||||
expelEnclosingWhitespace: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
export default () => ({
|
||||
name: 'strike',
|
||||
schema: {
|
||||
parseDOM: [
|
||||
{
|
||||
tag: 'del',
|
||||
},
|
||||
],
|
||||
toDOM: () => ['s', 0],
|
||||
},
|
||||
toMarkdown: {
|
||||
open: '~~',
|
||||
close: '~~',
|
||||
mixable: true,
|
||||
expelEnclosingWhitespace: true,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,9 +1,4 @@
|
|||
import Playable from './playable';
|
||||
import playable from './playable';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::AudioLinkFilter
|
||||
export default class Audio extends Playable {
|
||||
constructor() {
|
||||
super();
|
||||
this.mediaType = 'audio';
|
||||
}
|
||||
}
|
||||
export default () => playable({ mediaType: 'audio' });
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Blockquote as BaseBlockquote } from 'tiptap-extensions';
|
||||
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
|
||||
export default class Blockquote extends BaseBlockquote {
|
||||
export default () => ({
|
||||
name: 'blockquote',
|
||||
schema: {
|
||||
content: 'block*',
|
||||
group: 'block',
|
||||
defining: true,
|
||||
draggable: false,
|
||||
parseDOM: [{ tag: 'blockquote' }],
|
||||
toDOM: () => ['blockquote', 0],
|
||||
},
|
||||
toMarkdown(state, node) {
|
||||
if (!node.childCount) return;
|
||||
|
||||
defaultMarkdownSerializer.nodes.blockquote(state, node);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { BulletList as BaseBulletList } from 'tiptap-extensions';
|
||||
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
|
||||
export default class BulletList extends BaseBulletList {
|
||||
export default () => ({
|
||||
name: 'bullet_list',
|
||||
schema: {
|
||||
content: 'list_item+',
|
||||
group: 'block',
|
||||
parseDOM: [{ tag: 'ul' }],
|
||||
toDOM: () => ['ul', 0],
|
||||
},
|
||||
toMarkdown(state, node) {
|
||||
defaultMarkdownSerializer.nodes.bullet_list(state, node);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { CodeBlock as BaseCodeBlock } from 'tiptap-extensions';
|
||||
|
||||
const PLAINTEXT_LANG = 'plaintext';
|
||||
|
||||
// Transforms generated HTML back to GFM for:
|
||||
|
@ -9,68 +5,67 @@ const PLAINTEXT_LANG = 'plaintext';
|
|||
// - Banzai::Filter::MathFilter
|
||||
// - Banzai::Filter::MermaidFilter
|
||||
// - Banzai::Filter::SuggestionFilter
|
||||
export default class CodeBlock extends BaseCodeBlock {
|
||||
get schema() {
|
||||
return {
|
||||
content: 'text*',
|
||||
marks: '',
|
||||
group: 'block',
|
||||
code: true,
|
||||
defining: true,
|
||||
attrs: {
|
||||
lang: { default: PLAINTEXT_LANG },
|
||||
},
|
||||
parseDOM: [
|
||||
// Matches HTML generated by Banzai::Filter::SyntaxHighlightFilter, Banzai::Filter::MathFilter, Banzai::Filter::MermaidFilter, or Banzai::Filter::SuggestionFilter
|
||||
{
|
||||
tag: 'pre.code.highlight',
|
||||
preserveWhitespace: 'full',
|
||||
getAttrs: (el) => {
|
||||
const lang = el.getAttribute('lang');
|
||||
if (!lang || lang === '') return {};
|
||||
export default () => ({
|
||||
name: 'code_block',
|
||||
schema: {
|
||||
content: 'text*',
|
||||
marks: '',
|
||||
group: 'block',
|
||||
code: true,
|
||||
defining: true,
|
||||
attrs: {
|
||||
lang: { default: PLAINTEXT_LANG },
|
||||
},
|
||||
parseDOM: [
|
||||
// Matches HTML generated by Banzai::Filter::SyntaxHighlightFilter, Banzai::Filter::MathFilter, Banzai::Filter::MermaidFilter, or Banzai::Filter::SuggestionFilter
|
||||
{
|
||||
tag: 'pre.code.highlight',
|
||||
preserveWhitespace: 'full',
|
||||
getAttrs: (el) => {
|
||||
const lang = el.getAttribute('lang');
|
||||
if (!lang || lang === '') return {};
|
||||
|
||||
return { lang };
|
||||
},
|
||||
return { lang };
|
||||
},
|
||||
// Matches HTML generated by Banzai::Filter::MathFilter,
|
||||
// after being transformed by app/assets/javascripts/behaviors/markdown/render_math.js
|
||||
{
|
||||
tag: 'span.katex-display',
|
||||
preserveWhitespace: 'full',
|
||||
contentElement: 'annotation[encoding="application/x-tex"]',
|
||||
attrs: { lang: 'math' },
|
||||
},
|
||||
// Matches HTML generated by Banzai::Filter::MermaidFilter,
|
||||
// after being transformed by app/assets/javascripts/behaviors/markdown/render_mermaid.js
|
||||
{
|
||||
tag: 'svg.mermaid',
|
||||
preserveWhitespace: 'full',
|
||||
contentElement: 'text.source',
|
||||
attrs: { lang: 'mermaid' },
|
||||
},
|
||||
// Matches HTML generated by Banzai::Filter::SuggestionFilter,
|
||||
// after being transformed by app/assets/javascripts/vue_shared/components/markdown/suggestions.vue
|
||||
{
|
||||
tag: '.md-suggestion',
|
||||
skip: true,
|
||||
},
|
||||
{
|
||||
tag: '.md-suggestion-header',
|
||||
ignore: true,
|
||||
},
|
||||
{
|
||||
tag: '.md-suggestion-diff',
|
||||
preserveWhitespace: 'full',
|
||||
getContent: (el, schema) =>
|
||||
[...el.querySelectorAll('.line_content.new span')].map((span) =>
|
||||
schema.text(span.innerText),
|
||||
),
|
||||
attrs: { lang: 'suggestion' },
|
||||
},
|
||||
],
|
||||
toDOM: (node) => ['pre', { class: 'code highlight', lang: node.attrs.lang }, ['code', 0]],
|
||||
};
|
||||
}
|
||||
},
|
||||
// Matches HTML generated by Banzai::Filter::MathFilter,
|
||||
// after being transformed by app/assets/javascripts/behaviors/markdown/render_math.js
|
||||
{
|
||||
tag: 'span.katex-display',
|
||||
preserveWhitespace: 'full',
|
||||
contentElement: 'annotation[encoding="application/x-tex"]',
|
||||
attrs: { lang: 'math' },
|
||||
},
|
||||
// Matches HTML generated by Banzai::Filter::MermaidFilter,
|
||||
// after being transformed by app/assets/javascripts/behaviors/markdown/render_mermaid.js
|
||||
{
|
||||
tag: 'svg.mermaid',
|
||||
preserveWhitespace: 'full',
|
||||
contentElement: 'text.source',
|
||||
attrs: { lang: 'mermaid' },
|
||||
},
|
||||
// Matches HTML generated by Banzai::Filter::SuggestionFilter,
|
||||
// after being transformed by app/assets/javascripts/vue_shared/components/markdown/suggestions.vue
|
||||
{
|
||||
tag: '.md-suggestion',
|
||||
skip: true,
|
||||
},
|
||||
{
|
||||
tag: '.md-suggestion-header',
|
||||
ignore: true,
|
||||
},
|
||||
{
|
||||
tag: '.md-suggestion-diff',
|
||||
preserveWhitespace: 'full',
|
||||
getContent: (el, schema) =>
|
||||
[...el.querySelectorAll('.line_content.new span')].map((span) =>
|
||||
schema.text(span.innerText),
|
||||
),
|
||||
attrs: { lang: 'suggestion' },
|
||||
},
|
||||
],
|
||||
toDOM: (node) => ['pre', { class: 'code highlight', lang: node.attrs.lang }, ['code', 0]],
|
||||
},
|
||||
|
||||
toMarkdown(state, node) {
|
||||
if (!node.childCount) return;
|
||||
|
@ -95,5 +90,5 @@ export default class CodeBlock extends BaseCodeBlock {
|
|||
|
||||
state.write('```');
|
||||
state.closeBlock(node);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,22 +1,14 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Node } from 'tiptap';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
|
||||
export default class DescriptionDetails extends Node {
|
||||
get name() {
|
||||
return 'description_details';
|
||||
}
|
||||
export default () => ({
|
||||
name: 'description_details',
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
content: 'text*',
|
||||
marks: '',
|
||||
defining: true,
|
||||
parseDOM: [{ tag: 'dd' }],
|
||||
toDOM: () => ['dd', 0],
|
||||
};
|
||||
}
|
||||
schema: {
|
||||
content: 'text*',
|
||||
marks: '',
|
||||
defining: true,
|
||||
parseDOM: [{ tag: 'dd' }],
|
||||
toDOM: () => ['dd', 0],
|
||||
},
|
||||
|
||||
toMarkdown(state, node) {
|
||||
state.flushClose(1);
|
||||
|
@ -24,5 +16,5 @@ export default class DescriptionDetails extends Node {
|
|||
state.text(node.textContent, false);
|
||||
state.write('</dd>');
|
||||
state.closeBlock(node);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,21 +1,12 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Node } from 'tiptap';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
|
||||
export default class DescriptionList extends Node {
|
||||
get name() {
|
||||
return 'description_list';
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
content: '(description_term+ description_details+)+',
|
||||
group: 'block',
|
||||
parseDOM: [{ tag: 'dl' }],
|
||||
toDOM: () => ['dl', 0],
|
||||
};
|
||||
}
|
||||
export default () => ({
|
||||
name: 'description_list',
|
||||
schema: {
|
||||
content: '(description_term+ description_details+)+',
|
||||
group: 'block',
|
||||
parseDOM: [{ tag: 'dl' }],
|
||||
toDOM: () => ['dl', 0],
|
||||
},
|
||||
|
||||
toMarkdown(state, node) {
|
||||
state.write('<dl>\n');
|
||||
|
@ -24,5 +15,5 @@ export default class DescriptionList extends Node {
|
|||
state.ensureNewLine();
|
||||
state.write('</dl>');
|
||||
state.closeBlock(node);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,28 +1,18 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Node } from 'tiptap';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
|
||||
export default class DescriptionTerm extends Node {
|
||||
get name() {
|
||||
return 'description_term';
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
content: 'text*',
|
||||
marks: '',
|
||||
defining: true,
|
||||
parseDOM: [{ tag: 'dt' }],
|
||||
toDOM: () => ['dt', 0],
|
||||
};
|
||||
}
|
||||
|
||||
export default () => ({
|
||||
name: 'description_term',
|
||||
schema: {
|
||||
content: 'text*',
|
||||
marks: '',
|
||||
defining: true,
|
||||
parseDOM: [{ tag: 'dt' }],
|
||||
toDOM: () => ['dt', 0],
|
||||
},
|
||||
toMarkdown(state, node) {
|
||||
state.flushClose(state.closed && state.closed.type === node.type ? 1 : 2);
|
||||
state.write('<dt>');
|
||||
state.text(node.textContent, false);
|
||||
state.write('</dt>');
|
||||
state.closeBlock(node);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,22 +1,12 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Node } from 'tiptap';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
|
||||
export default class Details extends Node {
|
||||
get name() {
|
||||
return 'details';
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
content: 'summary block*',
|
||||
group: 'block',
|
||||
parseDOM: [{ tag: 'details' }],
|
||||
toDOM: () => ['details', { open: true, onclick: 'return false', tabindex: '-1' }, 0],
|
||||
};
|
||||
}
|
||||
|
||||
export default () => ({
|
||||
name: 'details',
|
||||
schema: {
|
||||
content: 'summary block*',
|
||||
group: 'block',
|
||||
parseDOM: [{ tag: 'details' }],
|
||||
toDOM: () => ['details', { open: true, onclick: 'return false', tabindex: '-1' }, 0],
|
||||
},
|
||||
toMarkdown(state, node) {
|
||||
state.write('<details>\n');
|
||||
state.renderContent(node);
|
||||
|
@ -24,5 +14,5 @@ export default class Details extends Node {
|
|||
state.ensureNewLine();
|
||||
state.write('</details>');
|
||||
state.closeBlock(node);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,15 +1,6 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Node } from 'tiptap';
|
||||
|
||||
export default class Doc extends Node {
|
||||
get name() {
|
||||
return 'doc';
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
content: 'block+',
|
||||
};
|
||||
}
|
||||
}
|
||||
export default () => ({
|
||||
name: 'doc',
|
||||
schema: {
|
||||
content: 'block+',
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,53 +1,43 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Node } from 'tiptap';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::EmojiFilter
|
||||
export default class Emoji extends Node {
|
||||
get name() {
|
||||
return 'emoji';
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
inline: true,
|
||||
group: 'inline',
|
||||
attrs: {
|
||||
name: {},
|
||||
title: {},
|
||||
moji: {},
|
||||
export default () => ({
|
||||
name: 'emoji',
|
||||
schema: {
|
||||
inline: true,
|
||||
group: 'inline',
|
||||
attrs: {
|
||||
name: {},
|
||||
title: {},
|
||||
moji: {},
|
||||
},
|
||||
parseDOM: [
|
||||
{
|
||||
tag: 'gl-emoji',
|
||||
getAttrs: (el) => ({
|
||||
name: el.dataset.name,
|
||||
title: el.getAttribute('title'),
|
||||
moji: el.textContent,
|
||||
}),
|
||||
},
|
||||
parseDOM: [
|
||||
{
|
||||
tag: 'gl-emoji',
|
||||
getAttrs: (el) => ({
|
||||
name: el.dataset.name,
|
||||
title: el.getAttribute('title'),
|
||||
moji: el.textContent,
|
||||
}),
|
||||
},
|
||||
{
|
||||
tag: 'img.emoji',
|
||||
getAttrs: (el) => {
|
||||
const name = el.getAttribute('title').replace(/^:|:$/g, '');
|
||||
{
|
||||
tag: 'img.emoji',
|
||||
getAttrs: (el) => {
|
||||
const name = el.getAttribute('title').replace(/^:|:$/g, '');
|
||||
|
||||
return {
|
||||
name,
|
||||
title: name,
|
||||
moji: name,
|
||||
};
|
||||
},
|
||||
return {
|
||||
name,
|
||||
title: name,
|
||||
moji: name,
|
||||
};
|
||||
},
|
||||
],
|
||||
toDOM: (node) => [
|
||||
'gl-emoji',
|
||||
{ 'data-name': node.attrs.name, title: node.attrs.title },
|
||||
node.attrs.moji,
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
},
|
||||
],
|
||||
toDOM: (node) => [
|
||||
'gl-emoji',
|
||||
{ 'data-name': node.attrs.name, title: node.attrs.title },
|
||||
node.attrs.moji,
|
||||
],
|
||||
},
|
||||
toMarkdown(state, node) {
|
||||
state.write(`:${node.attrs.name}:`);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { HardBreak as BaseHardBreak } from 'tiptap-extensions';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
|
||||
export default class HardBreak extends BaseHardBreak {
|
||||
export default () => ({
|
||||
name: 'hard_break',
|
||||
schema: {
|
||||
inline: true,
|
||||
group: 'inline',
|
||||
selectable: false,
|
||||
parseDOM: [{ tag: 'br' }],
|
||||
toDOM: () => ['br'],
|
||||
},
|
||||
toMarkdown(state) {
|
||||
if (!state.atBlank()) state.write(' \n');
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,13 +1,27 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Heading as BaseHeading } from 'tiptap-extensions';
|
||||
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
|
||||
export default class Heading extends BaseHeading {
|
||||
export default ({ levels = [1, 2, 3, 4, 5, 6] } = {}) => ({
|
||||
name: 'heading',
|
||||
schema: {
|
||||
attrs: {
|
||||
level: {
|
||||
default: 1,
|
||||
},
|
||||
},
|
||||
content: 'inline*',
|
||||
group: 'block',
|
||||
defining: true,
|
||||
draggable: false,
|
||||
parseDOM: levels.map((level) => ({
|
||||
tag: `h${level}`,
|
||||
attrs: { level },
|
||||
})),
|
||||
toDOM: (node) => [`h${node.attrs.level}`, 0],
|
||||
},
|
||||
toMarkdown(state, node) {
|
||||
if (!node.childCount) return;
|
||||
|
||||
defaultMarkdownSerializer.nodes.heading(state, node);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { HorizontalRule as BaseHorizontalRule } from 'tiptap-extensions';
|
||||
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
|
||||
export default class HorizontalRule extends BaseHorizontalRule {
|
||||
export default () => ({
|
||||
name: 'horizontal_rule',
|
||||
schema: {
|
||||
group: 'block',
|
||||
parseDOM: [{ tag: 'hr' }],
|
||||
toDOM: () => ['hr'],
|
||||
},
|
||||
toMarkdown(state, node) {
|
||||
defaultMarkdownSerializer.nodes.horizontal_rule(state, node);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,53 +1,48 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Image as BaseImage } from 'tiptap-extensions';
|
||||
import { placeholderImage } from '~/lazy_loader';
|
||||
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
|
||||
import { HIGHER_PARSE_RULE_PRIORITY } from '../constants';
|
||||
|
||||
export default class Image extends BaseImage {
|
||||
get schema() {
|
||||
return {
|
||||
attrs: {
|
||||
src: {},
|
||||
alt: {
|
||||
default: null,
|
||||
},
|
||||
title: {
|
||||
default: null,
|
||||
export default () => ({
|
||||
name: 'image',
|
||||
schema: {
|
||||
attrs: {
|
||||
src: {},
|
||||
alt: {
|
||||
default: null,
|
||||
},
|
||||
title: {
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
group: 'inline',
|
||||
inline: true,
|
||||
draggable: true,
|
||||
parseDOM: [
|
||||
// Matches HTML generated by Banzai::Filter::ImageLinkFilter
|
||||
{
|
||||
tag: 'a.no-attachment-icon',
|
||||
priority: HIGHER_PARSE_RULE_PRIORITY,
|
||||
skip: true,
|
||||
},
|
||||
// Matches HTML generated by Banzai::Filter::ImageLazyLoadFilter
|
||||
{
|
||||
tag: 'img[src]:not(.emoji)',
|
||||
getAttrs: (el) => {
|
||||
const imageSrc = el.src;
|
||||
const imageUrl =
|
||||
imageSrc && imageSrc !== placeholderImage ? imageSrc : el.dataset.src || '';
|
||||
|
||||
return {
|
||||
src: imageUrl,
|
||||
title: el.getAttribute('title'),
|
||||
alt: el.getAttribute('alt'),
|
||||
};
|
||||
},
|
||||
},
|
||||
group: 'inline',
|
||||
inline: true,
|
||||
draggable: true,
|
||||
parseDOM: [
|
||||
// Matches HTML generated by Banzai::Filter::ImageLinkFilter
|
||||
{
|
||||
tag: 'a.no-attachment-icon',
|
||||
priority: HIGHER_PARSE_RULE_PRIORITY,
|
||||
skip: true,
|
||||
},
|
||||
// Matches HTML generated by Banzai::Filter::ImageLazyLoadFilter
|
||||
{
|
||||
tag: 'img[src]:not(.emoji)',
|
||||
getAttrs: (el) => {
|
||||
const imageSrc = el.src;
|
||||
const imageUrl =
|
||||
imageSrc && imageSrc !== placeholderImage ? imageSrc : el.dataset.src || '';
|
||||
|
||||
return {
|
||||
src: imageUrl,
|
||||
title: el.getAttribute('title'),
|
||||
alt: el.getAttribute('alt'),
|
||||
};
|
||||
},
|
||||
},
|
||||
],
|
||||
toDOM: (node) => ['img', node.attrs],
|
||||
};
|
||||
}
|
||||
|
||||
],
|
||||
toDOM: (node) => ['img', node.attrs],
|
||||
},
|
||||
toMarkdown(state, node) {
|
||||
defaultMarkdownSerializer.nodes.image(state, node);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { ListItem as BaseListItem } from 'tiptap-extensions';
|
||||
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
|
||||
export default class ListItem extends BaseListItem {
|
||||
export default () => ({
|
||||
name: 'list_item',
|
||||
schema: {
|
||||
content: 'paragraph block*',
|
||||
defining: true,
|
||||
draggable: false,
|
||||
parseDOM: [{ tag: 'li' }],
|
||||
toDOM: () => ['li', 0],
|
||||
},
|
||||
toMarkdown(state, node) {
|
||||
defaultMarkdownSerializer.nodes.list_item(state, node);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,10 +1,25 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { OrderedList as BaseOrderedList } from 'tiptap-extensions';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
|
||||
export default class OrderedList extends BaseOrderedList {
|
||||
export default () => ({
|
||||
name: 'ordered_list',
|
||||
schema: {
|
||||
attrs: {
|
||||
order: {
|
||||
default: 1,
|
||||
},
|
||||
},
|
||||
content: 'list_item+',
|
||||
group: 'block',
|
||||
parseDOM: [
|
||||
{
|
||||
tag: 'ol',
|
||||
getAttrs: (dom) => ({
|
||||
order: dom.hasAttribute('start') ? dom.getAttribute('start') + 1 : 1,
|
||||
}),
|
||||
},
|
||||
],
|
||||
toDOM: (node) => (node.attrs.order === 1 ? ['ol', 0] : ['ol', { start: node.attrs.order }, 0]),
|
||||
},
|
||||
toMarkdown(state, node) {
|
||||
state.renderList(node, ' ', () => '1. ');
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,29 +1,21 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Node } from 'tiptap';
|
||||
import { HIGHER_PARSE_RULE_PRIORITY } from '../constants';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::TaskListFilter
|
||||
export default class OrderedTaskList extends Node {
|
||||
get name() {
|
||||
return 'ordered_task_list';
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
group: 'block',
|
||||
content: '(task_list_item|list_item)+',
|
||||
parseDOM: [
|
||||
{
|
||||
priority: HIGHER_PARSE_RULE_PRIORITY,
|
||||
tag: 'ol.task-list',
|
||||
},
|
||||
],
|
||||
toDOM: () => ['ol', { class: 'task-list' }, 0],
|
||||
};
|
||||
}
|
||||
export default () => ({
|
||||
name: 'ordered_task_list',
|
||||
schema: {
|
||||
group: 'block',
|
||||
content: '(task_list_item|list_item)+',
|
||||
parseDOM: [
|
||||
{
|
||||
priority: HIGHER_PARSE_RULE_PRIORITY,
|
||||
tag: 'ol.task-list',
|
||||
},
|
||||
],
|
||||
toDOM: () => ['ol', { class: 'task-list' }, 0],
|
||||
},
|
||||
|
||||
toMarkdown(state, node) {
|
||||
state.renderList(node, ' ', () => '1. ');
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,24 +1,15 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Node } from 'tiptap';
|
||||
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
|
||||
export default class Paragraph extends Node {
|
||||
get name() {
|
||||
return 'paragraph';
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
content: 'inline*',
|
||||
group: 'block',
|
||||
parseDOM: [{ tag: 'p' }],
|
||||
toDOM: () => ['p', 0],
|
||||
};
|
||||
}
|
||||
|
||||
export default () => ({
|
||||
name: 'paragraph',
|
||||
schema: {
|
||||
content: 'inline*',
|
||||
group: 'block',
|
||||
parseDOM: [{ tag: 'p' }],
|
||||
toDOM: () => ['p', 0],
|
||||
},
|
||||
toMarkdown(state, node) {
|
||||
defaultMarkdownSerializer.nodes.paragraph(state, node);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
/* eslint-disable @gitlab/require-i18n-strings */
|
||||
|
||||
import { Node } from 'tiptap';
|
||||
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
|
||||
|
||||
/**
|
||||
|
@ -10,62 +6,51 @@ import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer
|
|||
* the `mediaType` property in their constructors.
|
||||
* @abstract
|
||||
*/
|
||||
export default class Playable extends Node {
|
||||
constructor() {
|
||||
super();
|
||||
this.mediaType = '';
|
||||
this.extraElementAttrs = {};
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.mediaType;
|
||||
}
|
||||
|
||||
get schema() {
|
||||
const attrs = {
|
||||
src: {},
|
||||
alt: {
|
||||
default: null,
|
||||
},
|
||||
};
|
||||
|
||||
const parseDOM = [
|
||||
export default ({ mediaType, extraElementAttrs = {} }) => {
|
||||
const attrs = {
|
||||
src: {},
|
||||
alt: {
|
||||
default: null,
|
||||
},
|
||||
};
|
||||
const parseDOM = [
|
||||
{
|
||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
tag: `.${mediaType}-container`,
|
||||
getAttrs: (el) => ({
|
||||
src: el.querySelector(mediaType).src,
|
||||
alt: el.querySelector(mediaType).dataset.title,
|
||||
}),
|
||||
},
|
||||
];
|
||||
const toDOM = (node) => [
|
||||
'span',
|
||||
{ class: `media-container ${mediaType}-container` },
|
||||
[
|
||||
mediaType,
|
||||
{
|
||||
tag: `.${this.mediaType}-container`,
|
||||
getAttrs: (el) => ({
|
||||
src: el.querySelector(this.mediaType).src,
|
||||
alt: el.querySelector(this.mediaType).dataset.title,
|
||||
}),
|
||||
src: node.attrs.src,
|
||||
controls: true,
|
||||
'data-setup': '{}',
|
||||
'data-title': node.attrs.alt,
|
||||
...extraElementAttrs,
|
||||
},
|
||||
];
|
||||
],
|
||||
['a', { href: node.attrs.src }, node.attrs.alt],
|
||||
];
|
||||
|
||||
const toDOM = (node) => [
|
||||
'span',
|
||||
{ class: `media-container ${this.mediaType}-container` },
|
||||
[
|
||||
this.mediaType,
|
||||
{
|
||||
src: node.attrs.src,
|
||||
controls: true,
|
||||
'data-setup': '{}',
|
||||
'data-title': node.attrs.alt,
|
||||
...this.extraElementAttrs,
|
||||
},
|
||||
],
|
||||
['a', { href: node.attrs.src }, node.attrs.alt],
|
||||
];
|
||||
|
||||
return {
|
||||
return {
|
||||
name: mediaType,
|
||||
schema: {
|
||||
attrs,
|
||||
group: 'inline',
|
||||
inline: true,
|
||||
draggable: true,
|
||||
parseDOM,
|
||||
toDOM,
|
||||
};
|
||||
}
|
||||
|
||||
toMarkdown(state, node) {
|
||||
defaultMarkdownSerializer.nodes.image(state, node);
|
||||
}
|
||||
}
|
||||
},
|
||||
toMarkdown(state, node) {
|
||||
defaultMarkdownSerializer.nodes.image(state, node);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,53 +1,44 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Node } from 'tiptap';
|
||||
import { HIGHER_PARSE_RULE_PRIORITY } from '../constants';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::ReferenceFilter and subclasses
|
||||
export default class Reference extends Node {
|
||||
get name() {
|
||||
return 'reference';
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
inline: true,
|
||||
group: 'inline',
|
||||
atom: true,
|
||||
attrs: {
|
||||
className: {},
|
||||
referenceType: {},
|
||||
originalText: { default: null },
|
||||
href: {},
|
||||
text: {},
|
||||
export default () => ({
|
||||
name: 'reference',
|
||||
schema: {
|
||||
inline: true,
|
||||
group: 'inline',
|
||||
atom: true,
|
||||
attrs: {
|
||||
className: {},
|
||||
referenceType: {},
|
||||
originalText: { default: null },
|
||||
href: {},
|
||||
text: {},
|
||||
},
|
||||
parseDOM: [
|
||||
{
|
||||
tag: 'a.gfm:not([data-link=true])',
|
||||
priority: HIGHER_PARSE_RULE_PRIORITY,
|
||||
getAttrs: (el) => ({
|
||||
className: el.className,
|
||||
referenceType: el.dataset.referenceType,
|
||||
originalText: el.dataset.original,
|
||||
href: el.getAttribute('href'),
|
||||
text: el.textContent,
|
||||
}),
|
||||
},
|
||||
parseDOM: [
|
||||
{
|
||||
tag: 'a.gfm:not([data-link=true])',
|
||||
priority: HIGHER_PARSE_RULE_PRIORITY,
|
||||
getAttrs: (el) => ({
|
||||
className: el.className,
|
||||
referenceType: el.dataset.referenceType,
|
||||
originalText: el.dataset.original,
|
||||
href: el.getAttribute('href'),
|
||||
text: el.textContent,
|
||||
}),
|
||||
},
|
||||
],
|
||||
toDOM: (node) => [
|
||||
'a',
|
||||
{
|
||||
class: node.attrs.className,
|
||||
href: node.attrs.href,
|
||||
'data-reference-type': node.attrs.referenceType,
|
||||
'data-original': node.attrs.originalText,
|
||||
},
|
||||
node.attrs.text,
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
],
|
||||
toDOM: (node) => [
|
||||
'a',
|
||||
{
|
||||
class: node.attrs.className,
|
||||
href: node.attrs.href,
|
||||
'data-reference-type': node.attrs.referenceType,
|
||||
'data-original': node.attrs.originalText,
|
||||
},
|
||||
node.attrs.text,
|
||||
],
|
||||
},
|
||||
toMarkdown(state, node) {
|
||||
state.write(node.attrs.originalText || node.attrs.text);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,27 +1,17 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Node } from 'tiptap';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
|
||||
export default class Summary extends Node {
|
||||
get name() {
|
||||
return 'summary';
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
content: 'text*',
|
||||
marks: '',
|
||||
defining: true,
|
||||
parseDOM: [{ tag: 'summary' }],
|
||||
toDOM: () => ['summary', 0],
|
||||
};
|
||||
}
|
||||
|
||||
export default () => ({
|
||||
name: 'summary',
|
||||
schema: {
|
||||
content: 'text*',
|
||||
marks: '',
|
||||
defining: true,
|
||||
parseDOM: [{ tag: 'summary' }],
|
||||
toDOM: () => ['summary', 0],
|
||||
},
|
||||
toMarkdown(state, node) {
|
||||
state.write('<summary>');
|
||||
state.text(node.textContent, false);
|
||||
state.write('</summary>');
|
||||
state.closeBlock(node);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,25 +1,15 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Node } from 'tiptap';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
|
||||
export default class Table extends Node {
|
||||
get name() {
|
||||
return 'table';
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
content: 'table_head table_body',
|
||||
group: 'block',
|
||||
isolating: true,
|
||||
parseDOM: [{ tag: 'table' }],
|
||||
toDOM: () => ['table', 0],
|
||||
};
|
||||
}
|
||||
|
||||
export default () => ({
|
||||
name: 'table',
|
||||
schema: {
|
||||
content: 'table_head table_body',
|
||||
group: 'block',
|
||||
isolating: true,
|
||||
parseDOM: [{ tag: 'table' }],
|
||||
toDOM: () => ['table', 0],
|
||||
},
|
||||
toMarkdown(state, node) {
|
||||
state.renderContent(node);
|
||||
state.closeBlock(node);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,24 +1,14 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Node } from 'tiptap';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
|
||||
export default class TableBody extends Node {
|
||||
get name() {
|
||||
return 'table_body';
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
content: 'table_row+',
|
||||
parseDOM: [{ tag: 'tbody' }],
|
||||
toDOM: () => ['tbody', 0],
|
||||
};
|
||||
}
|
||||
|
||||
toMarkdown(state, node) {
|
||||
export default () => ({
|
||||
name: 'table_body',
|
||||
schema: {
|
||||
content: 'table_row+',
|
||||
parseDOM: [{ tag: 'tbody' }],
|
||||
toDOM: () => ['tbody', 0],
|
||||
},
|
||||
toMarkdown: (state, node) => {
|
||||
state.flushClose(1);
|
||||
state.renderContent(node);
|
||||
state.closeBlock(node);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,35 +1,25 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Node } from 'tiptap';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
|
||||
export default class TableCell extends Node {
|
||||
get name() {
|
||||
return 'table_cell';
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
attrs: {
|
||||
header: { default: false },
|
||||
align: { default: null },
|
||||
export default () => ({
|
||||
name: 'table_cell',
|
||||
schema: {
|
||||
attrs: {
|
||||
header: { default: false },
|
||||
align: { default: null },
|
||||
},
|
||||
content: 'inline*',
|
||||
isolating: true,
|
||||
parseDOM: [
|
||||
{
|
||||
tag: 'td, th',
|
||||
getAttrs: (el) => ({
|
||||
header: el.tagName === 'TH',
|
||||
align: el.getAttribute('align') || el.style.textAlign,
|
||||
}),
|
||||
},
|
||||
content: 'inline*',
|
||||
isolating: true,
|
||||
parseDOM: [
|
||||
{
|
||||
tag: 'td, th',
|
||||
getAttrs: (el) => ({
|
||||
header: el.tagName === 'TH',
|
||||
align: el.getAttribute('align') || el.style.textAlign,
|
||||
}),
|
||||
},
|
||||
],
|
||||
toDOM: (node) => [node.attrs.header ? 'th' : 'td', { align: node.attrs.align }, 0],
|
||||
};
|
||||
}
|
||||
|
||||
toMarkdown(state, node) {
|
||||
],
|
||||
toDOM: (node) => [node.attrs.header ? 'th' : 'td', { align: node.attrs.align }, 0],
|
||||
},
|
||||
toMarkdown: (state, node) => {
|
||||
state.renderInline(node);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,24 +1,14 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Node } from 'tiptap';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
|
||||
export default class TableHead extends Node {
|
||||
get name() {
|
||||
return 'table_head';
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
content: 'table_header_row',
|
||||
parseDOM: [{ tag: 'thead' }],
|
||||
toDOM: () => ['thead', 0],
|
||||
};
|
||||
}
|
||||
|
||||
toMarkdown(state, node) {
|
||||
export default () => ({
|
||||
name: 'table_head',
|
||||
schema: {
|
||||
content: 'table_header_row',
|
||||
parseDOM: [{ tag: 'thead' }],
|
||||
toDOM: () => ['thead', 0],
|
||||
},
|
||||
toMarkdown: (state, node) => {
|
||||
state.flushClose(1);
|
||||
state.renderContent(node);
|
||||
state.closeBlock(node);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,31 +1,23 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { HIGHER_PARSE_RULE_PRIORITY } from '../constants';
|
||||
import TableRow from './table_row';
|
||||
|
||||
const CENTER_ALIGN = 'center';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
|
||||
export default class TableHeaderRow extends TableRow {
|
||||
get name() {
|
||||
return 'table_header_row';
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
content: 'table_cell+',
|
||||
parseDOM: [
|
||||
{
|
||||
tag: 'thead tr',
|
||||
priority: HIGHER_PARSE_RULE_PRIORITY,
|
||||
},
|
||||
],
|
||||
toDOM: () => ['tr', 0],
|
||||
};
|
||||
}
|
||||
|
||||
toMarkdown(state, node) {
|
||||
const cellWidths = super.toMarkdown(state, node);
|
||||
export default () => ({
|
||||
name: 'table_header_row',
|
||||
schema: {
|
||||
content: 'table_cell+',
|
||||
parseDOM: [
|
||||
{
|
||||
tag: 'thead tr',
|
||||
priority: HIGHER_PARSE_RULE_PRIORITY,
|
||||
},
|
||||
],
|
||||
toDOM: () => ['tr', 0],
|
||||
},
|
||||
toMarkdown: (state, node) => {
|
||||
const cellWidths = TableRow().toMarkdown(state, node);
|
||||
|
||||
state.flushClose(1);
|
||||
|
||||
|
@ -40,5 +32,5 @@ export default class TableHeaderRow extends TableRow {
|
|||
state.write('|');
|
||||
|
||||
state.closeBlock(node);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,35 +1,26 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Node } from 'tiptap';
|
||||
import { __ } from '~/locale';
|
||||
import { HIGHER_PARSE_RULE_PRIORITY } from '../constants';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::TableOfContentsFilter
|
||||
export default class TableOfContents extends Node {
|
||||
get name() {
|
||||
return 'table_of_contents';
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
group: 'block',
|
||||
atom: true,
|
||||
parseDOM: [
|
||||
{
|
||||
tag: 'ul.section-nav',
|
||||
priority: HIGHER_PARSE_RULE_PRIORITY,
|
||||
},
|
||||
{
|
||||
tag: 'p.table-of-contents',
|
||||
priority: HIGHER_PARSE_RULE_PRIORITY,
|
||||
},
|
||||
],
|
||||
toDOM: () => ['p', { class: 'table-of-contents' }, __('Table of Contents')],
|
||||
};
|
||||
}
|
||||
|
||||
toMarkdown(state, node) {
|
||||
export default () => ({
|
||||
name: 'table_of_contents',
|
||||
schema: {
|
||||
group: 'block',
|
||||
atom: true,
|
||||
parseDOM: [
|
||||
{
|
||||
tag: 'ul.section-nav',
|
||||
priority: HIGHER_PARSE_RULE_PRIORITY,
|
||||
},
|
||||
{
|
||||
tag: 'p.table-of-contents',
|
||||
priority: HIGHER_PARSE_RULE_PRIORITY,
|
||||
},
|
||||
],
|
||||
toDOM: () => ['p', { class: 'table-of-contents' }, __('Table of Contents')],
|
||||
},
|
||||
toMarkdown: (state, node) => {
|
||||
state.write('[[_TOC_]]');
|
||||
state.closeBlock(node);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,22 +1,12 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Node } from 'tiptap';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
|
||||
export default class TableRow extends Node {
|
||||
get name() {
|
||||
return 'table_row';
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
content: 'table_cell+',
|
||||
parseDOM: [{ tag: 'tr' }],
|
||||
toDOM: () => ['tr', 0],
|
||||
};
|
||||
}
|
||||
|
||||
toMarkdown(state, node) {
|
||||
export default () => ({
|
||||
name: 'table_row',
|
||||
schema: {
|
||||
content: 'table_cell+',
|
||||
parseDOM: [{ tag: 'tr' }],
|
||||
toDOM: () => ['tr', 0],
|
||||
},
|
||||
toMarkdown: (state, node) => {
|
||||
const cellWidths = [];
|
||||
|
||||
state.flushClose(1);
|
||||
|
@ -34,5 +24,5 @@ export default class TableRow extends Node {
|
|||
state.closeBlock(node);
|
||||
|
||||
return cellWidths;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,29 +1,20 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Node } from 'tiptap';
|
||||
import { HIGHER_PARSE_RULE_PRIORITY } from '../constants';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::TaskListFilter
|
||||
export default class TaskList extends Node {
|
||||
get name() {
|
||||
return 'task_list';
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
group: 'block',
|
||||
content: '(task_list_item|list_item)+',
|
||||
parseDOM: [
|
||||
{
|
||||
priority: HIGHER_PARSE_RULE_PRIORITY,
|
||||
tag: 'ul.task-list',
|
||||
},
|
||||
],
|
||||
toDOM: () => ['ul', { class: 'task-list' }, 0],
|
||||
};
|
||||
}
|
||||
|
||||
export default () => ({
|
||||
name: 'task_list',
|
||||
schema: {
|
||||
group: 'block',
|
||||
content: '(task_list_item|list_item)+',
|
||||
parseDOM: [
|
||||
{
|
||||
priority: HIGHER_PARSE_RULE_PRIORITY,
|
||||
tag: 'ul.task-list',
|
||||
},
|
||||
],
|
||||
toDOM: () => ['ul', { class: 'task-list' }, 0],
|
||||
},
|
||||
toMarkdown(state, node) {
|
||||
state.renderList(node, ' ', () => '* ');
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,50 +1,38 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Node } from 'tiptap';
|
||||
import { HIGHER_PARSE_RULE_PRIORITY } from '../constants';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::TaskListFilter
|
||||
export default class TaskListItem extends Node {
|
||||
get name() {
|
||||
return 'task_list_item';
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
attrs: {
|
||||
done: {
|
||||
default: false,
|
||||
export default () => ({
|
||||
name: 'task_list_item',
|
||||
schema: {
|
||||
attrs: {
|
||||
done: {
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
defining: true,
|
||||
draggable: false,
|
||||
content: 'paragraph block*',
|
||||
parseDOM: [
|
||||
{
|
||||
priority: HIGHER_PARSE_RULE_PRIORITY,
|
||||
tag: 'li.task-list-item',
|
||||
getAttrs: (el) => {
|
||||
const checkbox = el.querySelector('input[type=checkbox].task-list-item-checkbox');
|
||||
return { done: checkbox && checkbox.checked };
|
||||
},
|
||||
},
|
||||
defining: true,
|
||||
draggable: false,
|
||||
content: 'paragraph block*',
|
||||
parseDOM: [
|
||||
{
|
||||
priority: HIGHER_PARSE_RULE_PRIORITY,
|
||||
tag: 'li.task-list-item',
|
||||
getAttrs: (el) => {
|
||||
const checkbox = el.querySelector('input[type=checkbox].task-list-item-checkbox');
|
||||
return { done: checkbox && checkbox.checked };
|
||||
},
|
||||
},
|
||||
],
|
||||
toDOM(node) {
|
||||
return [
|
||||
'li',
|
||||
{ class: 'task-list-item' },
|
||||
[
|
||||
'input',
|
||||
{ type: 'checkbox', class: 'task-list-item-checkbox', checked: node.attrs.done },
|
||||
],
|
||||
['div', { class: 'todo-content' }, 0],
|
||||
];
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
],
|
||||
toDOM(node) {
|
||||
return [
|
||||
'li',
|
||||
{ class: 'task-list-item' },
|
||||
['input', { type: 'checkbox', class: 'task-list-item-checkbox', checked: node.attrs.done }],
|
||||
['div', { class: 'todo-content' }, 0],
|
||||
];
|
||||
},
|
||||
},
|
||||
toMarkdown(state, node) {
|
||||
state.write(`[${node.attrs.done ? 'x' : ' '}] `);
|
||||
state.renderContent(node);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,20 +1,11 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { Node } from 'tiptap';
|
||||
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
|
||||
|
||||
export default class Text extends Node {
|
||||
get name() {
|
||||
return 'text';
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
group: 'inline',
|
||||
};
|
||||
}
|
||||
|
||||
export default () => ({
|
||||
name: 'text',
|
||||
schema: {
|
||||
group: 'inline',
|
||||
},
|
||||
toMarkdown(state, node) {
|
||||
defaultMarkdownSerializer.nodes.text(state, node);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,10 +1,4 @@
|
|||
import Playable from './playable';
|
||||
import playable from './playable';
|
||||
|
||||
// Transforms generated HTML back to GFM for Banzai::Filter::VideoLinkFilter
|
||||
export default class Video extends Playable {
|
||||
constructor() {
|
||||
super();
|
||||
this.mediaType = 'video';
|
||||
this.extraElementAttrs = { width: '400' };
|
||||
}
|
||||
}
|
||||
export default () => playable({ mediaType: 'video', extraElementAttrs: { width: '400' } });
|
||||
|
|
|
@ -1,24 +1,20 @@
|
|||
import { Schema } from 'prosemirror-model';
|
||||
import editorExtensions from './editor_extensions';
|
||||
|
||||
const nodes = editorExtensions
|
||||
.filter((extension) => extension.type === 'node')
|
||||
.reduce(
|
||||
(ns, { name, schema }) => ({
|
||||
...ns,
|
||||
[name]: schema,
|
||||
}),
|
||||
{},
|
||||
);
|
||||
const nodes = editorExtensions.nodes.reduce(
|
||||
(ns, { name, schema }) => ({
|
||||
...ns,
|
||||
[name]: schema,
|
||||
}),
|
||||
{},
|
||||
);
|
||||
|
||||
const marks = editorExtensions
|
||||
.filter((extension) => extension.type === 'mark')
|
||||
.reduce(
|
||||
(ms, { name, schema }) => ({
|
||||
...ms,
|
||||
[name]: schema,
|
||||
}),
|
||||
{},
|
||||
);
|
||||
const marks = editorExtensions.marks.reduce(
|
||||
(ms, { name, schema }) => ({
|
||||
...ms,
|
||||
[name]: schema,
|
||||
}),
|
||||
{},
|
||||
);
|
||||
|
||||
export default new Schema({ nodes, marks });
|
||||
|
|
|
@ -1,24 +1,20 @@
|
|||
import { MarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
|
||||
import editorExtensions from './editor_extensions';
|
||||
|
||||
const nodes = editorExtensions
|
||||
.filter((extension) => extension.type === 'node')
|
||||
.reduce(
|
||||
(ns, { name, toMarkdown }) => ({
|
||||
...ns,
|
||||
[name]: toMarkdown,
|
||||
}),
|
||||
{},
|
||||
);
|
||||
const nodes = editorExtensions.nodes.reduce(
|
||||
(ns, { name, toMarkdown }) => ({
|
||||
...ns,
|
||||
[name]: toMarkdown,
|
||||
}),
|
||||
{},
|
||||
);
|
||||
|
||||
const marks = editorExtensions
|
||||
.filter((extension) => extension.type === 'mark')
|
||||
.reduce(
|
||||
(ms, { name, toMarkdown }) => ({
|
||||
...ms,
|
||||
[name]: toMarkdown,
|
||||
}),
|
||||
{},
|
||||
);
|
||||
const marks = editorExtensions.marks.reduce(
|
||||
(ms, { name, toMarkdown }) => ({
|
||||
...ms,
|
||||
[name]: toMarkdown,
|
||||
}),
|
||||
{},
|
||||
);
|
||||
|
||||
export default new MarkdownSerializer(nodes, marks);
|
||||
|
|
|
@ -129,7 +129,7 @@ module DropdownsHelper
|
|||
end
|
||||
|
||||
def dropdown_loading
|
||||
spinner = loading_icon(container: true, size: "md", css_class: "gl-mt-7")
|
||||
spinner = gl_loading_icon(size: "md", css_class: "gl-mt-7")
|
||||
content_tag(:div, spinner, class: "dropdown-loading")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -49,13 +49,39 @@ module IconsHelper
|
|||
end
|
||||
end
|
||||
|
||||
def loading_icon(container: false, color: 'orange', size: 'sm', css_class: nil)
|
||||
css_classes = ['gl-spinner', "gl-spinner-#{color}", "gl-spinner-#{size}"]
|
||||
css_classes << "#{css_class}" unless css_class.blank?
|
||||
# Creates a GitLab UI loading icon/spinner.
|
||||
#
|
||||
# Examples:
|
||||
# # Default
|
||||
# gl_loading_icon
|
||||
#
|
||||
# # Sizes
|
||||
# gl_loading_icon(size: 'md')
|
||||
# gl_loading_icon(size: 'lg')
|
||||
# gl_loading_icon(size: 'xl')
|
||||
#
|
||||
# # Colors
|
||||
# gl_loading_icon(color: 'light')
|
||||
#
|
||||
# # Block/Inline
|
||||
# gl_loading_icon(inline: true)
|
||||
#
|
||||
# # Custom classes
|
||||
# gl_loading_icon(css_class: "foo-bar")
|
||||
#
|
||||
# See also https://gitlab-org.gitlab.io/gitlab-ui/?path=/story/base-loading-icon--default
|
||||
def gl_loading_icon(inline: false, color: 'dark', size: 'sm', css_class: nil)
|
||||
spinner = content_tag(:span, "", {
|
||||
class: %[gl-spinner gl-spinner-#{color} gl-spinner-#{size} gl-vertical-align-text-bottom!],
|
||||
aria: { label: _('Loading') }
|
||||
})
|
||||
|
||||
spinner = content_tag(:span, "", { class: css_classes.join(' '), aria: { label: _('Loading') } })
|
||||
|
||||
container == true ? content_tag(:div, spinner, { class: 'gl-spinner-container' }) : spinner
|
||||
container_classes = ['gl-spinner-container']
|
||||
container_classes << css_class unless css_class.blank?
|
||||
content_tag(inline ? :span : :div, spinner, {
|
||||
class: container_classes,
|
||||
role: 'status'
|
||||
})
|
||||
end
|
||||
|
||||
def external_snippet_icon(name)
|
||||
|
|
|
@ -12,8 +12,7 @@
|
|||
|
||||
.info-well.gl-display-none.gl-sm-display-flex.project-last-commit.gl-flex-direction-column
|
||||
#js-last-commit.gl-m-auto
|
||||
.gl-spinner-container.m-auto
|
||||
= loading_icon(size: 'md', color: 'dark', css_class: 'align-text-bottom')
|
||||
= gl_loading_icon(size: 'md')
|
||||
#js-code-owners
|
||||
|
||||
- if is_project_overview
|
||||
|
|
|
@ -21,8 +21,7 @@
|
|||
project_path: @project.full_path,
|
||||
target_branch: project.empty_repo? ? ref : @ref,
|
||||
original_branch: @ref } }
|
||||
.gl-spinner-container
|
||||
= loading_icon(size: 'md')
|
||||
= gl_loading_icon(size: 'md')
|
||||
- else
|
||||
%article.file-holder
|
||||
= render 'projects/blob/header', blob: blob
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
= loading_icon(css_class: "gl-vertical-align-text-bottom mr-1")
|
||||
= gl_loading_icon(inline: true, css_class: "gl-mr-2!")
|
||||
= s_('Pipelines|Validating GitLab CI configuration…')
|
||||
|
||||
= link_to _('Learn more'), help_page_path('ci/yaml/index')
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
.text-center.gl-mt-4.gl-mb-3
|
||||
= loading_icon(size: "md", css_class: "qa-spinner")
|
||||
= gl_loading_icon(size: "md", css_class: "qa-spinner gl-my-4")
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
= loading_icon(css_class: "gl-vertical-align-text-bottom")
|
||||
= gl_loading_icon(inline: true)
|
||||
= _("Analyzing file…")
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
= loading_icon(css_class: "gl-vertical-align-text-bottom mr-1")
|
||||
= gl_loading_icon(inline: true, css_class: "mr-1")
|
||||
= _('Metrics Dashboard YAML definition') + '…'
|
||||
|
||||
= link_to _('Learn more'), help_page_path('operations/metrics/dashboards/yaml.md')
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
= loading_icon(css_class: "gl-vertical-align-text-bottom gl-mr-1")
|
||||
= gl_loading_icon(inline: true, css_class: "gl-mr-1")
|
||||
Validating Route Map…
|
||||
|
||||
= link_to 'Learn more', help_page_path('ci/environments/index.md', anchor: 'go-from-source-files-to-public-pages')
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
.file-content#js-sketch-viewer{ data: { endpoint: blob_raw_path } }
|
||||
.text-center.gl-mt-4.gl-mb-3.js-loading-icon
|
||||
= loading_icon(size: "md")
|
||||
= gl_loading_icon(size: "md", css_class: "gl-my-4 js-loading-icon")
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.file-content.is-stl-loading
|
||||
.text-center#js-stl-viewer{ data: { endpoint: blob_raw_path } }
|
||||
= loading_icon(size: "md", css_class: "gl-mt-4 gl-mb-3")
|
||||
= gl_loading_icon(size: "md", css_class: "gl-my-4")
|
||||
.text-center.gl-mt-3.gl-mb-3.stl-controls
|
||||
.btn-group
|
||||
%button.gl-button.btn.btn-default.btn-sm.js-material-changer{ data: { type: 'wireframe' } }
|
||||
|
|
|
@ -34,5 +34,4 @@
|
|||
%div{ id: dom_id(@project) }
|
||||
%ol#commits-list.list-unstyled.content_list
|
||||
= render 'commits', project: @project, ref: @ref
|
||||
.loading.hide
|
||||
= loading_icon(size: "lg")
|
||||
= gl_loading_icon(size: 'lg', css_class: 'loading hide')
|
||||
|
|
|
@ -23,5 +23,4 @@
|
|||
= _('There are no matching files')
|
||||
%p.text-secondary
|
||||
= _('Try using a different search term to find the file you are looking for.')
|
||||
.text-center.gl-mt-3.loading
|
||||
= loading_icon(size: 'md')
|
||||
= gl_loading_icon(size: 'md', css_class: 'gl-mt-3 loading')
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
.save-project-loader
|
||||
.center
|
||||
%h2
|
||||
= loading_icon
|
||||
= gl_loading_icon(inline: true)
|
||||
= import_in_progress_title
|
||||
- if !has_ci_cd_only_params? && @project.external_import?
|
||||
%p.monospace git clone --bare #{@project.safe_import_url}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
.flash-text
|
||||
.loading-metrics.js-loading-custom-metrics
|
||||
%p.m-3
|
||||
= loading_icon(css_class: 'metrics-load-spinner')
|
||||
= gl_loading_icon(inline: true, css_class: 'metrics-load-spinner')
|
||||
= s_('PrometheusService|Finding custom metrics...')
|
||||
.empty-metrics.hidden.js-empty-custom-metrics
|
||||
%p.text-tertiary.m-3.js-no-active-integration-text.hidden
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
.card-body
|
||||
.loading-metrics.js-loading-metrics
|
||||
%p.m-3
|
||||
= loading_icon(css_class: 'metrics-load-spinner')
|
||||
= gl_loading_icon(inline: true, css_class: 'metrics-load-spinner')
|
||||
= s_('PrometheusService|Finding and configuring metrics...')
|
||||
.empty-metrics.hidden.js-empty-metrics
|
||||
%p.text-tertiary.m-3
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
- if any_projects?(@projects)
|
||||
.project-item-select-holder.btn-group.gl-ml-auto.gl-mr-auto.gl-relative.gl-overflow-hidden{ class: 'gl-display-flex!' }
|
||||
%a.btn.gl-button.btn-confirm.js-new-project-item-link.block-truncated.qa-new-project-item-link{ href: '', data: { label: local_assigns[:label], type: local_assigns[:type] }, class: "gl-m-0!" }
|
||||
= loading_icon(color: 'light')
|
||||
= gl_loading_icon(inline: true, color: 'light')
|
||||
= project_select_tag :project_path, class: "project-item-select gl-absolute! gl-visibility-hidden", data: { include_groups: local_assigns[:include_groups], order_by: 'last_activity_at', relative_path: local_assigns[:path], with_shared: local_assigns[:with_shared], include_projects_in_subgroups: local_assigns[:include_projects_in_subgroups] }, with_feature_enabled: local_assigns[:with_feature_enabled]
|
||||
%button.btn.dropdown-toggle.btn-confirm.btn-md.gl-button.gl-dropdown-toggle.dropdown-toggle-split.new-project-item-select-button.qa-new-project-item-select-button.gl-p-0.gl-w-100{ class: "gl-m-0!", 'aria-label': _('Toggle project select') }
|
||||
= sprite_icon('chevron-down')
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
// Fallback while content is loading
|
||||
.title.hide-collapsed
|
||||
= _('Time tracking')
|
||||
= loading_icon(css_class: 'gl-vertical-align-text-bottom')
|
||||
= gl_loading_icon(inline: true)
|
||||
- if issuable_sidebar.has_key?(:due_date)
|
||||
#js-due-date-entry-point
|
||||
|
||||
|
@ -109,8 +109,8 @@
|
|||
= dropdown_loading
|
||||
= dropdown_footer add_content_class: true do
|
||||
%button.gl-button.btn.btn-confirm.sidebar-move-issue-confirmation-button.js-move-issue-confirmation-button{ type: 'button', disabled: true }
|
||||
= gl_loading_icon(inline: true, css_class: 'sidebar-move-issue-confirmation-loading-icon gl-mr-2')
|
||||
= _('Move')
|
||||
= loading_icon(css_class: 'gl-vertical-align-text-bottom sidebar-move-issue-confirmation-loading-icon')
|
||||
|
||||
-# haml-lint:disable InlineJavaScript
|
||||
%script.js-sidebar-options{ type: "application/json" }= issuable_sidebar_options(issuable_sidebar).to_json.html_safe
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
directly_invite_members: can_admin_project_member?(@project) } }
|
||||
.title.hide-collapsed
|
||||
= _('Assignee')
|
||||
= loading_icon(css_class: 'gl-vertical-align-text-bottom')
|
||||
= gl_loading_icon(inline: true)
|
||||
|
||||
.js-sidebar-assignee-data.selectbox.hide-collapsed
|
||||
- if assignees.none?
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#js-vue-sidebar-reviewers{ data: { field: issuable_type, signed_in: signed_in } }
|
||||
.title.hide-collapsed
|
||||
= _('Reviewer')
|
||||
= loading_icon(css_class: 'gl-vertical-align-text-bottom')
|
||||
= gl_loading_icon(inline: true)
|
||||
|
||||
.selectbox.hide-collapsed
|
||||
- if reviewers.none?
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
%span.attaching-file-message
|
||||
-# Populated by app/assets/javascripts/dropzone_input.js
|
||||
%span.uploading-progress 0%
|
||||
= loading_icon(css_class: 'align-text-bottom gl-mr-2')
|
||||
= gl_loading_icon(inline: true, css_class: 'gl-mr-2')
|
||||
|
||||
%span.uploading-error-container.hide
|
||||
%span.uploading-error-icon
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: rate_limit_user_by_id_endpoint
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73069
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/348796
|
||||
milestone: '14.6'
|
||||
type: development
|
||||
group: group::optimize
|
||||
default_enabled: false
|
|
@ -771,6 +771,26 @@ group.require_two_factor_authentication=false
|
|||
group.save
|
||||
```
|
||||
|
||||
### Check and toggle a feature for all projects in a group
|
||||
|
||||
```ruby
|
||||
projects = Group.find_by_name('_group_name').projects
|
||||
projects.each do |p|
|
||||
state = p.<feature-name>?
|
||||
|
||||
if state
|
||||
puts "#{p.name} has <feature-name> already enabled. Skipping..."
|
||||
else
|
||||
puts "#{p.name} didn't have <feature-name> enabled. Enabling..."
|
||||
p.project_feature.update!(builds_access_level: ProjectFeature::PRIVATE)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
To find features that can be toggled, run `pp p.project_feature`.
|
||||
Available permission levels are listed in
|
||||
[concerns/featurable.rb](https://gitlab.com/gitlab-org/gitlab/blob/master/app/models/concerns/featurable.rb).
|
||||
|
||||
## Authentication
|
||||
|
||||
### Re-enable standard web sign-in form
|
||||
|
|
|
@ -11104,7 +11104,6 @@ four standard [pagination arguments](#connection-pagination-arguments):
|
|||
| <a id="groupistemporarystorageincreaseenabled"></a>`isTemporaryStorageIncreaseEnabled` | [`Boolean!`](#boolean) | Status of the temporary storage increase. |
|
||||
| <a id="grouplfsenabled"></a>`lfsEnabled` | [`Boolean`](#boolean) | Indicates if Large File Storage (LFS) is enabled for namespace. |
|
||||
| <a id="groupmentionsdisabled"></a>`mentionsDisabled` | [`Boolean`](#boolean) | Indicates if a group is disabled from getting mentioned. |
|
||||
| <a id="groupmergerequestviolations"></a>`mergeRequestViolations` | [`ComplianceViolationConnection`](#complianceviolationconnection) | Compliance violations reported on merge requests merged within the group. Available only when feature flag `compliance_violations_graphql_type` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice. (see [Connections](#connections)) |
|
||||
| <a id="groupname"></a>`name` | [`String!`](#string) | Name of the namespace. |
|
||||
| <a id="grouporganizations"></a>`organizations` | [`CustomerRelationsOrganizationConnection`](#customerrelationsorganizationconnection) | Find organizations of this group. (see [Connections](#connections)) |
|
||||
| <a id="grouppackagesettings"></a>`packageSettings` | [`PackageSettings`](#packagesettings) | Package settings for the namespace. |
|
||||
|
@ -11449,6 +11448,23 @@ four standard [pagination arguments](#connection-pagination-arguments):
|
|||
| <a id="grouplabelsonlygrouplabels"></a>`onlyGroupLabels` | [`Boolean`](#boolean) | Include only group level labels. |
|
||||
| <a id="grouplabelssearchterm"></a>`searchTerm` | [`String`](#string) | Search term to find labels with. |
|
||||
|
||||
##### `Group.mergeRequestViolations`
|
||||
|
||||
Compliance violations reported on merge requests merged within the group. Available only when feature flag `compliance_violations_graphql_type` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice.
|
||||
|
||||
Returns [`ComplianceViolationConnection`](#complianceviolationconnection).
|
||||
|
||||
This field returns a [connection](#connections). It accepts the
|
||||
four standard [pagination arguments](#connection-pagination-arguments):
|
||||
`before: String`, `after: String`, `first: Int`, `last: Int`.
|
||||
|
||||
###### Arguments
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="groupmergerequestviolationsfilters"></a>`filters` | [`ComplianceViolationInput`](#complianceviolationinput) | Filters applied when retrieving compliance violations. |
|
||||
| <a id="groupmergerequestviolationssort"></a>`sort` | [`ComplianceViolationSort`](#complianceviolationsort) | List compliance violations by sort order. |
|
||||
|
||||
##### `Group.mergeRequests`
|
||||
|
||||
Merge requests for projects in this group.
|
||||
|
@ -17156,6 +17172,21 @@ Severity of the compliance violation.
|
|||
| <a id="complianceviolationseveritylow"></a>`LOW` | Low severity. |
|
||||
| <a id="complianceviolationseveritymedium"></a>`MEDIUM` | Medium severity. |
|
||||
|
||||
### `ComplianceViolationSort`
|
||||
|
||||
Compliance violation sort values.
|
||||
|
||||
| Value | Description |
|
||||
| ----- | ----------- |
|
||||
| <a id="complianceviolationsortmerged_at_asc"></a>`MERGED_AT_ASC` | Date merged in ascending order, further sorted by ID in ascending order. |
|
||||
| <a id="complianceviolationsortmerged_at_desc"></a>`MERGED_AT_DESC` | Date merged in descending order, further sorted by ID in descending order. |
|
||||
| <a id="complianceviolationsortmerge_request_title_asc"></a>`MERGE_REQUEST_TITLE_ASC` | Merge request title in ascending order, further sorted by ID in ascending order. |
|
||||
| <a id="complianceviolationsortmerge_request_title_desc"></a>`MERGE_REQUEST_TITLE_DESC` | Merge request title in descending order, further sorted by ID in descending order. |
|
||||
| <a id="complianceviolationsortseverity_level_asc"></a>`SEVERITY_LEVEL_ASC` | Severity in ascending order, further sorted by ID in ascending order. |
|
||||
| <a id="complianceviolationsortseverity_level_desc"></a>`SEVERITY_LEVEL_DESC` | Severity in descending order, further sorted by ID in descending order. |
|
||||
| <a id="complianceviolationsortviolation_reason_asc"></a>`VIOLATION_REASON_ASC` | Violation reason in ascending order, further sorted by ID in ascending order. |
|
||||
| <a id="complianceviolationsortviolation_reason_desc"></a>`VIOLATION_REASON_DESC` | Violation reason in descending order, further sorted by ID in descending order. |
|
||||
|
||||
### `ConanMetadatumFileTypeEnum`
|
||||
|
||||
Conan file types.
|
||||
|
@ -19744,6 +19775,16 @@ Field that are available while modifying the custom mapping attributes for an HT
|
|||
| <a id="complianceframeworkinputname"></a>`name` | [`String`](#string) | New name for the compliance framework. |
|
||||
| <a id="complianceframeworkinputpipelineconfigurationfullpath"></a>`pipelineConfigurationFullPath` | [`String`](#string) | Full path of the compliance pipeline configuration stored in a project repository, such as `.gitlab/.compliance-gitlab-ci.yml@compliance/hipaa` **(ULTIMATE)**. |
|
||||
|
||||
### `ComplianceViolationInput`
|
||||
|
||||
#### Arguments
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="complianceviolationinputmergedafter"></a>`mergedAfter` | [`Date`](#date) | Merged date of merge requests merged after a compliance violation was created. |
|
||||
| <a id="complianceviolationinputmergedbefore"></a>`mergedBefore` | [`Date`](#date) | Merged date of merge requests merged before a compliance violation was created. |
|
||||
| <a id="complianceviolationinputprojectids"></a>`projectIds` | [`[ProjectID!]`](#projectid) | Filter compliance violations by project. |
|
||||
|
||||
### `DastProfileCadenceInput`
|
||||
|
||||
Represents DAST Profile Cadence.
|
||||
|
|
|
@ -88,50 +88,36 @@ Please use the following function inside JS to render an icon:
|
|||
|
||||
### Usage in HAML/Rails
|
||||
|
||||
To insert a loading spinner in HAML or Rails use the `loading_icon` helper:
|
||||
To insert a loading spinner in HAML or Rails use the `gl_loading_icon` helper:
|
||||
|
||||
```haml
|
||||
= loading_icon
|
||||
= gl_loading_icon
|
||||
```
|
||||
|
||||
You can include one or more of the following properties with the `loading_icon` helper, as demonstrated
|
||||
You can include one or more of the following properties with the `gl_loading_icon` helper, as demonstrated
|
||||
by the examples that follow:
|
||||
|
||||
- `container` (optional): wraps the loading icon in a container, which centers the loading icon using the `text-center` CSS property.
|
||||
- `color` (optional): either `orange` (default), `light`, or `dark`.
|
||||
- `inline` (optional): uses in an inline element if `true`, otherwise, a block element (default), with the spinner centered.
|
||||
- `color` (optional): either `dark` (default) or `light`.
|
||||
- `size` (optional): either `sm` (default), `md`, `lg`, or `xl`.
|
||||
- `css_class` (optional): defaults to an empty string, but can be used for utility classes to fine-tune alignment or spacing.
|
||||
- `css_class` (optional): defaults to nothing, but can be used for utility classes to fine-tune alignment or spacing.
|
||||
|
||||
**Example 1:**
|
||||
|
||||
The following HAML expression generates a loading icon's markup and
|
||||
centers the icon by wrapping it in a `gl-spinner-container` element.
|
||||
centers the icon.
|
||||
|
||||
```haml
|
||||
= loading_icon(container: true)
|
||||
```
|
||||
|
||||
**Output from example 1:**
|
||||
|
||||
```html
|
||||
<div class="gl-spinner-container">
|
||||
<span class="gl-spinner gl-spinner-orange gl-spinner-sm" aria-label="Loading"></span>
|
||||
</div>
|
||||
= gl_loading_icon
|
||||
```
|
||||
|
||||
**Example 2:**
|
||||
|
||||
The following HAML expression generates a loading icon's markup
|
||||
The following HAML expression generates an inline loading icon's markup
|
||||
with a custom size. It also appends a margin utility class.
|
||||
|
||||
```haml
|
||||
= loading_icon(size: 'lg', css_class: 'gl-mr-2')
|
||||
```
|
||||
|
||||
**Output from example 2:**
|
||||
|
||||
```html
|
||||
<span class="gl-spinner gl-spinner-orange gl-spinner-lg gl-mr-2" aria-label="Loading"></span>
|
||||
= gl_loading_icon(inline: true, size: 'lg', css_class: 'gl-mr-2')
|
||||
```
|
||||
|
||||
### Usage in Vue
|
||||
|
|
|
@ -142,13 +142,11 @@ module API
|
|||
get ":id", feature_category: :users do
|
||||
forbidden!('Not authorized!') unless current_user
|
||||
|
||||
if Feature.enabled?(:rate_limit_user_by_id_endpoint, type: :development)
|
||||
unless current_user.admin?
|
||||
check_rate_limit!(:users_get_by_id,
|
||||
scope: current_user,
|
||||
users_allowlist: Gitlab::CurrentSettings.current_application_settings.users_get_by_id_limit_allowlist
|
||||
)
|
||||
end
|
||||
unless current_user.admin?
|
||||
check_rate_limit!(:users_get_by_id,
|
||||
scope: current_user,
|
||||
users_allowlist: Gitlab::CurrentSettings.current_application_settings.users_get_by_id_limit_allowlist
|
||||
)
|
||||
end
|
||||
|
||||
user = User.find_by(id: params[:id])
|
||||
|
|
|
@ -175,8 +175,6 @@
|
|||
"three-orbit-controls": "^82.1.0",
|
||||
"three-stl-loader": "^1.0.4",
|
||||
"timeago.js": "^4.0.2",
|
||||
"tiptap": "^1.32.2",
|
||||
"tiptap-extensions": "^1.35.2",
|
||||
"url-loader": "^4.1.1",
|
||||
"uuid": "8.1.0",
|
||||
"visibilityjs": "^1.2.4",
|
||||
|
|
|
@ -231,23 +231,33 @@ RSpec.describe IconsHelper do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'loading_icon' do
|
||||
it 'returns span with gl-spinner class and default configuration' do
|
||||
expect(loading_icon.to_s)
|
||||
.to eq '<span class="gl-spinner gl-spinner-orange gl-spinner-sm" aria-label="Loading"></span>'
|
||||
describe 'gl_loading_icon' do
|
||||
it 'returns the default spinner markup' do
|
||||
expect(gl_loading_icon.to_s)
|
||||
.to eq '<div class="gl-spinner-container" role="status"><span class="gl-spinner gl-spinner-dark gl-spinner-sm gl-vertical-align-text-bottom!" aria-label="Loading"></span></div>'
|
||||
end
|
||||
|
||||
context 'when css_class is provided' do
|
||||
it 'appends css_class to gl-spinner element' do
|
||||
expect(loading_icon(css_class: 'gl-mr-2').to_s)
|
||||
.to eq '<span class="gl-spinner gl-spinner-orange gl-spinner-sm gl-mr-2" aria-label="Loading"></span>'
|
||||
it 'appends css_class to container element' do
|
||||
expect(gl_loading_icon(css_class: 'gl-mr-2').to_s).to match 'gl-spinner-container gl-mr-2'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when container is true' do
|
||||
it 'creates a container that has the gl-spinner-container class selector' do
|
||||
expect(loading_icon(container: true).to_s)
|
||||
.to eq '<div class="gl-spinner-container"><span class="gl-spinner gl-spinner-orange gl-spinner-sm" aria-label="Loading"></span></div>'
|
||||
context 'when size is provided' do
|
||||
it 'sets the size class' do
|
||||
expect(gl_loading_icon(size: 'xl').to_s).to match 'gl-spinner-xl'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when color is provided' do
|
||||
it 'sets the color class' do
|
||||
expect(gl_loading_icon(color: 'light').to_s).to match 'gl-spinner-light'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when inline is true' do
|
||||
it 'creates an inline container' do
|
||||
expect(gl_loading_icon(inline: true).to_s).to start_with '<span class="gl-spinner-container"'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -649,20 +649,6 @@ RSpec.describe API::Users do
|
|||
expect(response).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(rate_limit_user_by_id_endpoint: false)
|
||||
end
|
||||
|
||||
it 'does not throttle the request' do
|
||||
expect(Gitlab::ApplicationRateLimiter).not_to receive(:throttled?)
|
||||
|
||||
get api("/users/#{user.id}", user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when job title is present' do
|
||||
|
|
92
yarn.lock
92
yarn.lock
|
@ -8096,7 +8096,7 @@ lowercase-keys@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
|
||||
integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
|
||||
|
||||
lowlight@^1.17.0, lowlight@^1.20.0:
|
||||
lowlight@^1.20.0:
|
||||
version "1.20.0"
|
||||
resolved "https://registry.yarnpkg.com/lowlight/-/lowlight-1.20.0.tgz#ddb197d33462ad0d93bf19d17b6c301aa3941888"
|
||||
integrity sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==
|
||||
|
@ -9584,14 +9584,7 @@ prop-types@^15.7.2:
|
|||
object-assign "^4.1.1"
|
||||
react-is "^16.8.1"
|
||||
|
||||
prosemirror-collab@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/prosemirror-collab/-/prosemirror-collab-1.2.2.tgz#8d2c0e82779cfef5d051154bd0836428bd6d9c4a"
|
||||
integrity sha512-tBnHKMLgy5Qmx9MYVcLfs3pAyjtcqYYDd9kp3y+LSiQzkhMQDfZSV3NXWe4Gsly32adSef173BvObwfoSQL5MA==
|
||||
dependencies:
|
||||
prosemirror-state "^1.0.0"
|
||||
|
||||
prosemirror-commands@^1.1.4, prosemirror-commands@^1.2.1:
|
||||
prosemirror-commands@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/prosemirror-commands/-/prosemirror-commands-1.2.1.tgz#eae0cb714df695260659b78ff5d201d3a037e50d"
|
||||
integrity sha512-S/IkpXfpuLFsRynC2HQ5iYROUPiZskKS1+ClcWycGJvj4HMb/mVfeEkQrixYxgTl96EAh+RZQNWPC06GZXk5tQ==
|
||||
|
@ -9600,7 +9593,7 @@ prosemirror-commands@^1.1.4, prosemirror-commands@^1.2.1:
|
|||
prosemirror-state "^1.0.0"
|
||||
prosemirror-transform "^1.0.0"
|
||||
|
||||
prosemirror-dropcursor@^1.3.2, prosemirror-dropcursor@^1.4.0:
|
||||
prosemirror-dropcursor@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/prosemirror-dropcursor/-/prosemirror-dropcursor-1.4.0.tgz#91a859d4ee79c99b1c0ba6ee61c093b195c0d9f0"
|
||||
integrity sha512-6+YwTjmqDwlA/Dm+5wK67ezgqgjA/MhSDgaNxKUzH97SmeuWFXyLeDRxxOPZeSo7yTxcDGUCWTEjmQZsVBuMrQ==
|
||||
|
@ -9609,7 +9602,7 @@ prosemirror-dropcursor@^1.3.2, prosemirror-dropcursor@^1.4.0:
|
|||
prosemirror-transform "^1.1.0"
|
||||
prosemirror-view "^1.1.0"
|
||||
|
||||
prosemirror-gapcursor@^1.1.5, prosemirror-gapcursor@^1.2.1:
|
||||
prosemirror-gapcursor@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/prosemirror-gapcursor/-/prosemirror-gapcursor-1.2.1.tgz#02365e1bcc1ad25d390b0fb7f0e94a7fc173ad75"
|
||||
integrity sha512-PHa9lj27iM/g4C46gxVzsefuXVfy/LrGQH4QjMRht7VDBgw77iWYWn8ZHMWSFkwtr9jQEuxI5gccHHHwWG80nw==
|
||||
|
@ -9619,7 +9612,7 @@ prosemirror-gapcursor@^1.1.5, prosemirror-gapcursor@^1.2.1:
|
|||
prosemirror-state "^1.0.0"
|
||||
prosemirror-view "^1.0.0"
|
||||
|
||||
prosemirror-history@^1.1.3, prosemirror-history@^1.2.0:
|
||||
prosemirror-history@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/prosemirror-history/-/prosemirror-history-1.2.0.tgz#04cc4df8d2f7b2a46651a2780de191ada6d465ea"
|
||||
integrity sha512-B9v9xtf4fYbKxQwIr+3wtTDNLDZcmMMmGiI3TAPShnUzvo+Rmv1GiUrsQChY1meetHl7rhML2cppF3FTs7f7UQ==
|
||||
|
@ -9628,15 +9621,7 @@ prosemirror-history@^1.1.3, prosemirror-history@^1.2.0:
|
|||
prosemirror-transform "^1.0.0"
|
||||
rope-sequence "^1.3.0"
|
||||
|
||||
prosemirror-inputrules@^1.1.2, prosemirror-inputrules@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/prosemirror-inputrules/-/prosemirror-inputrules-1.1.3.tgz#93f9199ca02473259c30d7e352e4c14022d54638"
|
||||
integrity sha512-ZaHCLyBtvbyIHv0f5p6boQTIJjlD6o2NPZiEaZWT2DA+j591zS29QQEMT4lBqwcLW3qRSf7ZvoKNbf05YrsStw==
|
||||
dependencies:
|
||||
prosemirror-state "^1.0.0"
|
||||
prosemirror-transform "^1.0.0"
|
||||
|
||||
prosemirror-keymap@^1.0.0, prosemirror-keymap@^1.1.2, prosemirror-keymap@^1.1.4, prosemirror-keymap@^1.1.5:
|
||||
prosemirror-keymap@^1.0.0, prosemirror-keymap@^1.1.2, prosemirror-keymap@^1.1.5:
|
||||
version "1.1.5"
|
||||
resolved "https://registry.yarnpkg.com/prosemirror-keymap/-/prosemirror-keymap-1.1.5.tgz#b5984c7d30f5c75956c853126c54e9e624c0327b"
|
||||
integrity sha512-8SZgPH3K+GLsHL2wKuwBD9rxhsbnVBTwpHCO4VUO5GmqUQlxd/2GtBVWTsyLq4Dp3N9nGgPd3+lZFKUDuVp+Vw==
|
||||
|
@ -9652,7 +9637,7 @@ prosemirror-markdown@1.7.1:
|
|||
markdown-it "^12.0.0"
|
||||
prosemirror-model "^1.0.0"
|
||||
|
||||
prosemirror-model@^1.0.0, prosemirror-model@^1.13.1, prosemirror-model@^1.16.0, prosemirror-model@^1.16.1, prosemirror-model@^1.2.0, prosemirror-model@^1.8.1:
|
||||
prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.16.1, prosemirror-model@^1.2.0, prosemirror-model@^1.8.1:
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.16.1.tgz#fb388270bc9609b66298d6a7e15d0cc1d6c61253"
|
||||
integrity sha512-r1/w0HDU40TtkXp0DyKBnFPYwd8FSlUSJmGCGFv4DeynfeSlyQF2FD0RQbVEMOe6P3PpUSXM6LZBV7W/YNZ4mA==
|
||||
|
@ -9666,7 +9651,7 @@ prosemirror-schema-basic@^1.0.0, prosemirror-schema-basic@^1.1.2:
|
|||
dependencies:
|
||||
prosemirror-model "^1.2.0"
|
||||
|
||||
prosemirror-schema-list@^1.0.0, prosemirror-schema-list@^1.1.4, prosemirror-schema-list@^1.1.6:
|
||||
prosemirror-schema-list@^1.0.0, prosemirror-schema-list@^1.1.6:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/prosemirror-schema-list/-/prosemirror-schema-list-1.1.6.tgz#c3e13fe2f74750e4a53ff88d798dc0c4ccca6707"
|
||||
integrity sha512-aFGEdaCWmJzouZ8DwedmvSsL50JpRkqhQ6tcpThwJONVVmCgI36LJHtoQ4VGZbusMavaBhXXr33zyD2IVsTlkw==
|
||||
|
@ -9674,7 +9659,7 @@ prosemirror-schema-list@^1.0.0, prosemirror-schema-list@^1.1.4, prosemirror-sche
|
|||
prosemirror-model "^1.0.0"
|
||||
prosemirror-transform "^1.0.0"
|
||||
|
||||
prosemirror-state@^1.0.0, prosemirror-state@^1.2.2, prosemirror-state@^1.3.1, prosemirror-state@^1.3.3, prosemirror-state@^1.3.4:
|
||||
prosemirror-state@^1.0.0, prosemirror-state@^1.2.2, prosemirror-state@^1.3.1, prosemirror-state@^1.3.4:
|
||||
version "1.3.4"
|
||||
resolved "https://registry.yarnpkg.com/prosemirror-state/-/prosemirror-state-1.3.4.tgz#4c6b52628216e753fc901c6d2bfd84ce109e8952"
|
||||
integrity sha512-Xkkrpd1y/TQ6HKzN3agsQIGRcLckUMA9u3j207L04mt8ToRgpGeyhbVv0HI7omDORIBHjR29b7AwlATFFf2GLA==
|
||||
|
@ -9702,14 +9687,14 @@ prosemirror-test-builder@^1.0.5:
|
|||
prosemirror-schema-basic "^1.0.0"
|
||||
prosemirror-schema-list "^1.0.0"
|
||||
|
||||
prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transform@^1.2.1, prosemirror-transform@^1.2.8, prosemirror-transform@^1.3.3:
|
||||
prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transform@^1.2.1, prosemirror-transform@^1.3.3:
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/prosemirror-transform/-/prosemirror-transform-1.3.3.tgz#5f6712b0577a119cc418686fe7588b6dd9b7464d"
|
||||
integrity sha512-9NLVXy1Sfa2G6qPqhWMkEvwQQMTw7OyTqOZbJaGQWsCeH3hH5Cw+c5eNaLM1Uu75EyKLsEZhJ93XpHJBa6RX8A==
|
||||
dependencies:
|
||||
prosemirror-model "^1.0.0"
|
||||
|
||||
prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.13.3, prosemirror-view@^1.16.5, prosemirror-view@^1.23.6, prosemirror-view@^1.23.7:
|
||||
prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.13.3, prosemirror-view@^1.23.6, prosemirror-view@^1.23.7:
|
||||
version "1.23.7"
|
||||
resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.23.7.tgz#f003af94445ef456e397c18cf4bb995e7072097f"
|
||||
integrity sha512-ugY+g/4UI2Ree1zzdvbyQWF2KpbFa7kxKMLHaEJcxiPaErnZiD5wZFqIgFinc7fY2v/QM3DLnJ++2I45ULRdrg==
|
||||
|
@ -11335,61 +11320,6 @@ tippy.js@^6.3.7:
|
|||
dependencies:
|
||||
"@popperjs/core" "^2.9.0"
|
||||
|
||||
tiptap-commands@^1.17.1:
|
||||
version "1.17.1"
|
||||
resolved "https://registry.yarnpkg.com/tiptap-commands/-/tiptap-commands-1.17.1.tgz#a8974a26d87db57b2fd4fc56a552520c69e43a4a"
|
||||
integrity sha512-CyGvMD/c6fNer5LThWGtrVMXHAqHn93ivGQpqJ58x3HNZFuoIiF9QTWXAiWbY/4QrG0ANYHKCSe9n5afickTqw==
|
||||
dependencies:
|
||||
prosemirror-commands "^1.1.4"
|
||||
prosemirror-inputrules "^1.1.2"
|
||||
prosemirror-model "^1.13.1"
|
||||
prosemirror-schema-list "^1.1.4"
|
||||
prosemirror-state "^1.3.3"
|
||||
prosemirror-tables "^1.1.1"
|
||||
tiptap-utils "^1.13.1"
|
||||
|
||||
tiptap-extensions@^1.35.2:
|
||||
version "1.35.2"
|
||||
resolved "https://registry.yarnpkg.com/tiptap-extensions/-/tiptap-extensions-1.35.2.tgz#83dd6ee703ae8c83b58c7608f97253fcc4f1a94c"
|
||||
integrity sha512-TIMbHVJe0/3aVeTeCmqGbatDkfxduPYFOffNCmuKR+h6oQNzTu6rLVhRzoNqktfxIoi/b44SiDPorTjSN72dCw==
|
||||
dependencies:
|
||||
lowlight "^1.17.0"
|
||||
prosemirror-collab "^1.2.2"
|
||||
prosemirror-history "^1.1.3"
|
||||
prosemirror-model "^1.13.1"
|
||||
prosemirror-state "^1.3.3"
|
||||
prosemirror-tables "^1.1.1"
|
||||
prosemirror-transform "^1.2.8"
|
||||
prosemirror-view "^1.16.5"
|
||||
tiptap "^1.32.2"
|
||||
tiptap-commands "^1.17.1"
|
||||
tiptap-utils "^1.13.1"
|
||||
|
||||
tiptap-utils@^1.13.1:
|
||||
version "1.13.1"
|
||||
resolved "https://registry.yarnpkg.com/tiptap-utils/-/tiptap-utils-1.13.1.tgz#f2150ded432465d66aa03a5ab333803415cddd20"
|
||||
integrity sha512-RoCvMfkdu7fp9u7nsRr1OgsYU8RFjoHKHEKpx075rJ9X0t+j5Vxah9n6QzTTr4yjvcavq22WO2flFacm36zYtA==
|
||||
dependencies:
|
||||
prosemirror-model "^1.13.1"
|
||||
prosemirror-state "^1.3.3"
|
||||
prosemirror-tables "^1.1.1"
|
||||
|
||||
tiptap@^1.32.2:
|
||||
version "1.32.2"
|
||||
resolved "https://registry.yarnpkg.com/tiptap/-/tiptap-1.32.2.tgz#cd6259e853652bfc6860758ff44ebb695d5edd1c"
|
||||
integrity sha512-5IwVj8nGo8y5V3jbdtoEd7xNUsi8Q0N6WV2Nfs70olqz3fldXkiImBrDhZJ4Anx8vhyP6PIBttrg0prFVmwIvw==
|
||||
dependencies:
|
||||
prosemirror-commands "^1.1.4"
|
||||
prosemirror-dropcursor "^1.3.2"
|
||||
prosemirror-gapcursor "^1.1.5"
|
||||
prosemirror-inputrules "^1.1.3"
|
||||
prosemirror-keymap "^1.1.4"
|
||||
prosemirror-model "^1.13.1"
|
||||
prosemirror-state "^1.3.3"
|
||||
prosemirror-view "^1.16.5"
|
||||
tiptap-commands "^1.17.1"
|
||||
tiptap-utils "^1.13.1"
|
||||
|
||||
tmpl@1.0.x:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1"
|
||||
|
|
Loading…
Reference in a new issue