Commit 1dfecf3e authored by Fatih Acet's avatar Fatih Acet

Merge branch 'prettify-all-the-things-6' into 'master'

Prettify all the things (part 6)

See merge request gitlab-org/gitlab-ce!22256
parents 3e3f8ac8 3271c5f0
......@@ -22,9 +22,7 @@ export default {
directives: {
tooltip,
},
mixins: [
timeagoMixin,
],
mixins: [timeagoMixin],
props: {
deployment: {
type: Object,
......@@ -50,7 +48,7 @@ export default {
return !!(this.deployment.url && this.deployment.name);
},
hasMetrics() {
return !!(this.deployment.metrics_url);
return !!this.deployment.metrics_url;
},
},
methods: {
......@@ -63,7 +61,7 @@ export default {
MRWidgetService.stopEnvironment(this.deployment.stop_url)
.then(res => res.data)
.then((data) => {
.then(data => {
if (data.redirect_url) {
visitUrl(data.redirect_url);
}
......
......@@ -60,11 +60,29 @@ export default {
let memoryUsageMsg = '';
if (memoryTo > memoryFrom) {
memoryUsageMsg = sprintf(s__('mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} increased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB'), messageProps, false);
memoryUsageMsg = sprintf(
s__(
'mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} increased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB',
),
messageProps,
false,
);
} else if (memoryTo < memoryFrom) {
memoryUsageMsg = sprintf(s__('mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} decreased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB'), messageProps, false);
memoryUsageMsg = sprintf(
s__(
'mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} decreased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB',
),
messageProps,
false,
);
} else {
memoryUsageMsg = sprintf(s__('mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasisStart} unchanged %{emphasisEnd} at %{memoryFrom}MB'), messageProps, false);
memoryUsageMsg = sprintf(
s__(
'mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasisStart} unchanged %{emphasisEnd} at %{memoryFrom}MB',
),
messageProps,
false,
);
}
return memoryUsageMsg;
......@@ -77,7 +95,7 @@ export default {
methods: {
getMegabytes(bytesString) {
const valueInBytes = Number(bytesString).toFixed(2);
return (bytesToMiB(valueInBytes)).toFixed(2);
return bytesToMiB(valueInBytes).toFixed(2);
},
computeGraphData(metrics, deploymentTime) {
this.loadingMetrics = false;
......@@ -103,7 +121,7 @@ export default {
loadMetrics() {
backOff((next, stop) => {
MRWidgetService.fetchMetrics(this.metricsUrl)
.then((res) => {
.then(res => {
if (res.status === statusCodes.NO_CONTENT) {
this.backOffRequestCounter += 1;
/* eslint-disable no-unused-expressions */
......@@ -114,14 +132,14 @@ export default {
})
.catch(stop);
})
.then((res) => {
.then(res => {
if (res.status === statusCodes.NO_CONTENT) {
return res;
}
return res.data;
})
.then((data) => {
.then(data => {
this.computeGraphData(data.metrics, data.deployment_time);
return data;
})
......
<script>
import tooltip from '../../vue_shared/directives/tooltip';
import tooltip from '../../vue_shared/directives/tooltip';
export default {
name: 'MrWidgetAuthor',
directives: {
tooltip,
export default {
name: 'MrWidgetAuthor',
directives: {
tooltip,
},
props: {
author: {
type: Object,
required: true,
},
props: {
author: {
type: Object,
required: true,
},
showAuthorName: {
type: Boolean,
required: false,
default: true,
},
showAuthorTooltip: {
type: Boolean,
required: false,
default: false,
},
showAuthorName: {
type: Boolean,
required: false,
default: true,
},
computed: {
authorUrl() {
return this.author.webUrl || this.author.web_url;
},
avatarUrl() {
return this.author.avatarUrl || this.author.avatar_url;
},
showAuthorTooltip: {
type: Boolean,
required: false,
default: false,
},
};
},
computed: {
authorUrl() {
return this.author.webUrl || this.author.web_url;
},
avatarUrl() {
return this.author.avatarUrl || this.author.avatar_url;
},
},
};
</script>
<template>
<a
......
<script>
import tooltip from '~/vue_shared/directives/tooltip';
import MrWidgetAuthor from './mr_widget_author.vue';
import tooltip from '~/vue_shared/directives/tooltip';
import MrWidgetAuthor from './mr_widget_author.vue';
export default {
name: 'MrWidgetAuthorTime',
components: {
MrWidgetAuthor,
export default {
name: 'MrWidgetAuthorTime',
components: {
MrWidgetAuthor,
},
directives: {
tooltip,
},
props: {
actionText: {
type: String,
required: true,
},
directives: {
tooltip,
author: {
type: Object,
required: true,
},
props: {
actionText: {
type: String,
required: true,
},
author: {
type: Object,
required: true,
},
dateTitle: {
type: String,
required: true,
},
dateReadable: {
type: String,
required: true,
},
dateTitle: {
type: String,
required: true,
},
};
dateReadable: {
type: String,
required: true,
},
},
};
</script>
<template>
<h4 class="js-mr-widget-author">
......
<script>
import { sprintf, s__ } from '~/locale';
import { sprintf, s__ } from '~/locale';
export default {
name: 'MRWidgetMergeHelp',
props: {
missingBranch: {
type: String,
required: false,
default: '',
},
export default {
name: 'MRWidgetMergeHelp',
props: {
missingBranch: {
type: String,
required: false,
default: '',
},
computed: {
missingBranchInfo() {
return sprintf(
s__('mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the'),
{ branch: this.missingBranch },
);
},
},
computed: {
missingBranchInfo() {
return sprintf(
s__(
'mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the',
),
{ branch: this.missingBranch },
);
},
};
},
};
</script>
<template>
<section class="mr-widget-help">
......
<script>
import { s__ } from '~/locale';
import { s__ } from '~/locale';
export default {
name: 'MRWidgetRelatedLinks',
props: {
relatedLinks: {
type: Object,
required: true,
default: () => ({}),
},
state: {
type: String,
required: false,
default: '',
},
export default {
name: 'MRWidgetRelatedLinks',
props: {
relatedLinks: {
type: Object,
required: true,
default: () => ({}),
},
computed: {
closesText() {
if (this.state === 'merged') {
return s__('mrWidget|Closed');
}
if (this.state === 'closed') {
return s__('mrWidget|Did not close');
}
return s__('mrWidget|Closes');
},
state: {
type: String,
required: false,
default: '',
},
};
},
computed: {
closesText() {
if (this.state === 'merged') {
return s__('mrWidget|Closed');
}
if (this.state === 'closed') {
return s__('mrWidget|Did not close');
}
return s__('mrWidget|Closes');
},
},
};
</script>
<template>
<section class="mr-info-list mr-links">
......
<script>
import ciIcon from '../../vue_shared/components/ci_icon.vue';
import ciIcon from '../../vue_shared/components/ci_icon.vue';
export default {
components: {
ciIcon,
export default {
components: {
ciIcon,
},
props: {
status: {
type: String,
required: true,
},
props: {
status: {
type: String,
required: true,
},
showDisabledButton: {
type: Boolean,
required: false,
default: false,
},
showDisabledButton: {
type: Boolean,
required: false,
default: false,
},
computed: {
isLoading() {
return this.status === 'loading';
},
statusObj() {
return {
group: this.status,
icon: `status_${this.status}`,
};
},
},
computed: {
isLoading() {
return this.status === 'loading';
},
};
statusObj() {
return {
group: this.status,
icon: `status_${this.status}`,
};
},
},
};
</script>
<template>
<div class="space-children d-flex append-right-10 widget-status-icon">
......
<script>
import tooltip from '../../vue_shared/directives/tooltip';
import { __ } from '../../locale';
import tooltip from '../../vue_shared/directives/tooltip';
import { __ } from '../../locale';
export default {
directives: {
tooltip,
},
created() {
this.removesBranchText = __('<strong>Removes</strong> source branch');
this.tooltipTitle = __('A user with write access to the source branch selected this option');
},
};
export default {
directives: {
tooltip,
},
created() {
this.removesBranchText = __('<strong>Removes</strong> source branch');
this.tooltipTitle = __('A user with write access to the source branch selected this option');
},
};
</script>
<template>
......
<script>
import statusIcon from '../mr_widget_status_icon.vue';
import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetArchived',
components: {
statusIcon,
},
};
export default {
name: 'MRWidgetArchived',
components: {
statusIcon,
},
};
</script>
<template>
<div class="mr-widget-body media">
......
<script>
import eventHub from '../../event_hub';
import statusIcon from '../mr_widget_status_icon.vue';
import eventHub from '../../event_hub';
import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetAutoMergeFailed',
components: {
statusIcon,
export default {
name: 'MRWidgetAutoMergeFailed',
components: {
statusIcon,
},
props: {
mr: {
type: Object,
required: true,
},
props: {
mr: {
type: Object,
required: true,
},
},
data() {
return {
isRefreshing: false,
};
},
methods: {
refreshWidget() {
this.isRefreshing = true;
eventHub.$emit('MRWidgetUpdateRequested', () => {
this.isRefreshing = false;
});
},
data() {
return {
isRefreshing: false,
};
},
methods: {
refreshWidget() {
this.isRefreshing = true;
eventHub.$emit('MRWidgetUpdateRequested', () => {
this.isRefreshing = false;
});
},
},
};
},
};
</script>
<template>
<div class="mr-widget-body media">
......
<script>
import statusIcon from '../mr_widget_status_icon.vue';
import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetChecking',
components: {
statusIcon,
},
};
export default {
name: 'MRWidgetChecking',
components: {
statusIcon,
},
};
</script>
<template>
<div class="mr-widget-body media">
......
<script>
import MrWidgetAuthorTime from '../../components/mr_widget_author_time.vue';
import statusIcon from '../mr_widget_status_icon.vue';
import MrWidgetAuthorTime from '../../components/mr_widget_author_time.vue';
import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetClosed',
components: {
MrWidgetAuthorTime,
statusIcon,
},
props: {
/* TODO: This is providing all store and service down when it
export default {
name: 'MRWidgetClosed',
components: {
MrWidgetAuthorTime,
statusIcon,
},
props: {
/* TODO: This is providing all store and service down when it
only needs metrics and targetBranch */
mr: {
type: Object,
required: true,
default: () => ({}),
},
mr: {
type: Object,
required: true,
default: () => ({}),
},
};
},
};
</script>
<template>
<div class="mr-widget-body media">
......
<script>
import statusIcon from '../mr_widget_status_icon.vue';
import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetConflicts',
components: {
statusIcon,
},
props: {
/* TODO: This is providing all store and service down when it
export default {
name: 'MRWidgetConflicts',
components: {
statusIcon,
},
props: {
/* TODO: This is providing all store and service down when it
only needs a few props */
mr: {
type: Object,
required: true,
default: () => ({}),
},
mr: {
type: Object,
required: true,
default: () => ({}),
},
};
},
};
</script>
<template>
<div class="mr-widget-body media">
......
<script>
import Flash from '../../../flash';
import statusIcon from '../mr_widget_status_icon.vue';
import MrWidgetAuthor from '../../components/mr_widget_author.vue';
import eventHub from '../../event_hub';
import Flash from '../../../flash';
import statusIcon from '../mr_widget_status_icon.vue';
import MrWidgetAuthor from '../../components/mr_widget_author.vue';
import eventHub from '../../event_hub';
export default {
name: 'MRWidgetMergeWhenPipelineSucceeds',
components: {
MrWidgetAuthor,
statusIcon,
export default {
name: 'MRWidgetMergeWhenPipelineSucceeds',
components: {
MrWidgetAuthor,
statusIcon,
},
props: {
mr: {
type: Object,
required: true,
default: () => ({}),
},
props: {
mr: {
type: Object,
required: true,
default: () => ({}),
},
service: {
type: Object,
required: true,
default: () => ({}),
},
service: {
type: Object,
required: true,
default: () => ({}),
},
data() {
return {
isCancellingAutoMerge: false,
isRemovingSourceBranch: false,
};
},
computed: {
canRemoveSourceBranch() {
const {
shouldRemoveSourceBranch,
canRemoveSourceBranch,
mergeUserId,
currentUserId,
} = this.mr;
},
data() {
return {
isCancellingAutoMerge: false,
isRemovingSourceBranch: false,
};
},
computed: {
canRemoveSourceBranch() {
const {
shouldRemoveSourceBranch,
canRemoveSourceBranch,
mergeUserId,
currentUserId,
} = this.mr;
return !shouldRemoveSourceBranch &&
canRemoveSourceBranch &&
mergeUserId === currentUserId;
},
return !shouldRemoveSourceBranch && canRemoveSourceBranch && mergeUserId === currentUserId;
},
methods: {
cancelAutomaticMerge() {
this.isCancellingAutoMerge = true;
this.service.cancelAutomaticMerge()
.then(res => res.data)
.then((data) => {
eventHub.$emit('UpdateWidgetData', data);
})
.catch(() => {
this.isCancellingAutoMerge = false;
Flash('Something went wrong. Please try again.');
});
},
removeSourceBranch() {
const options = {
sha: this.mr.sha,
merge_when_pipeline_succeeds: true,
should_remove_source_branch: true,
};
},
methods: {
cancelAutomaticMerge() {
this.isCancellingAutoMerge = true;
this.service
.cancelAutomaticMerge()
.then(res => res.data)
.then(data => {
eventHub.$emit('UpdateWidgetData', data);
})
.catch(() => {
this.isCancellingAutoMerge = false;
Flash('Something went wrong. Please try again.');
});
},
removeSourceBranch() {
const options = {
sha: this.mr.sha,
merge_when_pipeline_succeeds: true,
should_remove_source_branch: true,
};
this.isRemovingSourceBranch = true;
this.service.merge(options)
.then(res => res.data)
.then((data) => {
if (data.status === 'merge_when_pipeline_succeeds') {
eventHub.$emit('MRWidgetUpdateRequested');
}
})
.catch(() => {
this.isRemovingSourceBranch = false;
Flash('Something went wrong. Please try again.');
});
},
this.isRemovingSourceBranch = true;
this.service
.merge(options)
.then(res => res.data)
.then(data => {
if (data.status === 'merge_when_pipeline_succeeds') {
eventHub.$emit('MRWidgetUpdateRequested');
}
})
.catch(() => {
this.isRemovingSourceBranch = false;
Flash('Something went wrong. Please try again.');
});
},
};
},
};
</script>
<template>
<div class="mr-widget-body media">
......
<script>
import Flash from '~/flash';
import tooltip from '~/vue_shared/directives/tooltip';
import { s__, __ } from '~/locale';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import MrWidgetAuthorTime from '../../components/mr_widget_author_time.vue';
import statusIcon from '../mr_widget_status_icon.vue';
import eventHub from '../../event_hub';
import Flash from '~/flash';
import tooltip from '~/vue_shared/directives/tooltip';
import { s__, __ } from '~/locale';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import MrWidgetAuthorTime from '../../components/mr_widget_author_time.vue';
import statusIcon from '../mr_widget_status_icon.vue';
import eventHub from '../../event_hub';
export default {
name: 'MRWidgetMerged',
directives: {
tooltip,
export default {
name: 'MRWidgetMerged',
directives: {
tooltip,
},
components: {
MrWidgetAuthorTime,
statusIcon,
ClipboardButton,
},
props: {
mr: {
type: Object,
required: true,
default: () => ({}),
},
components: {
MrWidgetAuthorTime,
statusIcon,
ClipboardButton,
service: {
type: Object,
required: true,
default: () => ({}),
},
props: {
mr: {
type: Object,
required: true,
default: () => ({}),
},
service: {
type: Object,
required: true,
default: () => ({}),
},
},
data() {
return {
isMakingRequest: false,
};
},
computed: {
shouldShowRemoveSourceBranch() {
const { sourceBranchRemoved, isRemovingSourceBranch, canRemoveSourceBranch } = this.mr;
return (
!sourceBranchRemoved &&
canRemoveSourceBranch &&
!this.isMakingRequest &&
!isRemovingSourceBranch
);
},
data() {
return {
isMakingRequest: false,
};
shouldShowSourceBranchRemoving() {
const { sourceBranchRemoved, isRemovingSourceBranch } = this.mr;
return !sourceBranchRemoved && (isRemovingSourceBranch || this.isMakingRequest);
},
computed: {
shouldShowRemoveSourceBranch() {
const {
sourceBranchRemoved,
isRemovingSourceBranch,
canRemoveSourceBranch,
} = this.mr;
return !sourceBranchRemoved &&
canRemoveSourceBranch &&
!this.isMakingRequest &&
!isRemovingSourceBranch;
},
shouldShowSourceBranchRemoving() {
const {
sourceBranchRemoved,
isRemovingSourceBranch,
} = this.mr;
return !sourceBranchRemoved &&
(isRemovingSourceBranch || this.isMakingRequest);
},
shouldShowMergedButtons() {
const {
canRevertInCurrentMR,
canCherryPickInCurrentMR,
revertInForkPath,
cherryPickInForkPath,
} = this.mr;
shouldShowMergedButtons() {
const {
canRevertInCurrentMR,
canCherryPickInCurrentMR,
revertInForkPath,
cherryPickInForkPath,
} = this.mr;
return canRevertInCurrentMR ||
canCherryPickInCurrentMR ||
revertInForkPath ||
cherryPickInForkPath;
},
revertTitle() {
return s__('mrWidget|Revert this merge request in a new merge request');
},
cherryPickTitle() {
return s__('mrWidget|Cherry-pick this merge request in a new merge request');
},
revertLabel() {
return s__('mrWidget|Revert');
},
cherryPickLabel() {
return s__('mrWidget|Cherry-pick');
},
return (
canRevertInCurrentMR || canCherryPickInCurrentMR || revertInForkPath || cherryPickInForkPath
);
},
revertTitle() {
return s__('mrWidget|Revert this merge request in a new merge request');
},
cherryPickTitle() {
return s__('mrWidget|Cherry-pick this merge request in a new merge request');
},
revertLabel() {
return s__('mrWidget|Revert');
},
cherryPickLabel() {
return s__('mrWidget|Cherry-pick');
},
methods: {
removeSourceBranch() {
this.isMakingRequest = true;
},
methods: {
removeSourceBranch() {
this.isMakingRequest = true;
this.service.removeSourceBranch()
.then(res => res.data)
.then((data) => {
if (data.message === 'Branch was removed') {
eventHub.$emit('MRWidgetUpdateRequested', () => {
this.isMakingRequest = false;
});
}
})
.catch(() => {
this.isMakingRequest = false;
Flash(__('Something went wrong. Please try again.'));
});
},
this.service
.removeSourceBranch()
.then(res => res.data)
.then(data => {
if (data.message === 'Branch was removed') {
eventHub.$emit('MRWidgetUpdateRequested', () => {
this.isMakingRequest = false;
});
}
})
.catch(() => {
this.isMakingRequest = false;
Flash(__('Something went wrong. Please try again.'));
});
},
};
},
};
</script>
<template>
<div class="mr-widget-body media">
......
<script>
import statusIcon from '../mr_widget_status_icon.vue';
import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetMerging',
components: {
statusIcon,
export default {
name: 'MRWidgetMerging',
components: {
statusIcon,
},
props: {
mr: {
type: Object,
required: true,
default: () => ({}),
},
props: {
mr: {
type: Object,
required: true,
default: () => ({}),
},
},
};
},
};
</script>
<template>
<div class="mr-widget-body mr-state-locked media">
......
<script>
import { sprintf, s__ } from '~/locale';
import tooltip from '~/vue_shared/directives/tooltip';
import statusIcon from '../mr_widget_status_icon.vue';
import mrWidgetMergeHelp from '../../components/mr_widget_merge_help.vue';
import { sprintf, s__ } from '~/locale';
import tooltip from '~/vue_shared/directives/tooltip';
import statusIcon from '../mr_widget_status_icon.vue';
import mrWidgetMergeHelp from '../../components/mr_widget_merge_help.vue';
export default {
name: 'MRWidgetMissingBranch',
directives: {
tooltip,
export default {
name: 'MRWidgetMissingBranch',
directives: {
tooltip,
},
components: {
mrWidgetMergeHelp,
statusIcon,
},
props: {
mr: {
type: Object,
required: true,
},
components: {
mrWidgetMergeHelp,
statusIcon,
},
computed: {
missingBranchName() {
return this.mr.sourceBranchRemoved ? 'source' : 'target';
},
props: {
mr: {
type: Object,
required: true,
},
},
computed: {
missingBranchName() {
return this.mr.sourceBranchRemoved ? 'source' : 'target';
},
missingBranchNameMessage() {
return sprintf(s__('mrWidget| Please restore it or use a different %{missingBranchName} branch'), {
missingBranchNameMessage() {
return sprintf(
s__('mrWidget| Please restore it or use a different %{missingBranchName} branch'),
{
missingBranchName: this.missingBranchName,
});
},
message() {
return sprintf(s__('mrWidget|If the %{missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line'), {
},
);
},
message() {
return sprintf(
s__(
'mrWidget|If the %{missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line',
),
{
missingBranchName: this.missingBranchName,
});
},
},
);
},
};
},
};
</script>
<template>
<div class="mr-widget-body media">
......
<script>
import StatusIcon from '../mr_widget_status_icon.vue';
import StatusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetNotAllowed',
components: {
StatusIcon,
},
};
export default {
name: 'MRWidgetNotAllowed',
components: {
StatusIcon,
},
};
</script>
<template>
......
<script>
import StatusIcon from '../mr_widget_status_icon.vue';
import StatusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetPipelineBlocked',
components: {
StatusIcon,
},
};
export default {
name: 'MRWidgetPipelineBlocked',
components: {
StatusIcon,
},
};
</script>
<template>
<div class="mr-widget-body media">
......
<script>
import simplePoll from '../../../lib/utils/simple_poll';
import eventHub from '../../event_hub';
import statusIcon from '../mr_widget_status_icon.vue';
import Flash from '../../../flash';
import simplePoll from '../../../lib/utils/simple_poll';
import eventHub from '../../event_hub';
import statusIcon from '../mr_widget_status_icon.vue';
import Flash from '../../../flash';
export default {
name: 'MRWidgetRebase',
components: {
statusIcon,
export default {
name: 'MRWidgetRebase',
components: {
statusIcon,
},
props: {
mr: {
type: Object,
required: true,
},
props: {
mr: {
type: Object,
required: true,
},
service: {
type: Object,
required: true,
},
service: {
type: Object,
required: true,
},
data() {
return {
isMakingRequest: false,
rebasingError: null,
};
},
data() {
return {
isMakingRequest: false,
rebasingError: null,
};
},
computed: {
status() {
if (this.mr.rebaseInProgress || this.isMakingRequest) {
return 'loading';
}
if (!this.mr.canPushToSourceBranch && !this.mr.rebaseInProgress) {
return 'warning';
}
return 'success';
},
computed: {
status() {
if (this.mr.rebaseInProgress || this.isMakingRequest) {
return 'loading';
}
if (!this.mr.canPushToSourceBranch && !this.mr.rebaseInProgress) {
return 'warning';
}
return 'success';
},
showDisabledButton() {
return ['failed', 'loading'].includes(this.status);
},
showDisabledButton() {
return ['failed', 'loading'].includes(this.status);
},
methods: {
rebase() {
this.isMakingRequest = true;
this.rebasingError = null;
},
methods: {
rebase() {
this.isMakingRequest = true;
this.rebasingError = null;
this.service.rebase()
.then(() => {
simplePoll(this.checkRebaseStatus);
})
.catch((error) => {
this.rebasingError = error.merge_error;
this.service
.rebase()
.then(() => {
simplePoll(this.checkRebaseStatus);
})
.catch(error => {
this.rebasingError = error.merge_error;
this.isMakingRequest = false;
Flash('Something went wrong. Please try again.');
});
},
checkRebaseStatus(continuePolling, stopPolling) {
this.service
.poll()
.then(res => res.data)
.then(res => {
if (res.rebase_in_progress) {
continuePolling();
} else {
this.isMakingRequest = false;
Flash('Something went wrong. Please try again.');
});
},
checkRebaseStatus(continuePolling, stopPolling) {
this.service.poll()
.then(res => res.data)
.then((res) => {
if (res.rebase_in_progress) {
continuePolling();
} else {
this.isMakingRequest = false;
if (res.merge_error && res.merge_error.length) {
this.rebasingError = res.merge_error;
Flash('Something went wrong. Please try again.');
}
eventHub.$emit('MRWidgetUpdateRequested');
stopPolling();
if (res.merge_error && res.merge_error.length) {
this.rebasingError = res.merge_error;
Flash('Something went wrong. Please try again.');
}
})
.catch(() => {
this.isMakingRequest = false;
Flash('Something went wrong. Please try again.');
eventHub.$emit('MRWidgetUpdateRequested');
stopPolling();
});
},
}
})
.catch(() => {
this.isMakingRequest = false;
Flash('Something went wrong. Please try again.');
stopPolling();
});
},
};
},
};
</script>
<template>
<div class="mr-widget-body media">
......
......@@ -24,9 +24,10 @@ export default {
methods: {
removeWIP() {
this.isMakingRequest = true;
this.service.removeWIP()
this.service
.removeWIP()
.then(res => res.data)
.then((data) => {
.then(data => {
eventHub.$emit('UpdateWidgetData', data);
new window.Flash('The merge request can now be merged.', 'notice'); // eslint-disable-line
$('.merge-request .detail-page-description .title').text(this.mr.title);
......
<script>
import Project from '~/pages/projects/project';
import SmartInterval from '~/smart_interval';
import createFlash from '../flash';
......@@ -100,8 +99,11 @@ export default {
return !!this.mr.relatedLinks && !this.mr.isNothingToMergeState;
},
shouldRenderSourceBranchRemovalStatus() {
return !this.mr.canRemoveSourceBranch && this.mr.shouldRemoveSourceBranch &&
(!this.mr.isNothingToMergeState && !this.mr.isMergedState);
return (
!this.mr.canRemoveSourceBranch &&
this.mr.shouldRemoveSourceBranch &&
(!this.mr.isNothingToMergeState && !this.mr.isMergedState)
);
},
},
created() {
......@@ -133,9 +135,10 @@ export default {
return new MRWidgetService(endpoints);
},
checkStatus(cb) {
return this.service.checkStatus()
return this.service
.checkStatus()
.then(res => res.data)
.then((data) => {
.then(data => {
this.handleNotification(data);
this.mr.setData(data);
this.setFaviconHelper();
......@@ -172,20 +175,24 @@ export default {
return Promise.resolve();
},
fetchDeployments() {
return this.service.fetchDeployments()
return this.service
.fetchDeployments()
.then(res => res.data)
.then((data) => {
.then(data => {
if (data.length) {
this.mr.deployments = data;
}
})
.catch(() => {
createFlash('Something went wrong while fetching the environments for this merge request. Please try again.');
createFlash(
'Something went wrong while fetching the environments for this merge request. Please try again.',
);
});
},
fetchActionsContent() {
this.service.fetchMergeActionsContent()
.then((res) => {
this.service
.fetchMergeActionsContent()
.then(res => {
if (res.data) {
const el = document.createElement('div');
el.innerHTML = res.data;
......@@ -212,22 +219,22 @@ export default {
this.pollingInterval.stopTimer();
},
bindEventHubListeners() {
eventHub.$on('MRWidgetUpdateRequested', (cb) => {
eventHub.$on('MRWidgetUpdateRequested', cb => {
this.checkStatus(cb);
});
// `params` should be an Array contains a Boolean, like `[true]`
// Passing parameter as Boolean didn't work.
eventHub.$on('SetBranchRemoveFlag', (params) => {
eventHub.$on('SetBranchRemoveFlag', params => {
[this.mr.isRemovingSourceBranch] = params;
});
eventHub.$on('FailedToMerge', (mergeError) => {
eventHub.$on('FailedToMerge', mergeError => {
this.mr.state = 'failedToMerge';
this.mr.mergeError = mergeError;
});
eventHub.$on('UpdateWidgetData', (data) => {
eventHub.$on('UpdateWidgetData', data => {
this.mr.setData(data);
});
......
......@@ -118,7 +118,9 @@ export default {
this.rectYAxisLabelDims.height != null ? this.rectYAxisLabelDims.height / 2 : 0;
const yCoord = this.vbHeight / 2 + rectWidth - 5;
return `translate(${this.minX + this.yAxisTextTransformPadding}, ${yCoord}) rotate(-${this.yAxisTextRotation})`;
return `translate(${this.minX + this.yAxisTextTransformPadding}, ${yCoord}) rotate(-${
this.yAxisTextRotation
})`;
},
},
mounted() {
......@@ -207,8 +209,7 @@ export default {
renderedYAxis.selectAll('.tick').each(function createTickLines(d, i) {
if (i > 0) {
d3
.select(this)
d3.select(this)
.select('line')
.attr('x2', width)
.attr('class', 'axis-tick');
......@@ -217,8 +218,7 @@ export default {
// Add the panning capabilities
if (this.isPanAvailable) {
d3
.select(this.$refs.baseSvg)
d3.select(this.$refs.baseSvg)
.call(this.zoom)
.on('wheel.zoom', null); // This disables the pan of the graph with the scroll of the mouse wheel
}
......
<script>
/* eslint-disable vue/require-default-prop */
export default {
name: 'DeprecatedModal', // use GlModal instead
/* eslint-disable vue/require-default-prop */
export default {
name: 'DeprecatedModal', // use GlModal instead
props: {
id: {
type: String,
required: false,
},
title: {
type: String,
required: false,
},
text: {
type: String,
required: false,
},
hideFooter: {
type: Boolean,
required: false,
default: false,
},
kind: {
type: String,
required: false,
default: 'primary',
},
modalDialogClass: {
type: String,
required: false,
default: '',
},
closeKind: {
type: String,
required: false,
default: 'default',
},
closeButtonLabel: {
type: String,
required: false,
default: 'Cancel',
},
primaryButtonLabel: {
type: String,
required: false,
default: '',
},
secondaryButtonLabel: {
type: String,
required: false,
default: '',
},
submitDisabled: {
type: Boolean,
required: false,
default: false,
},
props: {
id: {
type: String,
required: false,
},
title: {
type: String,
required: false,
},
text: {
type: String,
required: false,
},
hideFooter: {
type: Boolean,
required: false,
default: false,
},
kind: {
type: String,
required: false,
default: 'primary',
},
modalDialogClass: {
type: String,
required: false,
default: '',
},
closeKind: {
type: String,
required: false,
default: 'default',
},
closeButtonLabel: {
type: String,
required: false,
default: 'Cancel',
},
primaryButtonLabel: {
type: String,
required: false,
default: '',
},
secondaryButtonLabel: {
type: String,
required: false,
default: '',
},
submitDisabled: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
btnKindClass() {
return {
[`btn-${this.kind}`]: true,
};
},
btnCancelKindClass() {
return {
[`btn-${this.closeKind}`]: true,
};
},
computed: {
btnKindClass() {
return {
[`btn-${this.kind}`]: true,
};
},
btnCancelKindClass() {
return {
[`btn-${this.closeKind}`]: true,
};
},
},
methods: {
emitCancel(event) {
this.$emit('cancel', event);
},
emitSubmit(event) {
this.$emit('submit', event);
},
methods: {
emitCancel(event) {
this.$emit('cancel', event);
},
emitSubmit(event) {
this.$emit('submit', event);
},
};
},
};
</script>
<template>
......
......@@ -583,7 +583,5 @@ const fileNameIcons = {
};
export default function getIconForFile(name) {
return fileNameIcons[name] ||
fileExtensionIcons[name ? name.split('.').pop() : ''] ||
'';
return fileNameIcons[name] || fileExtensionIcons[name ? name.split('.').pop() : ''] || '';
}
<script>
// only allow classes in images.scss e.g. s12
const validSizes = [8, 10, 12, 16, 18, 24, 32, 48, 72];
let iconValidator = () => true;
......
<script>
import icon from '../../../vue_shared/components/icon.vue';
import icon from '../../../vue_shared/components/icon.vue';
export default {
components: {
icon,
export default {
components: {
icon,
},
props: {
isLocked: {
type: Boolean,
default: false,
required: false,
},
props: {
isLocked: {
type: Boolean,
default: false,
required: false,
},
isConfidential: {
type: Boolean,
default: false,
required: false,
},
isConfidential: {
type: Boolean,
default: false,
required: false,
},
computed: {
warningIcon() {
if (this.isConfidential) return 'eye-slash';
if (this.isLocked) return 'lock';
},
computed: {
warningIcon() {
if (this.isConfidential) return 'eye-slash';
if (this.isLocked) return 'lock';
return '';
},
isLockedAndConfidential() {
return this.isConfidential && this.isLocked;
},
return '';
},
};
isLockedAndConfidential() {
return this.isConfidential && this.isLocked;
},
},
};
</script>
<template>
<div class="issuable-note-warning">
......
<script>
/* eslint-disable vue/require-default-prop */
/* This is a re-usable vue component for rendering a button
/* eslint-disable vue/require-default-prop */
/* This is a re-usable vue component for rendering a button
that will probably be sending off ajax requests and need
to show the loading status by setting the `loading` option.
This can also be used for initial page load when you don't
......@@ -17,34 +17,34 @@
*/
export default {
props: {
loading: {
type: Boolean,
required: false,
default: false,
},
disabled: {
type: Boolean,
required: false,
default: false,
},
label: {
type: String,
required: false,
},
containerClass: {
type: [String, Array, Object],
required: false,
default: 'btn btn-align-content',
},
export default {
props: {
loading: {
type: Boolean,
required: false,
default: false,
},
methods: {
onClick(e) {
this.$emit('click', e);
},
disabled: {
type: Boolean,
required: false,
default: false,
},
};
label: {
type: String,
required: false,
},
containerClass: {
type: [String, Array, Object],
required: false,
default: 'btn btn-align-content',
},
},
methods: {
onClick(e) {
this.$emit('click', e);
},
},
};
</script>
<template>
......
<script>
import $ from 'jquery';
import { s__ } from '~/locale';
import Flash from '../../../flash';
import GLForm from '../../../gl_form';
import markdownHeader from './header.vue';
import markdownToolbar from './toolbar.vue';
import icon from '../icon.vue';
import $ from 'jquery';
import { s__ } from '~/locale';
import Flash from '../../../flash';
import GLForm from '../../../gl_form';
import markdownHeader from './header.vue';
import markdownToolbar from './toolbar.vue';
import icon from '../icon.vue';
export default {
components: {
markdownHeader,
markdownToolbar,
icon,
export default {
components: {
markdownHeader,
markdownToolbar,
icon,
},
props: {
markdownPreviewPath: {
type: String,
required: false,
default: '',
},
props: {
markdownPreviewPath: {
type: String,
required: false,
default: '',
},
markdownDocsPath: {
type: String,
required: true,
},
markdownVersion: {
type: Number,
required: false,
default: 0,
},
addSpacingClasses: {
type: Boolean,
required: false,
default: true,
},
quickActionsDocsPath: {
type: String,
required: false,
default: '',
},
canAttachFile: {
type: Boolean,
required: false,
default: true,
},
enableAutocomplete: {
type: Boolean,
required: false,
default: true,
},
markdownDocsPath: {
type: String,
required: true,
},
data() {
return {
markdownPreview: '',
referencedCommands: '',
referencedUsers: '',
markdownPreviewLoading: false,
previewMarkdown: false,
};
markdownVersion: {
type: Number,
required: false,
default: 0,
},
computed: {
shouldShowReferencedUsers() {
const referencedUsersThreshold = 10;
return this.referencedUsers.length >= referencedUsersThreshold;
},
addSpacingClasses: {
type: Boolean,
required: false,
default: true,
},
mounted() {
/*
GLForm class handles all the toolbar buttons
*/
return new GLForm($(this.$refs['gl-form']), {
emojis: this.enableAutocomplete,
members: this.enableAutocomplete,
issues: this.enableAutocomplete,
mergeRequests: this.enableAutocomplete,
epics: this.enableAutocomplete,
milestones: this.enableAutocomplete,
labels: this.enableAutocomplete,
snippets: this.enableAutocomplete,
});
quickActionsDocsPath: {
type: String,
required: false,
default: '',
},
beforeDestroy() {
const glForm = $(this.$refs['gl-form']).data('glForm');
if (glForm) {
glForm.destroy();
}
canAttachFile: {
type: Boolean,
required: false,
default: true,
},
enableAutocomplete: {
type: Boolean,
required: false,
default: true,
},
methods: {
showPreviewTab() {
if (this.previewMarkdown) return;
},
data() {
return {
markdownPreview: '',
referencedCommands: '',
referencedUsers: '',
markdownPreviewLoading: false,
previewMarkdown: false,
};
},
computed: {
shouldShowReferencedUsers() {
const referencedUsersThreshold = 10;
return this.referencedUsers.length >= referencedUsersThreshold;
},
},
mounted() {
/*
GLForm class handles all the toolbar buttons
*/
return new GLForm($(this.$refs['gl-form']), {
emojis: this.enableAutocomplete,
members: this.enableAutocomplete,
issues: this.enableAutocomplete,
mergeRequests: this.enableAutocomplete,
epics: this.enableAutocomplete,
milestones: this.enableAutocomplete,
labels: this.enableAutocomplete,
snippets: this.enableAutocomplete,
});
},
beforeDestroy() {
const glForm = $(this.$refs['gl-form']).data('glForm');
if (glForm) {
glForm.destroy();
}
},
methods: {
showPreviewTab() {
if (this.previewMarkdown) return;
this.previewMarkdown = true;
this.previewMarkdown = true;
/*
/*
Can't use `$refs` as the component is technically in the parent component
so we access the VNode & then get the element
*/
const text = this.$slots.textarea[0].elm.value;
const text = this.$slots.textarea[0].elm.value;
if (text) {
this.markdownPreviewLoading = true;
this.$http
.post(this.versionedPreviewPath(), { text })
.then(resp => resp.json())
.then(data => this.renderMarkdown(data))
.catch(() => new Flash(s__('Error loading markdown preview')));
} else {
this.renderMarkdown();
}
},
if (text) {
this.markdownPreviewLoading = true;
this.$http
.post(this.versionedPreviewPath(), { text })
.then(resp => resp.json())
.then(data => this.renderMarkdown(data))
.catch(() => new Flash(s__('Error loading markdown preview')));
} else {
this.renderMarkdown();
}
},
showWriteTab() {
this.markdownPreview = '';
this.previewMarkdown = false;
},
showWriteTab() {
this.markdownPreview = '';
this.previewMarkdown = false;
},
renderMarkdown(data = {}) {
this.markdownPreviewLoading = false;
this.markdownPreview = data.body || 'Nothing to preview.';
renderMarkdown(data = {}) {
this.markdownPreviewLoading = false;
this.markdownPreview = data.body || 'Nothing to preview.';
if (data.references) {
this.referencedCommands = data.references.commands;
this.referencedUsers = data.references.users;
}
if (data.references) {
this.referencedCommands = data.references.commands;
this.referencedUsers = data.references.users;
}
this.$nextTick(() => {
$(this.$refs['markdown-preview']).renderGFM();
});
},
this.$nextTick(() => {
$(this.$refs['markdown-preview']).renderGFM();
});
},
versionedPreviewPath() {
const { markdownPreviewPath, markdownVersion } = this;
return `${markdownPreviewPath}${
markdownPreviewPath.indexOf('?') === -1 ? '?' : '&'
}markdown_version=${markdownVersion}`;
},
versionedPreviewPath() {
const { markdownPreviewPath, markdownVersion } = this;
return `${markdownPreviewPath}${
markdownPreviewPath.indexOf('?') === -1 ? '?' : '&'
}markdown_version=${markdownVersion}`;
},
};
},
};
</script>
<template>
......
<script>
import $ from 'jquery';
import tooltip from '../../directives/tooltip';
import toolbarButton from './toolbar_button.vue';
import icon from '../icon.vue';
import $ from 'jquery';
import tooltip from '../../directives/tooltip';
import toolbarButton from './toolbar_button.vue';
import icon from '../icon.vue';
export default {
directives: {
tooltip,
export default {
directives: {
tooltip,
},
components: {
toolbarButton,
icon,
},
props: {
previewMarkdown: {
type: Boolean,
required: true,
},
components: {
toolbarButton,
icon,
},
computed: {
mdTable() {
return [
'| header | header |',
'| ------ | ------ |',
'| cell | cell |',
'| cell | cell |',
].join('\n');
},
props: {
previewMarkdown: {
type: Boolean,
required: true,
},
},
mounted() {
$(document).on('markdown-preview:show.vue', this.previewMarkdownTab);
$(document).on('markdown-preview:hide.vue', this.writeMarkdownTab);
},
beforeDestroy() {
$(document).off('markdown-preview:show.vue', this.previewMarkdownTab);
$(document).off('markdown-preview:hide.vue', this.writeMarkdownTab);
},
methods: {
isValid(form) {
return (
!form ||
(form.find('.js-vue-markdown-field').length && $(this.$el).closest('form')[0] === form[0])
);
},
computed: {
mdTable() {
return [
'| header | header |',
'| ------ | ------ |',
'| cell | cell |',
'| cell | cell |',
].join('\n');
},
},
mounted() {
$(document).on('markdown-preview:show.vue', this.previewMarkdownTab);
$(document).on('markdown-preview:hide.vue', this.writeMarkdownTab);
},
beforeDestroy() {
$(document).off('markdown-preview:show.vue', this.previewMarkdownTab);
$(document).off('markdown-preview:hide.vue', this.writeMarkdownTab);
},
methods: {
isValid(form) {
return !form ||
form.find('.js-vue-markdown-field').length &&
$(this.$el).closest('form')[0] === form[0];
},
previewMarkdownTab(event, form) {
if (event.target.blur) event.target.blur();
if (!this.isValid(form)) return;
previewMarkdownTab(event, form) {
if (event.target.blur) event.target.blur();
if (!this.isValid(form)) return;
this.$emit('preview-markdown');
},
this.$emit('preview-markdown');
},
writeMarkdownTab(event, form) {
if (event.target.blur) event.target.blur();
if (!this.isValid(form)) return;
writeMarkdownTab(event, form) {
if (event.target.blur) event.target.blur();
if (!this.isValid(form)) return;
this.$emit('write-markdown');
},
this.$emit('write-markdown');
},
};
},
};
</script>
<template>
......
<script>
import { Link } from '@gitlab-org/gitlab-ui';
import { Link } from '@gitlab-org/gitlab-ui';
export default {
components: {
'gl-link': Link,
export default {
components: {
'gl-link': Link,
},
props: {
markdownDocsPath: {
type: String,
required: true,
},
props: {
markdownDocsPath: {
type: String,
required: true,
},
quickActionsDocsPath: {
type: String,
required: false,
default: '',
},
canAttachFile: {
type: Boolean,
required: false,
default: true,
},
quickActionsDocsPath: {
type: String,
required: false,
default: '',
},
computed: {
hasQuickActionsDocsPath() {
return this.quickActionsDocsPath !== '';
},
canAttachFile: {
type: Boolean,
required: false,
default: true,
},
};
},
computed: {
hasQuickActionsDocsPath() {
return this.quickActionsDocsPath !== '';
},
},
};
</script>
<template>
......
<script>
import tooltip from '../../directives/tooltip';
import icon from '../icon.vue';
import tooltip from '../../directives/tooltip';
import icon from '../icon.vue';
export default {
components: {
icon,
export default {
components: {
icon,
},
directives: {
tooltip,
},
props: {
buttonTitle: {
type: String,
required: true,
},
directives: {
tooltip,
icon: {
type: String,
required: true,
},
props: {
buttonTitle: {
type: String,
required: true,
},
icon: {
type: String,
required: true,
},
tag: {
type: String,
required: true,
},
tagBlock: {
type: String,
required: false,
default: '',
},
tagSelect: {
type: String,
required: false,
default: '',
},
prepend: {
type: Boolean,
required: false,
default: false,
},
tag: {
type: String,
required: true,
},
};
tagBlock: {
type: String,
required: false,
default: '',
},
tagSelect: {
type: String,
required: false,
default: '',
},
prepend: {
type: Boolean,
required: false,
default: false,
},
},
};
</script>
<template>
......
......@@ -41,7 +41,8 @@ export default {
// Find metric timestamp which is closest to deploymentTime
timestampDiff = Math.abs(metricTimestamps[0] - median);
metricTimestamps.forEach((timestamp, index) => {
if (index === 0) { // Skip first element
if (index === 0) {
// Skip first element
return;
}
......
<script>
/**
* Common component to render a placeholder note and user information.
*
* This component needs to be used with a vuex store.
* That vuex store needs to have a `getUserData` getter that contains
* {
* path: String,
* avatar_url: String,
* name: String,
* username: String,
* }
*
* @example
* <placeholder-note
* :note="{body: 'This is a note'}"
* />
*/
import { mapGetters } from 'vuex';
import userAvatarLink from '../user_avatar/user_avatar_link.vue';
/**
* Common component to render a placeholder note and user information.
*
* This component needs to be used with a vuex store.
* That vuex store needs to have a `getUserData` getter that contains
* {
* path: String,
* avatar_url: String,
* name: String,
* username: String,
* }
*
* @example
* <placeholder-note
* :note="{body: 'This is a note'}"
* />
*/
import { mapGetters } from 'vuex';
import userAvatarLink from '../user_avatar/user_avatar_link.vue';
export default {
name: 'PlaceholderNote',
components: {
userAvatarLink,
export default {
name: 'PlaceholderNote',
components: {
userAvatarLink,
},
props: {
note: {
type: Object,
required: true,
},
props: {
note: {
type: Object,
required: true,
},
},
computed: {
...mapGetters([
'getUserData',
]),
},
};
},
computed: {
...mapGetters(['getUserData']),
},
};
</script>
<template>
......
<script>
/**
* Common component to render a placeholder system note.
*
* @example
* <placeholder-system-note
* :note="{ body: 'Commands are being applied'}"
* />
*/
export default {
name: 'PlaceholderSystemNote',
props: {
note: {
type: Object,
required: true,
},
/**
* Common component to render a placeholder system note.
*
* @example
* <placeholder-system-note
* :note="{ body: 'Commands are being applied'}"
* />
*/
export default {
name: 'PlaceholderSystemNote',
props: {
note: {
type: Object,
required: true,
},
};
},
};
</script>
<template>
......
<script>
export default {
props: {
startSize: {
type: Number,
required: true,
},
side: {
type: String,
required: true,
},
minSize: {
type: Number,
required: false,
default: 0,
},
maxSize: {
type: Number,
required: false,
default: Number.MAX_VALUE,
},
enabled: {
type: Boolean,
required: false,
default: true,
},
export default {
props: {
startSize: {
type: Number,
required: true,
},
data() {
return {
size: this.startSize,
};
side: {
type: String,
required: true,
},
computed: {
className() {
return `drag-${this.side}`;
},
cursorStyle() {
if (this.enabled) {
return { cursor: 'ew-resize' };
}
return {};
},
minSize: {
type: Number,
required: false,
default: 0,
},
methods: {
resetSize(e) {
e.preventDefault();
this.$emit('resize-start', this.size);
maxSize: {
type: Number,
required: false,
default: Number.MAX_VALUE,
},
enabled: {
type: Boolean,
required: false,
default: true,
},
},
data() {
return {
size: this.startSize,
};
},
computed: {
className() {
return `drag-${this.side}`;
},
cursorStyle() {
if (this.enabled) {
return { cursor: 'ew-resize' };
}
return {};
},
},
methods: {
resetSize(e) {
e.preventDefault();
this.$emit('resize-start', this.size);
this.size = this.startSize;
this.$emit('update:size', this.size);
this.size = this.startSize;
this.$emit('update:size', this.size);
// End resizing on next tick so that listeners can react to DOM changes
this.$nextTick(() => {
this.$emit('resize-end', this.size);
});
},
startDrag(e) {
if (this.enabled) {
e.preventDefault();
this.startPos = e.clientX;
this.currentStartSize = this.size;
document.addEventListener('mousemove', this.drag);
document.addEventListener('mouseup', this.endDrag, { once: true });
this.$emit('resize-start', this.size);
}
},
drag(e) {
// End resizing on next tick so that listeners can react to DOM changes
this.$nextTick(() => {
this.$emit('resize-end', this.size);
});
},
startDrag(e) {
if (this.enabled) {
e.preventDefault();
let moved = e.clientX - this.startPos;
if (this.side === 'left') moved = -moved;
let newSize = this.currentStartSize + moved;
if (newSize < this.minSize) {
newSize = this.minSize;
} else if (newSize > this.maxSize) {
newSize = this.maxSize;
}
this.size = newSize;
this.startPos = e.clientX;
this.currentStartSize = this.size;
document.addEventListener('mousemove', this.drag);
document.addEventListener('mouseup', this.endDrag, { once: true });
this.$emit('resize-start', this.size);
}
},
drag(e) {
e.preventDefault();
let moved = e.clientX - this.startPos;
if (this.side === 'left') moved = -moved;
let newSize = this.currentStartSize + moved;
if (newSize < this.minSize) {
newSize = this.minSize;
} else if (newSize > this.maxSize) {
newSize = this.maxSize;
}
this.size = newSize;
this.$emit('update:size', newSize);
},
endDrag(e) {
e.preventDefault();
document.removeEventListener('mousemove', this.drag);
this.$emit('resize-end', this.size);
},
this.$emit('update:size', newSize);
},
endDrag(e) {
e.preventDefault();
document.removeEventListener('mousemove', this.drag);
this.$emit('resize-end', this.size);
},
};
},
};
</script>
<template>
......
<script>
import Pikaday from 'pikaday';
import { parsePikadayDate, pikadayToString } from '../../lib/utils/datefix';
import Pikaday from 'pikaday';
import { parsePikadayDate, pikadayToString } from '../../lib/utils/datefix';
export default {
name: 'DatePicker',
props: {
label: {
type: String,
required: false,
default: 'Date picker',
},
selectedDate: {
type: Date,
required: false,
default: null,
},
minDate: {
type: Date,
required: false,
default: null,
},
maxDate: {
type: Date,
required: false,
default: null,
},
export default {
name: 'DatePicker',
props: {
label: {
type: String,
required: false,
default: 'Date picker',
},
mounted() {
this.calendar = new Pikaday({
field: this.$el.querySelector('.dropdown-menu-toggle'),
theme: 'gitlab-theme animate-picker',
format: 'yyyy-mm-dd',
container: this.$el,
defaultDate: this.selectedDate,
setDefaultDate: !!this.selectedDate,
minDate: this.minDate,
maxDate: this.maxDate,
parse: dateString => parsePikadayDate(dateString),
toString: date => pikadayToString(date),
onSelect: this.selected.bind(this),
onClose: this.toggled.bind(this),
});
this.$el.append(this.calendar.el);
this.calendar.show();
selectedDate: {
type: Date,
required: false,
default: null,
},
minDate: {
type: Date,
required: false,
default: null,
},
beforeDestroy() {
this.calendar.destroy();
maxDate: {
type: Date,
required: false,
default: null,
},
},
mounted() {
this.calendar = new Pikaday({
field: this.$el.querySelector('.dropdown-menu-toggle'),
theme: 'gitlab-theme animate-picker',
format: 'yyyy-mm-dd',
container: this.$el,
defaultDate: this.selectedDate,
setDefaultDate: !!this.selectedDate,
minDate: this.minDate,
maxDate: this.maxDate,
parse: dateString => parsePikadayDate(dateString),
toString: date => pikadayToString(date),
onSelect: this.selected.bind(this),
onClose: this.toggled.bind(this),
});
this.$el.append(this.calendar.el);
this.calendar.show();
},
beforeDestroy() {
this.calendar.destroy();
},
methods: {
selected(dateText) {
this.$emit('newDateSelected', this.calendar.toString(dateText));
},
methods: {
selected(dateText) {
this.$emit('newDateSelected', this.calendar.toString(dateText));
},
toggled() {
this.$emit('hidePicker');
},
toggled() {
this.$emit('hidePicker');
},
};
},
};
</script>
<template>
......
<script>
/* This is a re-usable vue component for rendering a project avatar that
/* This is a re-usable vue component for rendering a project avatar that
does not need to link to the project's profile. The image and an optional
tooltip can be configured by props passed to this component.
......@@ -16,70 +15,70 @@
*/
import defaultAvatarUrl from 'images/no_avatar.png';
import { placeholderImage } from '../../../lazy_loader';
import tooltip from '../../directives/tooltip';
import defaultAvatarUrl from 'images/no_avatar.png';
import { placeholderImage } from '../../../lazy_loader';
import tooltip from '../../directives/tooltip';
export default {
name: 'ProjectAvatarImage',
directives: {
tooltip,
export default {
name: 'ProjectAvatarImage',
directives: {
tooltip,
},
props: {
lazy: {
type: Boolean,
required: false,
default: false,
},
imgSrc: {
type: String,
required: false,
default: defaultAvatarUrl,
},
cssClasses: {
type: String,
required: false,
default: '',
},
imgAlt: {
type: String,
required: false,
default: 'project avatar',
},
size: {
type: Number,
required: false,
default: 20,
},
tooltipText: {
type: String,
required: false,
default: '',
},
tooltipPlacement: {
type: String,
required: false,
default: 'top',
},
},
computed: {
// API response sends null when gravatar is disabled and
// we provide an empty string when we use it inside project avatar link.
// In both cases we should render the defaultAvatarUrl
sanitizedSource() {
return this.imgSrc === '' || this.imgSrc === null ? defaultAvatarUrl : this.imgSrc;
},
resultantSrcAttribute() {
return this.lazy ? placeholderImage : this.sanitizedSource;
},
props: {
lazy: {
type: Boolean,
required: false,
default: false,
},
imgSrc: {
type: String,
required: false,
default: defaultAvatarUrl,
},
cssClasses: {
type: String,
required: false,
default: '',
},
imgAlt: {
type: String,
required: false,
default: 'project avatar',
},
size: {
type: Number,
required: false,
default: 20,
},
tooltipText: {
type: String,
required: false,
default: '',
},
tooltipPlacement: {
type: String,
required: false,
default: 'top',
},
tooltipContainer() {
return this.tooltipText ? 'body' : null;
},
computed: {
// API response sends null when gravatar is disabled and
// we provide an empty string when we use it inside project avatar link.
// In both cases we should render the defaultAvatarUrl
sanitizedSource() {
return this.imgSrc === '' || this.imgSrc === null ? defaultAvatarUrl : this.imgSrc;
},
resultantSrcAttribute() {
return this.lazy ? placeholderImage : this.sanitizedSource;
},
tooltipContainer() {
return this.tooltipText ? 'body' : null;
},
avatarSizeClass() {
return `s${this.size}`;
},
avatarSizeClass() {
return `s${this.size}`;
},
};
},
};
</script>
<template>
......
<script>
import DeprecatedModal from './deprecated_modal.vue';
import DeprecatedModal from './deprecated_modal.vue';
export default {
name: 'RecaptchaModal',
export default {
name: 'RecaptchaModal',
components: {
DeprecatedModal,
},
components: {
DeprecatedModal,
},
props: {
html: {
type: String,
required: false,
default: '',
},
props: {
html: {
type: String,
required: false,
default: '',
},
},
data() {
return {
script: {},
scriptSrc: 'https://www.google.com/recaptcha/api.js',
};
},
data() {
return {
script: {},
scriptSrc: 'https://www.google.com/recaptcha/api.js',
};
},
watch: {
html() {
this.appendRecaptchaScript();
},
watch: {
html() {
this.appendRecaptchaScript();
},
},
mounted() {
window.recaptchaDialogCallback = this.submit.bind(this);
},
mounted() {
window.recaptchaDialogCallback = this.submit.bind(this);
},
methods: {
appendRecaptchaScript() {
this.removeRecaptchaScript();
methods: {
appendRecaptchaScript() {
this.removeRecaptchaScript();
const script = document.createElement('script');
script.src = this.scriptSrc;
script.classList.add('js-recaptcha-script');
script.async = true;
script.defer = true;
const script = document.createElement('script');
script.src = this.scriptSrc;
script.classList.add('js-recaptcha-script');
script.async = true;
script.defer = true;
this.script = script;
this.script = script;
document.body.appendChild(script);
},
document.body.appendChild(script);
},
removeRecaptchaScript() {
if (this.script instanceof Element) this.script.remove();
},
removeRecaptchaScript() {
if (this.script instanceof Element) this.script.remove();
},
close() {
this.removeRecaptchaScript();
this.$emit('close');
},
close() {
this.removeRecaptchaScript();
this.$emit('close');
},
submit() {
this.$el.querySelector('form').submit();
},
submit() {
this.$el.querySelector('form').submit();
},
};
},
};
</script>
<template>
......
<script>
import datePicker from '../pikaday.vue';
import toggleSidebar from './toggle_sidebar.vue';
import collapsedCalendarIcon from './collapsed_calendar_icon.vue';
import { dateInWords } from '../../../lib/utils/datetime_utility';
import datePicker from '../pikaday.vue';
import toggleSidebar from './toggle_sidebar.vue';
import collapsedCalendarIcon from './collapsed_calendar_icon.vue';
import { dateInWords } from '../../../lib/utils/datetime_utility';
export default {
name: 'SidebarDatePicker',
components: {
datePicker,
toggleSidebar,
collapsedCalendarIcon,
},
props: {
blockClass: {
type: String,
required: false,
default: '',
},
collapsed: {
type: Boolean,
required: false,
default: true,
},
showToggleSidebar: {
type: Boolean,
required: false,
default: false,
},
isLoading: {
type: Boolean,
required: false,
default: false,
},
editable: {
type: Boolean,
required: false,
default: false,
},
label: {
type: String,
required: false,
default: 'Date picker',
},
selectedDate: {
type: Date,
required: false,
default: null,
},
minDate: {
type: Date,
required: false,
default: null,
},
maxDate: {
type: Date,
required: false,
default: null,
},
},
data() {
return {
editing: false,
};
},
computed: {
selectedAndEditable() {
return this.selectedDate && this.editable;
},
selectedDateWords() {
return dateInWords(this.selectedDate, true);
},
collapsedText() {
return this.selectedDateWords ? this.selectedDateWords : 'None';
},
},
methods: {
stopEditing() {
this.editing = false;
},
toggleDatePicker() {
this.editing = !this.editing;
},
newDateSelected(date = null) {
this.date = date;
this.editing = false;
this.$emit('saveDate', date);
},
toggleSidebar() {
this.$emit('toggleCollapse');
},
},
};
export default {
name: 'SidebarDatePicker',
components: {
datePicker,
toggleSidebar,
collapsedCalendarIcon,
},
props: {
blockClass: {
type: String,
required: false,
default: '',
},
collapsed: {
type: Boolean,
required: false,
default: true,
},
showToggleSidebar: {
type: Boolean,
required: false,
default: false,
},
isLoading: {
type: Boolean,
required: false,
default: false,
},
editable: {
type: Boolean,
required: false,
default: false,
},
label: {
type: String,
required: false,
default: 'Date picker',
},
selectedDate: {
type: Date,
required: false,
default: null,
},
minDate: {
type: Date,
required: false,
default: null,
},
maxDate: {
type: Date,
required: false,
default: null,
},
},
data() {
return {
editing: false,
};
},
computed: {
selectedAndEditable() {
return this.selectedDate && this.editable;
},
selectedDateWords() {
return dateInWords(this.selectedDate, true);
},
collapsedText() {
return this.selectedDateWords ? this.selectedDateWords : 'None';
},
},
methods: {
stopEditing() {
this.editing = false;
},
toggleDatePicker() {
this.editing = !this.editing;
},
newDateSelected(date = null) {
this.date = date;
this.editing = false;
this.$emit('saveDate', date);
},
toggleSidebar() {
this.$emit('toggleCollapse');
},
},
};
</script>
<template>
......
<script>
import { s__ } from '../../locale';
const PAGINATION_UI_BUTTON_LIMIT = 4;
const UI_LIMIT = 6;
const SPREAD = '...';
const PREV = s__('Pagination|Prev');
const NEXT = s__('Pagination|Next');
const FIRST = s__('Pagination|« First');
const LAST = s__('Pagination|Last »');
export default {
props: {
/**
import { s__ } from '../../locale';
const PAGINATION_UI_BUTTON_LIMIT = 4;
const UI_LIMIT = 6;
const SPREAD = '...';
const PREV = s__('Pagination|Prev');
const NEXT = s__('Pagination|Next');
const FIRST = s__('Pagination|« First');
const LAST = s__('Pagination|Last »');
export default {
props: {
/**
This function will take the information given by the pagination component
Here is an example `change` method:
......@@ -20,12 +20,12 @@
gl.utils.visitUrl(`?page=${pagenum}`);
},
*/
change: {
type: Function,
required: true,
},
change: {
type: Function,
required: true,
},
/**
/**
pageInfo will come from the headers of the API call
in the `.then` clause of the VueResource API call
there should be a function that contructs the pageInfo for this component
......@@ -41,94 +41,94 @@
previousPage: +headers['X-Prev-Page'],
});
*/
pageInfo: {
type: Object,
required: true,
},
pageInfo: {
type: Object,
required: true,
},
},
computed: {
prev() {
return this.pageInfo.previousPage;
},
next() {
return this.pageInfo.nextPage;
},
getItems() {
const total = this.pageInfo.totalPages;
const { page } = this.pageInfo;
const items = [];
if (page > 1) {
items.push({ title: FIRST, first: true });
}
if (page > 1) {
items.push({ title: PREV, prev: true });
} else {
items.push({ title: PREV, disabled: true, prev: true });
}
if (page > UI_LIMIT) items.push({ title: SPREAD, separator: true });
const start = Math.max(page - PAGINATION_UI_BUTTON_LIMIT, 1);
const end = Math.min(page + PAGINATION_UI_BUTTON_LIMIT, total);
for (let i = start; i <= end; i += 1) {
const isActive = i === page;
items.push({ title: i, active: isActive, page: true });
}
if (total - page > PAGINATION_UI_BUTTON_LIMIT) {
items.push({ title: SPREAD, separator: true, page: true });
}
if (page === total) {
items.push({ title: NEXT, disabled: true, next: true });
} else if (total - page >= 1) {
items.push({ title: NEXT, next: true });
}
if (total - page >= 1) {
items.push({ title: LAST, last: true });
}
return items;
},
showPagination() {
return this.pageInfo.totalPages > 1;
},
computed: {
prev() {
return this.pageInfo.previousPage;
},
next() {
return this.pageInfo.nextPage;
},
getItems() {
const total = this.pageInfo.totalPages;
const { page } = this.pageInfo;
const items = [];
if (page > 1) {
items.push({ title: FIRST, first: true });
}
if (page > 1) {
items.push({ title: PREV, prev: true });
} else {
items.push({ title: PREV, disabled: true, prev: true });
}
if (page > UI_LIMIT) items.push({ title: SPREAD, separator: true });
const start = Math.max(page - PAGINATION_UI_BUTTON_LIMIT, 1);
const end = Math.min(page + PAGINATION_UI_BUTTON_LIMIT, total);
for (let i = start; i <= end; i += 1) {
const isActive = i === page;
items.push({ title: i, active: isActive, page: true });
}
if (total - page > PAGINATION_UI_BUTTON_LIMIT) {
items.push({ title: SPREAD, separator: true, page: true });
}
if (page === total) {
items.push({ title: NEXT, disabled: true, next: true });
} else if (total - page >= 1) {
items.push({ title: NEXT, next: true });
}
if (total - page >= 1) {
items.push({ title: LAST, last: true });
}
return items;
},
showPagination() {
return this.pageInfo.totalPages > 1;
},
},
methods: {
changePage(text, isDisabled) {
if (isDisabled) return;
const { totalPages, nextPage, previousPage } = this.pageInfo;
switch (text) {
case SPREAD:
break;
case LAST:
this.change(totalPages);
break;
case NEXT:
this.change(nextPage);
break;
case PREV:
this.change(previousPage);
break;
case FIRST:
this.change(1);
break;
default:
this.change(+text);
break;
}
},
methods: {
changePage(text, isDisabled) {
if (isDisabled) return;
const { totalPages, nextPage, previousPage } = this.pageInfo;
switch (text) {
case SPREAD:
break;
case LAST:
this.change(totalPages);
break;
case NEXT:
this.change(nextPage);
break;
case PREV:
this.change(previousPage);
break;
case FIRST:
this.change(1);
break;
default:
this.change(+text);
break;
}
},
hideOnSmallScreen(item) {
return !item.first && !item.last && !item.next && !item.prev && !item.active;
},
hideOnSmallScreen(item) {
return !item.first && !item.last && !item.next && !item.prev && !item.active;
},
};
},
};
</script>
<template>
<div
......
......@@ -11,9 +11,7 @@ export default {
directives: {
tooltip,
},
mixins: [
timeagoMixin,
],
mixins: [timeagoMixin],
props: {
time: {
type: String,
......
<script>
import { s__ } from '../../locale';
import icon from './icon.vue';
import { s__ } from '../../locale';
import icon from './icon.vue';
const ICON_ON = 'status_success_borderless';
const ICON_OFF = 'status_failed_borderless';
const LABEL_ON = s__('ToggleButton|Toggle Status: ON');
const LABEL_OFF = s__('ToggleButton|Toggle Status: OFF');
const ICON_ON = 'status_success_borderless';
const ICON_OFF = 'status_failed_borderless';
const LABEL_ON = s__('ToggleButton|Toggle Status: ON');
const LABEL_OFF = s__('ToggleButton|Toggle Status: OFF');
export default {
components: {
icon,
},
export default {
components: {
icon,
},
model: {
prop: 'value',
event: 'change',
},
model: {
prop: 'value',
event: 'change',
},
props: {
name: {
type: String,
required: false,
default: null,
},
value: {
type: Boolean,
required: false,
default: null,
},
disabledInput: {
type: Boolean,
required: false,
default: false,
},
isLoading: {
type: Boolean,
required: false,
default: false,
},
props: {
name: {
type: String,
required: false,
default: null,
},
value: {
type: Boolean,
required: false,
default: null,
},
disabledInput: {
type: Boolean,
required: false,
default: false,
},
isLoading: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
toggleIcon() {
return this.value ? ICON_ON : ICON_OFF;
},
ariaLabel() {
return this.value ? LABEL_ON : LABEL_OFF;
},
computed: {
toggleIcon() {
return this.value ? ICON_ON : ICON_OFF;
},
ariaLabel() {
return this.value ? LABEL_ON : LABEL_OFF;
},
},
methods: {
toggleFeature() {
if (!this.disabledInput) this.$emit('change', !this.value);
},
methods: {
toggleFeature() {
if (!this.disabledInput) this.$emit('change', !this.value);
},
};
},
};
</script>
<template>
......
<script>
/* This is a re-usable vue component for rendering a user avatar wrapped in
a clickable link (likely to the user's profile). The link, image, and
tooltip can be configured by props passed to this component.
......
<script>
/* This is a re-usable vue component for rendering a user avatar svg (typically
for a blank state). It will receive styles comparable to the user avatar,
but no image is loaded, it isn't wrapped in a link, and tooltips aren't supported.
......@@ -42,4 +41,3 @@ export default {
v-html="svg"
/>
</template>
......@@ -4,10 +4,7 @@
*
* Components need to have `scope`, `page` and `requestData`
*/
import {
historyPushState,
buildUrlWithCurrentLocation,
} from '../../lib/utils/common_utils';
import { historyPushState, buildUrlWithCurrentLocation } from '../../lib/utils/common_utils';
export default {
methods: {
......@@ -24,12 +21,14 @@ export default {
// stop polling
this.poll.stop();
const queryString = Object.keys(parameters).map((parameter) => {
const value = parameters[parameter];
// update internal state for UI
this[parameter] = value;
return `${parameter}=${encodeURIComponent(value)}`;
}).join('&');
const queryString = Object.keys(parameters)
.map(parameter => {
const value = parameters[parameter];
// update internal state for UI
this[parameter] = value;
return `${parameter}=${encodeURIComponent(value)}`;
})
.join('&');
// update polling parameters
this.requestData = parameters;
......
......@@ -6,7 +6,7 @@ export default class ListLabel {
this.color = obj.color;
this.textColor = obj.text_color;
this.description = obj.description;
this.priority = (obj.priority !== null) ? obj.priority : Infinity;
this.priority = obj.priority !== null ? obj.priority : Infinity;
}
}
......
import {
__,
n__,
s__,
sprintf,
} from '../locale';
import { __, n__, s__, sprintf } from '../locale';
export default (Vue) => {
export default Vue => {
Vue.mixin({
methods: {
/**
......
......@@ -21,7 +21,7 @@ Vue.http.interceptors.push((request, next) => {
Vue.http.interceptors.push((request, next) => {
request.headers.set(csrf.headerKey, csrf.token);
next((response) => {
next(response => {
// Headers object has a `forEach` property that iterates through all values.
const headers = {};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment