diff options
Diffstat (limited to 'app/assets')
18 files changed, 297 insertions, 145 deletions
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js index 43a5325cf71..8259133c95b 100644 --- a/app/assets/javascripts/gfm_auto_complete.js +++ b/app/assets/javascripts/gfm_auto_complete.js @@ -132,9 +132,8 @@ class GfmAutoComplete { callbacks: { ...this.getDefaultCallbacks(), matcher(flag, subtext) { - const relevantText = subtext.trim().split(/\s/).pop(); const regexp = new RegExp(`(?:[^${glRegexp.unicodeLetters}0-9:]|\n|^):([^:]*)$`, 'gi'); - const match = regexp.exec(relevantText); + const match = regexp.exec(subtext); return match && match.length ? match[1] : null; }, diff --git a/app/assets/javascripts/monitoring/components/graph.vue b/app/assets/javascripts/monitoring/components/graph.vue index 9e67a6f2146..42615d2bb8e 100644 --- a/app/assets/javascripts/monitoring/components/graph.vue +++ b/app/assets/javascripts/monitoring/components/graph.vue @@ -209,6 +209,7 @@ const xAxis = d3.axisBottom() .scale(axisXScale) + .ticks(this.graphWidth / 120) .tickFormat(timeScaleFormat); const yAxis = d3.axisLeft() diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index c640003d958..6d1b2f452c0 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -16,6 +16,10 @@ import Autosize from 'autosize'; import 'vendor/jquery.caret'; // required by jquery.atwho import 'vendor/jquery.atwho'; import AjaxCache from '~/lib/utils/ajax_cache'; +import Vue from 'vue'; +import syntaxHighlight from '~/syntax_highlight'; +import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue'; +import { __ } from '~/locale'; import axios from './lib/utils/axios_utils'; import { getLocationHash } from './lib/utils/url_utility'; import Flash from './flash'; @@ -99,6 +103,13 @@ export default class Notes { $('.note-edit-form').clone() .addClass('mr-note-edit-form').insertAfter('.note-edit-form'); } + + const hash = getLocationHash(); + const $anchor = hash && document.getElementById(hash); + + if ($anchor) { + this.loadLazyDiff({ currentTarget: $anchor }); + } } setViewType(view) { @@ -135,6 +146,8 @@ export default class Notes { this.$wrapperEl.on('click', '.js-close-discussion-note-form', this.cancelDiscussionForm); // toggle commit list this.$wrapperEl.on('click', '.system-note-commit-list-toggler', this.toggleCommitList); + + this.$wrapperEl.on('click', '.js-toggle-lazy-diff', this.loadLazyDiff); // fetch notes when tab becomes visible this.$wrapperEl.on('visibilitychange', this.visibilityChange); // when issue status changes, we need to refresh data @@ -173,6 +186,7 @@ export default class Notes { this.$wrapperEl.off('keydown', '.js-note-text'); this.$wrapperEl.off('click', '.js-comment-resolve-button'); this.$wrapperEl.off('click', '.system-note-commit-list-toggler'); + this.$wrapperEl.off('click', '.js-toggle-lazy-diff'); this.$wrapperEl.off('ajax:success', '.js-main-target-form'); this.$wrapperEl.off('ajax:success', '.js-discussion-note-form'); this.$wrapperEl.off('ajax:complete', '.js-main-target-form'); @@ -1207,6 +1221,60 @@ export default class Notes { return this.notesCountBadge.text(parseInt(this.notesCountBadge.text(), 10) + updateCount); } + static renderPlaceholderComponent($container) { + const el = $container.find('.js-code-placeholder').get(0); + new Vue({ // eslint-disable-line no-new + el, + components: { + SkeletonLoadingContainer, + }, + render(createElement) { + return createElement('skeleton-loading-container'); + }, + }); + } + + static renderDiffContent($container, data) { + const { discussion_html } = data; + const lines = $(discussion_html).find('.line_holder'); + lines.addClass('fade-in'); + $container.find('tbody').prepend(lines); + const fileHolder = $container.find('.file-holder'); + $container.find('.line-holder-placeholder').remove(); + syntaxHighlight(fileHolder); + } + + static renderDiffError($container) { + $container.find('.line_content').html( + $(` + <div class="nothing-here-block"> + ${__('Unable to load the diff.')} <a class="js-toggle-lazy-diff" href="javascript:void(0)">Try again</a>? + </div> + `), + ); + } + + loadLazyDiff(e) { + const $container = $(e.currentTarget).closest('.js-toggle-container'); + Notes.renderPlaceholderComponent($container); + + $container.find('.js-toggle-lazy-diff').removeClass('js-toggle-lazy-diff'); + + const tableEl = $container.find('tbody'); + if (tableEl.length === 0) return; + + const fileHolder = $container.find('.file-holder'); + const url = fileHolder.data('linesPath'); + + axios.get(url) + .then(({ data }) => { + Notes.renderDiffContent($container, data); + }) + .catch(() => { + Notes.renderDiffError($container); + }); + } + toggleCommitList(e) { const $element = $(e.currentTarget); const $closestSystemCommitList = $element.siblings('.system-note-commit-list'); diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue index 1785be01a0d..42bc383f4d2 100644 --- a/app/assets/javascripts/notes/components/comment_form.vue +++ b/app/assets/javascripts/notes/components/comment_form.vue @@ -1,6 +1,6 @@ <script> import $ from 'jquery'; - import { mapActions, mapGetters } from 'vuex'; + import { mapActions, mapGetters, mapState } from 'vuex'; import _ from 'underscore'; import Autosize from 'autosize'; import { __, sprintf } from '~/locale'; @@ -53,6 +53,9 @@ 'getNotesData', 'openState', ]), + ...mapState([ + 'isToggleStateButtonLoading', + ]), noteableDisplayName() { return this.noteableType.replace(/_/g, ' '); }, @@ -143,6 +146,7 @@ 'closeIssue', 'reopenIssue', 'toggleIssueLocalState', + 'toggleStateButtonLoading', ]), setIsSubmitButtonDisabled(note, isSubmitting) { if (!_.isEmpty(note) && !isSubmitting) { @@ -170,13 +174,14 @@ if (this.noteType === constants.DISCUSSION) { noteData.data.note.type = constants.DISCUSSION_NOTE; } + this.note = ''; // Empty textarea while being requested. Repopulate in catch this.resizeTextarea(); this.stopPolling(); this.saveNote(noteData) .then((res) => { - this.isSubmitting = false; + this.enableButton(); this.restartPolling(); if (res.errors) { @@ -198,7 +203,7 @@ } }) .catch(() => { - this.isSubmitting = false; + this.enableButton(); this.discard(false); const msg = `Your comment could not be submitted! @@ -220,6 +225,7 @@ Please check your network connection and try again.`; .then(() => this.enableButton()) .catch(() => { this.enableButton(); + this.toggleStateButtonLoading(false); Flash( sprintf( __('Something went wrong while closing the %{issuable}. Please try again later'), @@ -232,6 +238,7 @@ Please check your network connection and try again.`; .then(() => this.enableButton()) .catch(() => { this.enableButton(); + this.toggleStateButtonLoading(false); Flash( sprintf( __('Something went wrong while reopening the %{issuable}. Please try again later'), @@ -419,13 +426,13 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown" <loading-button v-if="canUpdateIssue" - :loading="isSubmitting" + :loading="isToggleStateButtonLoading" @click="handleSave(true)" :container-class="[ actionButtonClassNames, 'btn btn-comment btn-comment-and-close js-action-button' ]" - :disabled="isSubmitting" + :disabled="isToggleStateButtonLoading || isSubmitting" :label="issueActionButtonTitle" /> diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js index dc0e3c39775..ebbacb576d6 100644 --- a/app/assets/javascripts/notes/stores/actions.js +++ b/app/assets/javascripts/notes/stores/actions.js @@ -71,21 +71,32 @@ export const toggleResolveNote = ({ commit }, { endpoint, isResolved, discussion commit(mutationType, res); }); -export const closeIssue = ({ commit, dispatch, state }) => service +export const closeIssue = ({ commit, dispatch, state }) => { + dispatch('toggleStateButtonLoading', true); + return service .toggleIssueState(state.notesData.closePath) .then(res => res.json()) .then((data) => { commit(types.CLOSE_ISSUE); dispatch('emitStateChangedEvent', data); + dispatch('toggleStateButtonLoading', false); }); +}; -export const reopenIssue = ({ commit, dispatch, state }) => service +export const reopenIssue = ({ commit, dispatch, state }) => { + dispatch('toggleStateButtonLoading', true); + return service .toggleIssueState(state.notesData.reopenPath) .then(res => res.json()) .then((data) => { commit(types.REOPEN_ISSUE); dispatch('emitStateChangedEvent', data); + dispatch('toggleStateButtonLoading', false); }); +}; + +export const toggleStateButtonLoading = ({ commit }, value) => + commit(types.TOGGLE_STATE_BUTTON_LOADING, value); export const emitStateChangedEvent = ({ commit, getters }, data) => { const event = new CustomEvent('issuable_vue_app:change', { detail: { diff --git a/app/assets/javascripts/notes/stores/index.js b/app/assets/javascripts/notes/stores/index.js index 488a9ca38d3..9ed19bf171e 100644 --- a/app/assets/javascripts/notes/stores/index.js +++ b/app/assets/javascripts/notes/stores/index.js @@ -12,6 +12,9 @@ export default new Vuex.Store({ targetNoteHash: null, lastFetchedAt: null, + // View layer + isToggleStateButtonLoading: false, + // holds endpoints and permissions provided through haml notesData: {}, userData: {}, diff --git a/app/assets/javascripts/notes/stores/mutation_types.js b/app/assets/javascripts/notes/stores/mutation_types.js index da1b5a9e51a..b455e23ecde 100644 --- a/app/assets/javascripts/notes/stores/mutation_types.js +++ b/app/assets/javascripts/notes/stores/mutation_types.js @@ -17,3 +17,4 @@ export const UPDATE_DISCUSSION = 'UPDATE_DISCUSSION'; // Issue export const CLOSE_ISSUE = 'CLOSE_ISSUE'; export const REOPEN_ISSUE = 'REOPEN_ISSUE'; +export const TOGGLE_STATE_BUTTON_LOADING = 'TOGGLE_STATE_BUTTON_LOADING'; diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js index 949628a65c0..9308daa36f1 100644 --- a/app/assets/javascripts/notes/stores/mutations.js +++ b/app/assets/javascripts/notes/stores/mutations.js @@ -199,4 +199,8 @@ export default { [types.REOPEN_ISSUE](state) { Object.assign(state.noteableData, { state: constants.REOPENED }); }, + + [types.TOGGLE_STATE_BUTTON_LOADING](state, value) { + Object.assign(state, { isToggleStateButtonLoading: value }); + }, }; diff --git a/app/assets/javascripts/pages/ci/lints/create/index.js b/app/assets/javascripts/pages/ci/lints/new/index.js index 8e8a843da0b..8e8a843da0b 100644 --- a/app/assets/javascripts/pages/ci/lints/create/index.js +++ b/app/assets/javascripts/pages/ci/lints/new/index.js diff --git a/app/assets/javascripts/performance_bar.js b/app/assets/javascripts/performance_bar.js index ef44e2323ef..c22598ee665 100644 --- a/app/assets/javascripts/performance_bar.js +++ b/app/assets/javascripts/performance_bar.js @@ -14,8 +14,6 @@ export default class PerformanceBar { init(opts) { const $container = $(opts.container); - this.$sqlProfileLink = $container.find('.js-toggle-modal-peek-sql'); - this.$sqlProfileModal = $container.find('#modal-peek-pg-queries'); this.$lineProfileLink = $container.find('.js-toggle-modal-peek-line-profile'); this.$lineProfileModal = $('#modal-peek-line-profile'); this.initEventListeners(); @@ -23,7 +21,6 @@ export default class PerformanceBar { } initEventListeners() { - this.$sqlProfileLink.on('click', () => this.handleSQLProfileLink()); this.$lineProfileLink.on('click', e => this.handleLineProfileLink(e)); $(document).on('click', '.js-lineprof-file', PerformanceBar.toggleLineProfileFile); } @@ -36,10 +33,6 @@ export default class PerformanceBar { } } - handleSQLProfileLink() { - PerformanceBar.toggleModal(this.$sqlProfileModal); - } - handleLineProfileLink(e) { const lineProfilerParameter = getParameterValues('lineprofiler'); const lineProfilerParameterRegex = new RegExp(`lineprofiler=${lineProfilerParameter[0]}`); diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue new file mode 100644 index 00000000000..7bef2e97349 --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue @@ -0,0 +1,144 @@ +<script> +import timeagoMixin from '../../vue_shared/mixins/timeago'; +import tooltip from '../../vue_shared/directives/tooltip'; +import LoadingButton from '../../vue_shared/components/loading_button.vue'; +import { visitUrl } from '../../lib/utils/url_utility'; +import createFlash from '../../flash'; +import MemoryUsage from './memory_usage.vue'; +import StatusIcon from './mr_widget_status_icon.vue'; +import MRWidgetService from '../services/mr_widget_service'; + +export default { + name: 'Deployment', + components: { + LoadingButton, + MemoryUsage, + StatusIcon, + }, + directives: { + tooltip, + }, + mixins: [ + timeagoMixin, + ], + props: { + deployment: { + type: Object, + required: true, + }, + }, + data() { + return { + isStopping: false, + }; + }, + computed: { + deployTimeago() { + return this.timeFormated(this.deployment.deployed_at); + }, + hasExternalUrls() { + return !!(this.deployment.external_url && this.deployment.external_url_formatted); + }, + hasDeploymentTime() { + return !!(this.deployment.deployed_at && this.deployment.deployed_at_formatted); + }, + hasDeploymentMeta() { + return !!(this.deployment.url && this.deployment.name); + }, + hasMetrics() { + return !!(this.deployment.metrics_url); + }, + }, + methods: { + stopEnvironment() { + const msg = 'Are you sure you want to stop this environment?'; + const isConfirmed = confirm(msg); // eslint-disable-line + + if (isConfirmed) { + this.isStopping = true; + + MRWidgetService.stopEnvironment(this.deployment.stop_url) + .then(res => res.data) + .then((data) => { + if (data.redirect_url) { + visitUrl(data.redirect_url); + } + + this.isStopping = false; + }) + .catch(() => { + createFlash('Something went wrong while stopping this environment. Please try again.'); + this.isStopping = false; + }); + } + }, + }, +}; +</script> + +<template> + <div class="mr-widget-heading deploy-heading"> + <div class="ci-widget media"> + <div class="ci-status-icon ci-status-icon-success"> + <span class="js-icon-link icon-link"> + <status-icon status="success" /> + </span> + </div> + <div class="media-body"> + <div class="deploy-body"> + <template v-if="hasDeploymentMeta"> + <span> + Deployed to + </span> + <a + :href="deployment.url" + target="_blank" + rel="noopener noreferrer nofollow" + class="deploy-link js-deploy-meta" + > + {{ deployment.name }} + </a> + </template> + <template v-if="hasExternalUrls"> + <span> + on + </span> + <a + :href="deployment.external_url" + target="_blank" + rel="noopener noreferrer nofollow" + class="deploy-link js-deploy-url" + > + <i + class="fa fa-external-link" + aria-hidden="true" + > + </i> + {{ deployment.external_url_formatted }} + </a> + </template> + <span + v-if="hasDeploymentTime" + v-tooltip + :title="deployment.deployed_at_formatted" + class="js-deploy-time" + > + {{ deployTimeago }} + </span> + <loading-button + v-if="deployment.stop_url" + container-class="btn btn-default btn-xs prepend-left-default" + label="Stop environment" + :loading="isStopping" + @click="stopEnvironment" + /> + </div> + <memory-usage + v-if="hasMetrics" + :metrics-url="deployment.metrics_url" + :metrics-monitoring-url="deployment.metrics_monitoring_url" + /> + </div> + </div> + </div> +</template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js deleted file mode 100644 index c7f992384c8..00000000000 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js +++ /dev/null @@ -1,113 +0,0 @@ -import { getTimeago } from '~/lib/utils/datetime_utility'; -import { visitUrl } from '../../lib/utils/url_utility'; -import Flash from '../../flash'; -import MemoryUsage from './memory_usage.vue'; -import StatusIcon from './mr_widget_status_icon.vue'; -import MRWidgetService from '../services/mr_widget_service'; - -export default { - name: 'MRWidgetDeployment', - props: { - mr: { type: Object, required: true }, - service: { type: Object, required: true }, - }, - components: { - MemoryUsage, - StatusIcon, - }, - methods: { - formatDate(date) { - return getTimeago().format(date); - }, - hasExternalUrls(deployment = {}) { - return deployment.external_url && deployment.external_url_formatted; - }, - hasDeploymentTime(deployment = {}) { - return deployment.deployed_at && deployment.deployed_at_formatted; - }, - hasDeploymentMeta(deployment = {}) { - return deployment.url && deployment.name; - }, - stopEnvironment(deployment) { - const msg = 'Are you sure you want to stop this environment?'; - const isConfirmed = confirm(msg); // eslint-disable-line - - if (isConfirmed) { - MRWidgetService.stopEnvironment(deployment.stop_url) - .then(res => res.data) - .then((data) => { - if (data.redirect_url) { - visitUrl(data.redirect_url); - } - }) - .catch(() => { - new Flash('Something went wrong while stopping this environment. Please try again.'); // eslint-disable-line - }); - } - }, - }, - template: ` - <div class="mr-widget-heading deploy-heading"> - <div v-for="deployment in mr.deployments"> - <div class="ci-widget media"> - <div class="ci-status-icon ci-status-icon-success"> - <span class="js-icon-link icon-link"> - <status-icon status="success" /> - </span> - </div> - <div class="media-body space-children"> - <span> - <span - v-if="hasDeploymentMeta(deployment)"> - Deployed to - </span> - <a - v-if="hasDeploymentMeta(deployment)" - :href="deployment.url" - target="_blank" - rel="noopener noreferrer nofollow" - class="js-deploy-meta inline"> - {{deployment.name}} - </a> - <span - v-if="hasExternalUrls(deployment)"> - on - </span> - <a - v-if="hasExternalUrls(deployment)" - :href="deployment.external_url" - target="_blank" - rel="noopener noreferrer nofollow" - class="js-deploy-url inline"> - <i - class="fa fa-external-link" - aria-hidden="true" /> - {{deployment.external_url_formatted}} - </a> - <span - v-if="hasDeploymentTime(deployment)" - :data-title="deployment.deployed_at_formatted" - class="js-deploy-time" - data-toggle="tooltip" - data-placement="top"> - {{formatDate(deployment.deployed_at)}} - </span> - </span> - <button - type="button" - v-if="deployment.stop_url" - @click="stopEnvironment(deployment)" - class="btn btn-default btn-xs"> - Stop environment - </button> - <memory-usage - v-if="deployment.metrics_url" - :metrics-url="deployment.metrics_url" - :metrics-monitoring-url="deployment.metrics_monitoring_url" - /> - </div> - </div> - </div> - </div> - `, -}; diff --git a/app/assets/javascripts/vue_merge_request_widget/dependencies.js b/app/assets/javascripts/vue_merge_request_widget/dependencies.js index 20624aad0ad..efbe1c96d1c 100644 --- a/app/assets/javascripts/vue_merge_request_widget/dependencies.js +++ b/app/assets/javascripts/vue_merge_request_widget/dependencies.js @@ -14,7 +14,7 @@ export { default as SmartInterval } from '~/smart_interval'; export { default as WidgetHeader } from './components/mr_widget_header.vue'; export { default as WidgetMergeHelp } from './components/mr_widget_merge_help.vue'; export { default as WidgetPipeline } from './components/mr_widget_pipeline.vue'; -export { default as WidgetDeployment } from './components/mr_widget_deployment'; +export { default as Deployment } from './components/deployment.vue'; export { default as WidgetMaintainerEdit } from './components/mr_widget_maintainer_edit.vue'; export { default as WidgetRelatedLinks } from './components/mr_widget_related_links.vue'; export { default as MergedState } from './components/states/mr_widget_merged.vue'; diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js index cc8bc6af1e1..169adfe0a1d 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js @@ -5,7 +5,7 @@ import { WidgetHeader, WidgetMergeHelp, WidgetPipeline, - WidgetDeployment, + Deployment, WidgetMaintainerEdit, WidgetRelatedLinks, MergedState, @@ -67,9 +67,6 @@ export default { shouldRenderRelatedLinks() { return !!this.mr.relatedLinks && !this.mr.isNothingToMergeState; }, - shouldRenderDeployments() { - return this.mr.deployments.length; - }, shouldRenderSourceBranchRemovalStatus() { return !this.mr.canRemoveSourceBranch && this.mr.shouldRemoveSourceBranch && (!this.mr.isNothingToMergeState && !this.mr.isMergedState); @@ -216,7 +213,7 @@ export default { 'mr-widget-header': WidgetHeader, 'mr-widget-merge-help': WidgetMergeHelp, 'mr-widget-pipeline': WidgetPipeline, - 'mr-widget-deployment': WidgetDeployment, + Deployment, 'mr-widget-maintainer-edit': WidgetMaintainerEdit, 'mr-widget-related-links': WidgetRelatedLinks, 'mr-widget-merged': MergedState, @@ -250,10 +247,11 @@ export default { :ci-status="mr.ciStatus" :has-ci="mr.hasCI" /> - <mr-widget-deployment - v-if="shouldRenderDeployments" - :mr="mr" - :service="service" /> + <deployment + v-for="deployment in mr.deployments" + :key="deployment.id" + :deployment="deployment" + /> <div class="mr-widget-section"> <component :is="componentName" diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss index ddd9dbb2be4..e12b5aab381 100644 --- a/app/assets/stylesheets/framework/mixins.scss +++ b/app/assets/stylesheets/framework/mixins.scss @@ -17,8 +17,6 @@ */ @mixin markdown-table { width: auto; - display: block; - overflow-x: auto; } /* diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index 8b680c2dc52..b487f6278c2 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -194,8 +194,6 @@ .commit-actions { @media (min-width: $screen-sm-min) { - font-size: 0; - .fa-spinner { font-size: 12px; } @@ -204,7 +202,7 @@ .ci-status-link { display: inline-block; position: relative; - top: 1px; + top: 2px; } .btn-clipboard, @@ -226,7 +224,7 @@ .ci-status-icon { position: relative; - top: 1px; + top: 2px; } } diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index f887a11004f..4692d0fb873 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -718,6 +718,8 @@ } .mr-memory-usage { + width: 100%; + p.usage-info-loading .usage-info-load-spinner { margin-right: 10px; font-size: 16px; @@ -727,3 +729,36 @@ .fork-sprite { margin-right: -5px; } + +.deploy-heading { + .media-body { + min-width: 0; + } +} + +.deploy-body { + display: flex; + flex-wrap: wrap; + + @media (min-width: $screen-xs) { + flex-wrap: nowrap; + white-space: nowrap; + } + + > *:not(:last-child) { + margin-right: .3em; + } +} + +.deploy-link { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + min-width: 100px; + max-width: 150px; + + @media (min-width: $screen-xs) { + min-width: 0; + max-width: 100%; + } +} diff --git a/app/assets/stylesheets/pages/wiki.scss b/app/assets/stylesheets/pages/wiki.scss index e70a57c2a67..9a0ec936979 100644 --- a/app/assets/stylesheets/pages/wiki.scss +++ b/app/assets/stylesheets/pages/wiki.scss @@ -180,6 +180,11 @@ ul.wiki-pages-list.content-list { } } +.wiki-holder { + overflow-x: auto; + overflow-y: hidden; +} + .wiki { table { @include markdown-table; |