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