Commit 3271c5f0 authored by Mike Greiling's avatar Mike Greiling

Prettify vue_shared modules

parent 8b7c86ea
......@@ -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() : ''] || '';
}
......@@ -41,10 +41,14 @@ export default {
},
},
mounted() {
$(this.$el).on('shown.bs.modal', this.opened).on('hidden.bs.modal', this.closed);
$(this.$el)
.on('shown.bs.modal', this.opened)
.on('hidden.bs.modal', this.closed);
},
beforeDestroy() {
$(this.$el).off('shown.bs.modal', this.opened).off('hidden.bs.modal', this.closed);
$(this.$el)
.off('shown.bs.modal', this.opened)
.off('hidden.bs.modal', this.closed);
},
methods: {
emitCancel(event) {
......
<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