BigW Consortium Gitlab

[ci skip] Fix more rules

parent 615f1927
...@@ -37,6 +37,16 @@ ...@@ -37,6 +37,16 @@
"import/no-commonjs": "error", "import/no-commonjs": "error",
"no-multiple-empty-lines": ["error", { "max": 1 }], "no-multiple-empty-lines": ["error", { "max": 1 }],
"promise/catch-or-return": "error", "promise/catch-or-return": "error",
"no-underscore-dangle": ["error", { "allow": ["__"]}] "no-underscore-dangle": ["error", { "allow": ["__"]}],
"vue/html-self-closing": ["error", {
"html": {
"void": "always",
"normal": "any",
"component": "always"
},
"svg": "always",
"math": "any"
}]
} }
} }
<script> <script>
export default { export default {
name: 'modal', name: 'Modal',
props: {
props: { title: {
title: { type: String,
type: String, required: false,
required: false, default: '',
}, },
text: { text: {
type: String, type: String,
required: false, required: false,
}, default: '',
hideFooter: { },
type: Boolean, hideFooter: {
required: false, type: Boolean,
default: false, required: false,
}, default: false,
kind: { },
type: String, kind: {
required: false, type: String,
default: 'primary', required: false,
}, default: 'primary',
modalDialogClass: { },
type: String, modalDialogClass: {
required: false, type: String,
default: '', required: false,
}, default: '',
closeKind: { },
type: String, closeKind: {
required: false, type: String,
default: 'default', required: false,
}, default: 'default',
closeButtonLabel: { },
type: String, closeButtonLabel: {
required: false, type: String,
default: 'Cancel', required: false,
}, default: 'Cancel',
primaryButtonLabel: { },
type: String, primaryButtonLabel: {
required: false, type: String,
default: '', required: false,
}, default: '',
submitDisabled: { },
type: Boolean, submitDisabled: {
required: false, type: Boolean,
default: false, required: false,
default: false,
},
}, },
},
computed: { computed: {
btnKindClass() { btnKindClass() {
return { return {
[`btn-${this.kind}`]: true, [`btn-${this.kind}`]: true,
}; };
},
btnCancelKindClass() {
return {
[`btn-${this.closeKind}`]: true,
};
},
}, },
btnCancelKindClass() {
return {
[`btn-${this.closeKind}`]: true,
};
},
},
methods: { methods: {
close() { close() {
this.$emit('toggle', false); this.$emit('toggle', false);
}, },
emitSubmit(status) { emitSubmit(status) {
this.$emit('submit', status); this.$emit('submit', status);
},
}, },
}, };
};
</script> </script>
<template> <template>
<div class="modal-open"> <div class="modal-open">
<div
class="modal show"
role="dialog"
tabindex="-1"
>
<div <div
:class="modalDialogClass" class="modal show"
class="modal-dialog" role="dialog"
role="document" tabindex="-1"
> >
<div class="modal-content"> <div
<div class="modal-header"> :class="modalDialogClass"
<slot name="header"> class="modal-dialog"
<h4 class="modal-title pull-left"> role="document"
{{this.title}} >
</h4> <div class="modal-content">
<div class="modal-header">
<slot name="header">
<h4 class="modal-title pull-left">
{{ this.title }}
</h4>
<button
type="button"
class="close pull-right"
@click="close"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
</slot>
</div>
<div class="modal-body">
<slot name="body">
</slot>
</div>
<div
class="modal-footer"
v-if="!hideFooter"
>
<button <button
type="button" type="button"
class="close pull-right" class="btn pull-left"
@click="close" :class="btnCancelKindClass"
aria-label="Close" @click="close">
> {{ closeButtonLabel }}
<span aria-hidden="true">&times;</span>
</button> </button>
</slot> <button
</div> v-if="primaryButtonLabel"
<div class="modal-body"> type="button"
<slot name="body" :text="text"> class="btn pull-right js-primary-button"
<p>{{this.text}}</p> :disabled="submitDisabled"
</slot> :class="btnKindClass"
</div> @click="emitSubmit(true)">
<div class="modal-footer" v-if="!hideFooter"> {{ primaryButtonLabel }}
<button </button>
type="button" </div>
class="btn pull-left"
:class="btnCancelKindClass"
@click="close">
{{ closeButtonLabel }}
</button>
<button
v-if="primaryButtonLabel"
type="button"
class="btn pull-right js-primary-button"
:disabled="submitDisabled"
:class="btnKindClass"
@click="emitSubmit(true)">
{{ primaryButtonLabel }}
</button>
</div> </div>
</div> </div>
</div> </div>
<div class="modal-backdrop fade in"></div>
</div> </div>
<div class="modal-backdrop fade in" />
</div>
</template> </template>
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
this.$emit('onChangeTab', tab.scope); this.$emit('onChangeTab', tab.scope);
}, },
}, },
}; };
</script> </script>
<template> <template>
<ul class="nav-links scrolling-tabs"> <ul class="nav-links scrolling-tabs">
...@@ -55,21 +55,20 @@ ...@@ -55,21 +55,20 @@
:class="{ :class="{
active: tab.isActive, active: tab.isActive,
}" }"
> >
<a <a
role="button" role="button"
@click="onTabClick(tab)" @click="onTabClick(tab)"
:class="`js-${scope}-tab-${tab.scope}`" :class="`js-${scope}-tab-${tab.scope}`"
> >
{{ tab.name }} {{ tab.name }}
<span <span
v-if="shouldRenderBadge(tab.count)" v-if="shouldRenderBadge(tab.count)"
class="badge" class="badge"
> >
{{tab.count}} {{ tab.count }}
</span> </span>
</a> </a>
</li> </li>
</ul> </ul>
......
...@@ -20,16 +20,16 @@ ...@@ -20,16 +20,16 @@
import userAvatarLink from '../user_avatar/user_avatar_link.vue'; import userAvatarLink from '../user_avatar/user_avatar_link.vue';
export default { export default {
name: 'placeholderNote', name: 'PlaceholderNote',
components: {
userAvatarLink,
},
props: { props: {
note: { note: {
type: Object, type: Object,
required: true, required: true,
}, },
}, },
components: {
userAvatarLink,
},
computed: { computed: {
...mapGetters([ ...mapGetters([
'getUserData', 'getUserData',
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
:link-href="getUserData.path" :link-href="getUserData.path"
:img-src="getUserData.avatar_url" :img-src="getUserData.avatar_url"
:img-size="40" :img-size="40"
/> />
</div> </div>
<div <div
:class="{ discussion: !note.individual_note }" :class="{ discussion: !note.individual_note }"
...@@ -54,14 +54,14 @@ ...@@ -54,14 +54,14 @@
<div class="note-header"> <div class="note-header">
<div class="note-header-info"> <div class="note-header-info">
<a :href="getUserData.path"> <a :href="getUserData.path">
<span class="hidden-xs">{{getUserData.name}}</span> <span class="hidden-xs">{{ getUserData.name }}</span>
<span class="note-headline-light">@{{getUserData.username}}</span> <span class="note-headline-light">@{{ getUserData.username }}</span>
</a> </a>
</div> </div>
</div> </div>
<div class="note-body"> <div class="note-body">
<div class="note-text"> <div class="note-text">
<p>{{note.body}}</p> <p>{{ note.body }}</p>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* /> * />
*/ */
export default { export default {
name: 'placeholderSystemNote', name: 'PlaceholderSystemNote',
props: { props: {
note: { note: {
type: Object, type: Object,
...@@ -20,10 +20,10 @@ ...@@ -20,10 +20,10 @@
<template> <template>
<li class="note system-note timeline-entry being-posted fade-in-half"> <li class="note system-note timeline-entry being-posted fade-in-half">
<div class="timeline-entry-inner"> <div class="timeline-entry-inner">
<div class="timeline-content"> <div class="timeline-content">
<em>{{note.body}}</em> <em>{{ note.body }}</em>
</div> </div>
</div> </div>
</li> </li>
</template> </template>
...@@ -21,16 +21,16 @@ ...@@ -21,16 +21,16 @@
import { spriteIcon } from '../../../lib/utils/common_utils'; import { spriteIcon } from '../../../lib/utils/common_utils';
export default { export default {
name: 'systemNote', name: 'SystemNote',
components: {
noteHeader,
},
props: { props: {
note: { note: {
type: Object, type: Object,
required: true, required: true,
}, },
}, },
components: {
noteHeader,
},
computed: { computed: {
...mapGetters([ ...mapGetters([
'targetNoteHash', 'targetNoteHash',
......
<script> <script>
export default { export default {
props: { props: {
startSize: { startSize: {
type: Number, type: Number,
required: true, 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,
},
}, },
side: { data() {
type: String, return {
required: true, size: this.startSize,
};
}, },
minSize: { computed: {
type: Number, className() {
required: false, return `drag${this.side}`;
default: 0, },
cursorStyle() {
if (this.enabled) {
return { cursor: 'ew-resize' };
}
return {};
},
}, },
maxSize: { methods: {
type: Number, resetSize(e) {
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.size = this.startSize;
this.$emit('update:size', this.size);
},
startDrag(e) {
if (this.enabled) {
e.preventDefault(); e.preventDefault();
this.startPos = e.clientX; this.size = this.startSize;
this.currentStartSize = this.size; this.$emit('update:size', this.size);
document.addEventListener('mousemove', this.drag); },
document.addEventListener('mouseup', this.endDrag, { once: true }); startDrag(e) {
this.$emit('resize-start', this.size); if (this.enabled) {
} e.preventDefault();
}, this.startPos = e.clientX;
drag(e) { this.currentStartSize = this.size;
e.preventDefault(); document.addEventListener('mousemove', this.drag);
let moved = e.clientX - this.startPos; document.addEventListener('mouseup', this.endDrag, { once: true });
if (this.side === 'left') moved = -moved; this.$emit('resize-start', this.size);
let newSize = this.currentStartSize + moved; }
if (newSize < this.minSize) { },
newSize = this.minSize; drag(e) {
} else if (newSize > this.maxSize) { e.preventDefault();
newSize = this.maxSize; let moved = e.clientX - this.startPos;
} if (this.side === 'left') moved = -moved;
this.size = newSize; 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); this.$emit('update:size', newSize);
}, },
endDrag(e) { endDrag(e) {
e.preventDefault(); e.preventDefault();
document.removeEventListener('mousemove', this.drag); document.removeEventListener('mousemove', this.drag);
this.$emit('resize-end', this.size); this.$emit('resize-end', this.size);
},
}, },
}, };
};
</script> </script>
<template> <template>
<div <div
class="dragHandle" class="dragHandle"
:class="className" :class="className"
:style="cursorStyle" :style="cursorStyle"
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
import { parsePikadayDate, pikadayToString } from '../../lib/utils/datefix'; import { parsePikadayDate, pikadayToString } from '../../lib/utils/datefix';
export default { export default {
name: 'datePicker', name: 'DatePicker',
props: { props: {
label: { label: {
type: String, type: String,
...@@ -23,14 +23,6 @@ ...@@ -23,14 +23,6 @@
required: false, required: false,
}, },
}, },
methods: {
selected(dateText) {
this.$emit('newDateSelected', this.calendar.toString(dateText));
},
toggled() {
this.$emit('hidePicker');
},
},
mounted() { mounted() {
this.calendar = new Pikaday({ this.calendar = new Pikaday({
field: this.$el.querySelector('.dropdown-menu-toggle'), field: this.$el.querySelector('.dropdown-menu-toggle'),
...@@ -53,6 +45,14 @@ ...@@ -53,6 +45,14 @@
beforeDestroy() { beforeDestroy() {
this.calendar.destroy(); this.calendar.destroy();
}, },
methods: {
selected(dateText) {
this.$emit('newDateSelected', this.calendar.toString(dateText));
},
toggled() {
this.$emit('hidePicker');
},
},
}; };
</script> </script>
...@@ -66,7 +66,7 @@ ...@@ -66,7 +66,7 @@
@click="toggled" @click="toggled"
> >
<span class="dropdown-toggle-text"> <span class="dropdown-toggle-text">
{{label}} {{ label }}
</span> </span>
<i <i
class="fa fa-chevron-down" class="fa fa-chevron-down"
......
<script> <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 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. tooltip can be configured by props passed to this component.
Sample configuration: Sample configuration:
<project-avatar-image <project-avatar-image
:lazy="true" :lazy="true"
:img-src="projectAvatarSrc" :img-src="projectAvatarSrc"
:img-alt="tooltipText" :img-alt="tooltipText"
:tooltip-text="tooltipText" :tooltip-text="tooltipText"
tooltip-placement="top" tooltip-placement="top"
/> />
*/ */
import defaultAvatarUrl from 'images/no_avatar.png'; import defaultAvatarUrl from 'images/no_avatar.png';
import { placeholderImage } from '../../../lazy_loader'; import { placeholderImage } from '../../../lazy_loader';
import tooltip from '../../directives/tooltip'; import tooltip from '../../directives/tooltip';
export default { export default {
name: 'ProjectAvatarImage', name: 'ProjectAvatarImage',
props: { directives: {
lazy: { tooltip,
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',
},
},
directives: {
tooltip,
},
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() { props: {
return this.tooltipText ? 'body' : null; 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',
},
}, },
avatarSizeClass() { computed: {
return `s${this.size}`; // 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}`;
},
}, },
}, };
};
</script> </script>
<template> <template>
...@@ -87,7 +87,7 @@ export default { ...@@ -87,7 +87,7 @@ export default {
v-tooltip v-tooltip
class="avatar" class="avatar"
:class="{ :class="{
lazy, lazy: lazy,
[avatarSizeClass]: true, [avatarSizeClass]: true,
[cssClasses]: true [cssClasses]: true
}" }"
......
...@@ -2,8 +2,10 @@ ...@@ -2,8 +2,10 @@
import modal from './modal.vue'; import modal from './modal.vue';
export default { export default {
name: 'recaptcha-modal', name: 'RecaptchaModal',
components: {
modal,
},
props: { props: {
html: { html: {
type: String, type: String,
...@@ -18,11 +20,14 @@ export default { ...@@ -18,11 +20,14 @@ export default {
scriptSrc: 'https://www.google.com/recaptcha/api.js', scriptSrc: 'https://www.google.com/recaptcha/api.js',
}; };
}, },
watch: {
components: { html() {
modal, this.appendRecaptchaScript();
},
},
mounted() {
window.recaptchaDialogCallback = this.submit.bind(this);
}, },
methods: { methods: {
appendRecaptchaScript() { appendRecaptchaScript() {
this.removeRecaptchaScript(); this.removeRecaptchaScript();
...@@ -51,35 +56,26 @@ export default { ...@@ -51,35 +56,26 @@ export default {
this.$el.querySelector('form').submit(); this.$el.querySelector('form').submit();
}, },
}, },
watch: {
html() {
this.appendRecaptchaScript();
},
},
mounted() {
window.recaptchaDialogCallback = this.submit.bind(this);
},
}; };
</script> </script>
<template> <template>
<modal <modal
kind="warning" kind="warning"
class="recaptcha-modal js-recaptcha-modal" class="recaptcha-modal js-recaptcha-modal"
:hide-footer="true" :hide-footer="true"
:title="__('Please solve the reCAPTCHA')" :title="__('Please solve the reCAPTCHA')"
@toggle="close" @toggle="close"
> >
<div slot="body"> <div slot="body">
<p> <p>
{{__('We want to be sure it is you, please confirm you are not a robot.')}} {{ __('We want to be sure it is you, please confirm you are not a robot.') }}
</p> </p>
<div <div
ref="recaptcha" ref="recaptcha"
v-html="html" v-html="html"
></div> >
</div> </div>
</modal> </div>
</modal>
</template> </template>
<script> <script>
export default { export default {
name: 'collapsedCalendarIcon', name: 'CollapsedCalendarIcon',
props: { props: {
containerClass: { containerClass: {
type: String, type: String,
......
...@@ -4,7 +4,11 @@ ...@@ -4,7 +4,11 @@
import collapsedCalendarIcon from './collapsed_calendar_icon.vue'; import collapsedCalendarIcon from './collapsed_calendar_icon.vue';
export default { export default {
name: 'sidebarCollapsedGroupedDatePicker', name: 'SidebarCollapsedGroupedDatePicker',
components: {
toggleSidebar,
collapsedCalendarIcon,
},
props: { props: {
collapsed: { collapsed: {
type: Boolean, type: Boolean,
...@@ -30,10 +34,6 @@ ...@@ -30,10 +34,6 @@
default: false, default: false,
}, },
}, },
components: {
toggleSidebar,
collapsedCalendarIcon,
},
computed: { computed: {
hasMinAndMaxDates() { hasMinAndMaxDates() {
return this.minDate && this.maxDate; return this.minDate && this.maxDate;
......
...@@ -6,7 +6,13 @@ ...@@ -6,7 +6,13 @@
import { dateInWords } from '../../../lib/utils/datetime_utility'; import { dateInWords } from '../../../lib/utils/datetime_utility';
export default { export default {
name: 'sidebarDatePicker', name: 'SidebarDatePicker',
components: {
datePicker,
toggleSidebar,
loadingIcon,
collapsedCalendarIcon,
},
props: { props: {
collapsed: { collapsed: {
type: Boolean, type: Boolean,
...@@ -51,12 +57,6 @@ ...@@ -51,12 +57,6 @@
editing: false, editing: false,
}; };
}, },
components: {
datePicker,
toggleSidebar,
loadingIcon,
collapsedCalendarIcon,
},
computed: { computed: {
selectedAndEditable() { selectedAndEditable() {
return this.selectedDate && this.editable; return this.selectedDate && this.editable;
......
<script> <script>
export default { export default {
name: 'toggleSidebar', name: 'ToggleSidebar',
props: { props: {
collapsed: { collapsed: {
type: Boolean, type: Boolean,
...@@ -25,6 +25,6 @@ ...@@ -25,6 +25,6 @@
aria-label="toggle collapse" aria-label="toggle collapse"
class="fa" class="fa"
:class="{ 'fa-angle-double-right': !collapsed, 'fa-angle-double-left': collapsed }" :class="{ 'fa-angle-double-right': !collapsed, 'fa-angle-double-left': collapsed }"
></i> />
</button> </button>
</template> </template>
<script> <script>
import { s__ } from '../../locale'; import { s__ } from '../../locale';
const PAGINATION_UI_BUTTON_LIMIT = 4; const PAGINATION_UI_BUTTON_LIMIT = 4;
const UI_LIMIT = 6; const UI_LIMIT = 6;
const SPREAD = '...'; const SPREAD = '...';
const PREV = s__('Pagination|Prev'); const PREV = s__('Pagination|Prev');
const NEXT = s__('Pagination|Next'); const NEXT = s__('Pagination|Next');
const FIRST = s__('Pagination|« First'); const FIRST = s__('Pagination|« First');
const LAST = s__('Pagination|Last »'); const LAST = s__('Pagination|Last »');
export default { export default {
props: { props: {
/** /**
This function will take the information given by the pagination component This function will take the information given by the pagination component
*/
Here is an example `change` method: change: {
type: Function,
change(pagenum) { required: true,
gl.utils.visitUrl(`?page=${pagenum}`);
}, },
*/
change: {
type: Function,
required: true,
},
/** /**
pageInfo will come from the headers of the API call pageInfo will come from the headers of the API call
in the `.then` clause of the VueResource API call in the `.then` clause of the VueResource API call
there should be a function that contructs the pageInfo for this component there should be a function that contructs the pageInfo for this component
This is an example: This is an example:
const pageInfo = headers => ({ const pageInfo = headers => ({
perPage: +headers['X-Per-Page'], perPage: +headers['X-Per-Page'],
page: +headers['X-Page'], page: +headers['X-Page'],
total: +headers['X-Total'], total: +headers['X-Total'],
totalPages: +headers['X-Total-Pages'], totalPages: +headers['X-Total-Pages'],
nextPage: +headers['X-Next-Page'], nextPage: +headers['X-Next-Page'],
previousPage: +headers['X-Prev-Page'], previousPage: +headers['X-Prev-Page'],
}); });
*/ */
pageInfo: { pageInfo: {
type: Object, type: Object,
required: true, required: true,
}, },
},
methods: {
changePage(e) {
if (e.target.parentElement.classList.contains('disabled')) return;
const text = e.target.innerText;
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;
}
},
},
computed: {
prev() {
return this.pageInfo.previousPage;
},
next() {
return this.pageInfo.nextPage;
}, },
getItems() { computed: {
const total = this.pageInfo.totalPages; prev() {
const page = this.pageInfo.page; return this.pageInfo.previousPage;
const items = []; },
next() {
if (page > 1) { return this.pageInfo.nextPage;
items.push({ title: FIRST, first: true }); },
} getItems() {
const total = this.pageInfo.totalPages;
if (page > 1) { const page = this.pageInfo.page;
items.push({ title: PREV, prev: true }); const items = [];
} else {
items.push({ title: PREV, disabled: true, prev: true }); if (page > 1) {
} items.push({ title: FIRST, first: true });
}
if (page > UI_LIMIT) items.push({ title: SPREAD, separator: true });
if (page > 1) {
const start = Math.max(page - PAGINATION_UI_BUTTON_LIMIT, 1); items.push({ title: PREV, prev: true });
const end = Math.min(page + PAGINATION_UI_BUTTON_LIMIT, total); } else {
items.push({ title: PREV, disabled: true, prev: true });
for (let i = start; i <= end; i += 1) { }
const isActive = i === page;
items.push({ title: i, active: isActive, page: true }); if (page > UI_LIMIT) items.push({ title: SPREAD, separator: true });
}
const start = Math.max(page - PAGINATION_UI_BUTTON_LIMIT, 1);
if (total - page > PAGINATION_UI_BUTTON_LIMIT) { const end = Math.min(page + PAGINATION_UI_BUTTON_LIMIT, total);
items.push({ title: SPREAD, separator: true, page: true });
} for (let i = start; i <= end; i += 1) {
const isActive = i === page;
if (page === total) { items.push({ title: i, active: isActive, page: true });
items.push({ title: NEXT, disabled: true, next: true }); }
} else if (total - page >= 1) {
items.push({ title: NEXT, next: true }); if (total - page > PAGINATION_UI_BUTTON_LIMIT) {
} items.push({ title: SPREAD, separator: true, page: true });
}
if (total - page >= 1) {
items.push({ title: LAST, last: true }); if (page === total) {
} items.push({ title: NEXT, disabled: true, next: true });
} else if (total - page >= 1) {
return items; items.push({ title: NEXT, next: true });
}
if (total - page >= 1) {
items.push({ title: LAST, last: true });
}
return items;
},
showPagination() {
return this.pageInfo.totalPages > 1;
},
}, },
showPagination() { methods: {
return this.pageInfo.totalPages > 1; changePage(e) {
if (e.target.parentElement.classList.contains('disabled')) return;
const text = e.target.innerText;
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;
}
},
}, },
}, };
};
</script> </script>
<template> <template>
<div <div
...@@ -135,7 +129,8 @@ export default { ...@@ -135,7 +129,8 @@ export default {
> >
<ul class="pagination clearfix"> <ul class="pagination clearfix">
<li <li
v-for="item in getItems" v-for="(item, index) in getItems"
:key="index"
:class="{ :class="{
page: item.page, page: item.page,
'js-previous-button': item.prev, 'js-previous-button': item.prev,
...@@ -145,8 +140,11 @@ export default { ...@@ -145,8 +140,11 @@ export default {
separator: item.separator, separator: item.separator,
active: item.active, active: item.active,
disabled: item.disabled disabled: item.disabled
}"> }"
<a @click.prevent="changePage($event)">{{item.title}}</a> >
<a @click.prevent="changePage($event)">
{{ item.title }}
</a>
</li> </li>
</ul> </ul>
</div> </div>
......
...@@ -40,6 +40,6 @@ export default { ...@@ -40,6 +40,6 @@ export default {
:height="size" :height="size"
:width="size" :width="size"
v-html="svg" v-html="svg"
/> />
</template> </template>
...@@ -90,11 +90,11 @@ ...@@ -90,11 +90,11 @@
"devDependencies": { "devDependencies": {
"@gitlab-org/gitlab-svgs": "^1.4.0", "@gitlab-org/gitlab-svgs": "^1.4.0",
"babel-plugin-istanbul": "^4.1.5", "babel-plugin-istanbul": "^4.1.5",
"eslint": "^3.10.1", "eslint": "3.18.0",
"eslint-config-airbnb-base": "^10.0.1", "eslint-config-airbnb-base": "^10.0.1",
"eslint-import-resolver-webpack": "^0.8.3", "eslint-import-resolver-webpack": "^0.8.3",
"eslint-plugin-filenames": "^1.1.0", "eslint-plugin-filenames": "^1.1.0",
"eslint-plugin-html": "^4.0.1", "eslint-plugin-html": "2.0.1",
"eslint-plugin-import": "^2.2.0", "eslint-plugin-import": "^2.2.0",
"eslint-plugin-jasmine": "^2.1.0", "eslint-plugin-jasmine": "^2.1.0",
"eslint-plugin-promise": "^3.5.0", "eslint-plugin-promise": "^3.5.0",
......
...@@ -2372,9 +2372,9 @@ eslint-plugin-filenames@^1.1.0: ...@@ -2372,9 +2372,9 @@ eslint-plugin-filenames@^1.1.0:
lodash.kebabcase "4.0.1" lodash.kebabcase "4.0.1"
lodash.snakecase "4.0.1" lodash.snakecase "4.0.1"
eslint-plugin-html@^4.0.1: eslint-plugin-html@2.0.1:
version "4.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-html/-/eslint-plugin-html-4.0.1.tgz#fc70072263cc938496fbbc9cf648660e41fa269a" resolved "https://registry.yarnpkg.com/eslint-plugin-html/-/eslint-plugin-html-2.0.1.tgz#3a829510e82522f1e2e44d55d7661a176121fce1"
dependencies: dependencies:
htmlparser2 "^3.8.2" htmlparser2 "^3.8.2"
...@@ -2419,9 +2419,9 @@ eslint-visitor-keys@^1.0.0: ...@@ -2419,9 +2419,9 @@ eslint-visitor-keys@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d"
eslint@^3.10.1: eslint@3.18.0:
version "3.19.0" version "3.18.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.19.0.tgz#c8fc6201c7f40dd08941b87c085767386a679acc" resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.18.0.tgz#647e985c4ae71502d20ac62c109f66d5104c8a4b"
dependencies: dependencies:
babel-code-frame "^6.16.0" babel-code-frame "^6.16.0"
chalk "^1.1.3" chalk "^1.1.3"
......
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