diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-11-01 21:10:46 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-11-01 21:10:46 +0300 |
commit | 28d82e1650f73793f752425db2e1f26722b20b41 (patch) | |
tree | ef1d9902191ea305120c4608d489c97b87a337a3 /app | |
parent | eac94e5cd6a59aad41ff1f86ed7cc892898d8516 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
8 files changed, 101 insertions, 158 deletions
diff --git a/app/assets/javascripts/behaviors/markdown/render_gfm.js b/app/assets/javascripts/behaviors/markdown/render_gfm.js index ee5c0fe5ef3..a08cf48c327 100644 --- a/app/assets/javascripts/behaviors/markdown/render_gfm.js +++ b/app/assets/javascripts/behaviors/markdown/render_gfm.js @@ -15,7 +15,7 @@ $.fn.renderGFM = function renderGFM() { syntaxHighlight(this.find('.js-syntax-highlight').get()); renderKroki(this.find('.js-render-kroki[hidden]').get()); renderMath(this.find('.js-render-math')); - renderSandboxedMermaid(this.find('.js-render-mermaid')); + renderSandboxedMermaid(this.find('.js-render-mermaid').get()); renderJSONTable( Array.from(this.find('[lang="json"][data-lang-params="table"]').get()).map((e) => e.parentNode), ); diff --git a/app/assets/javascripts/behaviors/markdown/render_sandboxed_mermaid.js b/app/assets/javascripts/behaviors/markdown/render_sandboxed_mermaid.js index 077e96b2fee..66007aa9e3d 100644 --- a/app/assets/javascripts/behaviors/markdown/render_sandboxed_mermaid.js +++ b/app/assets/javascripts/behaviors/markdown/render_sandboxed_mermaid.js @@ -1,5 +1,4 @@ -import $ from 'jquery'; -import { once, countBy } from 'lodash'; +import { countBy } from 'lodash'; import { __ } from '~/locale'; import { getBaseURL, @@ -8,7 +7,8 @@ import { joinPaths, } from '~/lib/utils/url_utility'; import { darkModeEnabled } from '~/lib/utils/color_utils'; -import { setAttributes } from '~/lib/utils/dom_utils'; +import { setAttributes, isElementVisible } from '~/lib/utils/dom_utils'; +import { createAlert, VARIANT_WARNING } from '~/flash'; import { unrestrictedPages } from './constants'; // Renders diagrams and flowcharts from text using Mermaid in any element with the @@ -27,17 +27,30 @@ import { unrestrictedPages } from './constants'; const SANDBOX_FRAME_PATH = '/-/sandbox/mermaid'; // This is an arbitrary number; Can be iterated upon when suitable. -const MAX_CHAR_LIMIT = 2000; +export const MAX_CHAR_LIMIT = 2000; // Max # of mermaid blocks that can be rendered in a page. -const MAX_MERMAID_BLOCK_LIMIT = 50; +export const MAX_MERMAID_BLOCK_LIMIT = 50; // Max # of `&` allowed in Chaining of links syntax const MAX_CHAINING_OF_LINKS_LIMIT = 30; + export const BUFFER_IFRAME_HEIGHT = 10; export const SANDBOX_ATTRIBUTES = 'allow-scripts allow-popups'; + +const ALERT_CONTAINER_CLASS = 'mermaid-alert-container'; +export const LAZY_ALERT_SHOWN_CLASS = 'lazy-alert-shown'; + // Keep a map of mermaid blocks we've already rendered. const elsProcessingMap = new WeakMap(); let renderedMermaidBlocks = 0; +/** + * Determines whether a given Mermaid diagram is visible. + * + * @param {Element} el The Mermaid DOM node + * @returns + */ +const isVisibleMermaid = (el) => el.closest('details') === null && isElementVisible(el); + function shouldLazyLoadMermaidBlock(source) { /** * If source contains `&`, which means that it might @@ -104,8 +117,8 @@ function renderMermaidEl(el, source) { ); } -function renderMermaids($els) { - if (!$els.length) return; +function renderMermaids(els) { + if (!els.length) return; const pageName = document.querySelector('body').dataset.page; @@ -114,7 +127,7 @@ function renderMermaids($els) { let renderedChars = 0; - $els.each((i, el) => { + els.forEach((el) => { // Skipping all the elements which we've already queued in requestIdleCallback if (elsProcessingMap.has(el)) { return; @@ -133,33 +146,29 @@ function renderMermaids($els) { renderedMermaidBlocks >= MAX_MERMAID_BLOCK_LIMIT || shouldLazyLoadMermaidBlock(source)) ) { - const html = ` - <div class="alert gl-alert gl-alert-warning alert-dismissible lazy-render-mermaid-container js-lazy-render-mermaid-container fade show" role="alert"> - <div> - <div> - <div class="js-warning-text"></div> - <div class="gl-alert-actions"> - <button type="button" class="js-lazy-render-mermaid btn gl-alert-action btn-confirm btn-md gl-button">Display</button> - </div> - </div> - <button type="button" class="close" data-dismiss="alert" aria-label="Close"> - <span aria-hidden="true">×</span> - </button> - </div> - </div> - `; - - const $parent = $(el).parent(); - - if (!$parent.hasClass('lazy-alert-shown')) { - $parent.after(html); - $parent - .siblings() - .find('.js-warning-text') - .text( - __('Warning: Displaying this diagram might cause performance issues on this page.'), - ); - $parent.addClass('lazy-alert-shown'); + const parent = el.parentNode; + + if (!parent.classList.contains(LAZY_ALERT_SHOWN_CLASS)) { + const alertContainer = document.createElement('div'); + alertContainer.classList.add(ALERT_CONTAINER_CLASS); + alertContainer.classList.add('gl-mb-5'); + parent.after(alertContainer); + createAlert({ + message: __( + 'Warning: Displaying this diagram might cause performance issues on this page.', + ), + variant: VARIANT_WARNING, + parent: parent.parentNode, + containerSelector: `.${ALERT_CONTAINER_CLASS}`, + primaryButton: { + text: __('Display'), + clickHandler: () => { + alertContainer.remove(); + renderMermaidEl(el, source); + }, + }, + }); + parent.classList.add(LAZY_ALERT_SHOWN_CLASS); } return; @@ -176,37 +185,33 @@ function renderMermaids($els) { }); } -const hookLazyRenderMermaidEvent = once(() => { - $(document.body).on('click', '.js-lazy-render-mermaid', function eventHandler() { - const parent = $(this).closest('.js-lazy-render-mermaid-container'); - const pre = parent.prev(); - - const el = pre.find('.js-render-mermaid'); - - parent.remove(); - - // sandbox update - const element = el.get(0); - const { source } = fixElementSource(element); +export default function renderMermaid(els) { + if (!els.length) return; - renderMermaidEl(element, source); - }); -}); - -export default function renderMermaid($els) { - if (!$els.length) return; + const visibleMermaids = []; + const hiddenMermaids = []; - const visibleMermaids = $els.filter(function filter() { - return $(this).closest('details').length === 0 && $(this).is(':visible'); - }); + for (const el of els) { + if (isVisibleMermaid(el)) { + visibleMermaids.push(el); + } else { + hiddenMermaids.push(el); + } + } renderMermaids(visibleMermaids); - $els.closest('details').one('toggle', function toggle() { - if (this.open) { - renderMermaids($(this).find('.js-render-mermaid')); - } + hiddenMermaids.forEach((el) => { + el.closest('details')?.addEventListener( + 'toggle', + ({ target: details }) => { + if (details.open) { + renderMermaids([...details.querySelectorAll('.js-render-mermaid')]); + } + }, + { + once: true, + }, + ); }); - - hookLazyRenderMermaidEvent(); } diff --git a/app/assets/javascripts/blob/components/blob_edit_content.vue b/app/assets/javascripts/blob/components/blob_edit_content.vue deleted file mode 100644 index 0e670bbd80a..00000000000 --- a/app/assets/javascripts/blob/components/blob_edit_content.vue +++ /dev/null @@ -1,66 +0,0 @@ -<script> -import { debounce } from 'lodash'; -import { initSourceEditor } from '~/blob/utils'; -import { SNIPPET_MEASURE_BLOBS_CONTENT } from '~/performance/constants'; - -import eventHub from './eventhub'; - -export default { - props: { - value: { - type: String, - required: false, - default: '', - }, - fileName: { - type: String, - required: false, - default: '', - }, - // This is used to help uniquely create a monaco model - // even if two blob's share a file path. - fileGlobalId: { - type: String, - required: false, - default: '', - }, - }, - data() { - return { - editor: null, - }; - }, - watch: { - fileName(newVal) { - this.editor.updateModelLanguage(newVal); - }, - }, - mounted() { - this.editor = initSourceEditor({ - el: this.$refs.editor, - blobPath: this.fileName, - blobContent: this.value, - blobGlobalId: this.fileGlobalId, - }); - - this.editor.onDidChangeModelContent(debounce(this.onFileChange.bind(this), 250)); - - eventHub.$emit(SNIPPET_MEASURE_BLOBS_CONTENT); - }, - beforeDestroy() { - this.editor.dispose(); - }, - methods: { - onFileChange() { - this.$emit('input', this.editor.getValue()); - }, - }, -}; -</script> -<template> - <div class="file-content code"> - <div id="editor" ref="editor" data-editor-loading> - <pre class="editor-loading-content">{{ value }}</pre> - </div> - </div> -</template> diff --git a/app/assets/javascripts/blob/utils.js b/app/assets/javascripts/blob/utils.js index 92c6277d4f1..b2400a4b224 100644 --- a/app/assets/javascripts/blob/utils.js +++ b/app/assets/javascripts/blob/utils.js @@ -1,19 +1,5 @@ -import Editor from '~/editor/source_editor'; import { getBaseURL } from '~/lib/utils/url_utility'; -export function initSourceEditor({ el, ...args }) { - const editor = new Editor({ - scrollbar: { - alwaysConsumeMouseWheel: false, - }, - }); - - return editor.createInstance({ - el, - ...args, - }); -} - const blameLinesPerPage = document.querySelector('.js-per-page')?.dataset?.blamePerPage; export const getPageParamValue = (lineNum, blamePerPage = blameLinesPerPage) => { diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue b/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue index f4292183f7b..a2d8b7cbd15 100644 --- a/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue +++ b/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue @@ -24,6 +24,11 @@ export default { SafeHtml: GlSafeHtmlDirective, }, props: { + isFirstChunk: { + type: Boolean, + required: false, + default: false, + }, chunkIndex: { type: Number, required: false, @@ -74,6 +79,11 @@ export default { }, created() { + if (this.isFirstChunk) { + this.isLoading = false; + return; + } + window.requestIdleCallback(() => { this.isLoading = false; const { hash } = this.$route; diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue b/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue index 27752d4576a..f621a23734a 100644 --- a/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue @@ -203,6 +203,7 @@ export default { :content="firstChunk.content" :starting-from="firstChunk.startingFrom" :is-highlighted="firstChunk.isHighlighted" + is-first-chunk :language="firstChunk.language" :blame-path="blob.blamePath" /> diff --git a/app/views/award_emoji/_awards_block.html.haml b/app/views/award_emoji/_awards_block.html.haml index 6bdf1f7aae1..5062599c261 100644 --- a/app/views/award_emoji/_awards_block.html.haml +++ b/app/views/award_emoji/_awards_block.html.haml @@ -8,15 +8,19 @@ - grouped_emojis = awardable.grouped_awards(with_thumbs: inline) .awards.js-awards-block{ class: ("hidden" if !inline && grouped_emojis.empty?), data: { award_url: toggle_award_url(awardable) } } - awards_sort(grouped_emojis).each do |emoji, awards| - = render Pajamas::ButtonComponent.new(button_options: { class: "#{award_state_class(awardable, awards, current_user)} award-control js-emoji-btn", title: award_user_list(awards, current_user), data: { toggle: 'tooltip', container: 'body' } }) do + %button.gl-button.btn.btn-default.award-control.js-emoji-btn.has-tooltip{ type: "button", + class: [award_state_class(awardable, awards, current_user)], + data: { title: award_user_list(awards, current_user) } } = emoji_icon(emoji) %span.award-control-text.js-counter = awards.count - if can?(current_user, :award_emoji, awardable) .award-menu-holder.js-award-holder - = render Pajamas::ButtonComponent.new(button_text_classes: 'gl-display-flex', button_options: { title: _('Add reaction'), class: 'js-add-award award-control has-tooltip', 'aria-label': _('Add reaction') }) do - = sprite_icon('slight-smile', css_class: 'award-control-icon-neutral gl-icon gl-mr-0!') - = sprite_icon('smiley', css_class: 'award-control-icon-positive gl-icon') - = sprite_icon('smile', css_class: 'award-control-icon-super-positive gl-icon') + %button.gl-button.btn.btn-default.award-control.has-tooltip.js-add-award{ type: 'button', + 'aria-label': _('Add reaction'), + data: { title: _('Add reaction') } } + %span{ class: "award-control-icon award-control-icon-neutral gl-icon" }= sprite_icon('slight-smile') + %span{ class: "award-control-icon award-control-icon-positive gl-icon" }= sprite_icon('smiley') + %span{ class: "award-control-icon award-control-icon-super-positive gl-icon" }= sprite_icon('smile') = yield diff --git a/app/views/notify/pipeline_failed_email.html.haml b/app/views/notify/pipeline_failed_email.html.haml index 4e8d8a20ef1..5d4d2c0fcd8 100644 --- a/app/views/notify/pipeline_failed_email.html.haml +++ b/app/views/notify/pipeline_failed_email.html.haml @@ -6,7 +6,7 @@ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;line-height:1;" } %img{ alt: "✖", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-x-red-inverted.gif'), style: "display:block;", width: "13" }/ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" } - Pipeline ##{@pipeline.id} has failed! + = s_('Notify|Pipeline #%{pipeline_id} has failed!') % { pipeline_id: @pipeline.id } %tr.spacer %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" } @@ -15,7 +15,8 @@ %table.table-info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" } %tbody %tr - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" } Project + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" } + = _('Project') %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;" } - namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name - namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner) @@ -25,7 +26,8 @@ %a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" } = @project.name %tr - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Branch + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } + = _('Branch') %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" } %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" } %tbody @@ -36,7 +38,8 @@ %a.muted{ href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;" } = @pipeline.source_ref %tr - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Commit + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } + = _('Commit') %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:400;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" } %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" } %tbody @@ -54,7 +57,8 @@ = @pipeline.git_commit_message.truncate(50) - commit = @pipeline.commit %tr - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Commit Author + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } + = s_('Notify|Commit Author') %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" } %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" } %tbody @@ -93,11 +97,10 @@ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" } %tbody %tr - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;font-weight:500;line-height:1.4;vertical-align:baseline;" } - Pipeline - %a{ href: pipeline_url(@pipeline), style: "color:#3777b0;text-decoration:none;" } - = "\##{@pipeline.id}" - triggered by + - common_style = "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;font-weight:500;line-height:1.4;vertical-align:baseline;" + - pipeline_link = link_to "##{@pipeline.id}", pipeline_url(@pipeline), style: "color:#3777b0;text-decoration:none;" + %td{ style: "#{common_style}" } + = s_('Notify|Pipeline %{pipeline_link} triggered by').html_safe % { pipeline_link: pipeline_link } - if @pipeline.user %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;padding-left:5px", width: "24" } %img.avatar{ height: "24", src: avatar_icon_for_user(@pipeline.user, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/ |