diff options
Diffstat (limited to 'app/assets')
4 files changed, 289 insertions, 21 deletions
diff --git a/app/assets/javascripts/pipelines/mixins/pipelines.js b/app/assets/javascripts/pipelines/mixins/pipelines.js index 74ca3071364..3cc9d0a3a4e 100644 --- a/app/assets/javascripts/pipelines/mixins/pipelines.js +++ b/app/assets/javascripts/pipelines/mixins/pipelines.js @@ -27,11 +27,7 @@ export default { }, computed: { shouldRenderPagination() { - return ( - !this.isLoading && - this.state.pipelines.length && - this.state.pageInfo.total > this.state.pageInfo.perPage - ); + return !this.isLoading; }, }, beforeMount() { diff --git a/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue b/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue new file mode 100644 index 00000000000..27cfa8abb24 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue @@ -0,0 +1,116 @@ +<script> +import { GlTooltipDirective } from '@gitlab/ui'; +import { __, sprintf } from '~/locale'; +import IssueMilestone from '~/vue_shared/components/issue/issue_milestone.vue'; +import IssueAssignees from '~/vue_shared/components/issue/issue_assignees.vue'; +import relatedIssuableMixin from '~/vue_shared/mixins/related_issuable_mixin'; + +export default { + name: 'IssueItem', + components: { + IssueMilestone, + IssueAssignees, + }, + directives: { + GlTooltip: GlTooltipDirective, + }, + mixins: [relatedIssuableMixin], + props: { + canReorder: { + type: Boolean, + required: false, + default: false, + }, + }, + computed: { + stateTitle() { + return sprintf( + '<span class="bold">%{state}</span> %{timeInWords}<br/><span class="text-tertiary">%{timestamp}</span>', + { + state: this.isOpen ? __('Opened') : __('Closed'), + timeInWords: this.isOpen ? this.createdAtInWords : this.closedAtInWords, + timestamp: this.isOpen ? this.createdAtTimestamp : this.closedAtTimestamp, + }, + ); + }, + }, +}; +</script> + +<template> + <div + :class="{ + 'issuable-info-container': !canReorder, + 'card-body': canReorder, + }" + class="item-body" + > + <div class="item-contents"> + <div class="item-title d-flex align-items-center"> + <icon + v-if="hasState" + v-tooltip + :css-classes="iconClass" + :name="iconName" + :size="16" + :title="stateTitle" + :aria-label="state" + data-html="true" + /> + <icon + v-if="confidential" + v-gl-tooltip + name="eye-slash" + :size="16" + :title="__('Confidential')" + class="confidential-icon append-right-4" + :aria-label="__('Confidential')" + /> + <a :href="computedPath" class="sortable-link">{{ title }}</a> + </div> + <div class="item-meta"> + <div class="d-flex align-items-center item-path-id"> + <icon + v-if="hasState" + v-tooltip + :css-classes="iconClass" + :name="iconName" + :size="16" + :title="stateTitle" + :aria-label="state" + data-html="true" + /> + <span v-tooltip :title="itemPath" class="path-id-text">{{ itemPath }}</span> + {{ pathIdSeparator }}{{ itemId }} + </div> + <div class="item-meta-child d-flex align-items-center"> + <issue-milestone + v-if="hasMilestone" + :milestone="milestone" + class="d-flex align-items-center item-milestone" + /> + <slot name="dueDate"></slot> + <slot name="weight"></slot> + </div> + <issue-assignees + v-if="assignees.length" + :assignees="assignees" + class="item-assignees d-inline-flex" + /> + </div> + </div> + <button + v-if="canRemove" + ref="removeButton" + v-tooltip + :disabled="removeDisabled" + type="button" + class="btn btn-default btn-svg btn-item-remove js-issue-item-remove-button qa-remove-issue-button" + title="Remove" + aria-label="Remove" + @click="onRemoveRequest" + > + <icon :size="16" class="btn-item-remove-icon" name="close" /> + </button> + </div> +</template> diff --git a/app/assets/javascripts/vue_shared/components/table_pagination.vue b/app/assets/javascripts/vue_shared/components/table_pagination.vue index 9f38c2e4b9e..8e0b08032f7 100644 --- a/app/assets/javascripts/vue_shared/components/table_pagination.vue +++ b/app/assets/javascripts/vue_shared/components/table_pagination.vue @@ -54,15 +54,14 @@ export default { return this.pageInfo.nextPage; }, getItems() { - const total = this.pageInfo.totalPages; - const { page } = this.pageInfo; + const { totalPages, nextPage, previousPage, page } = this.pageInfo; const items = []; if (page > 1) { items.push({ title: FIRST, first: true }); } - if (page > 1) { + if (previousPage) { items.push({ title: PREV, prev: true }); } else { items.push({ title: PREV, disabled: true, prev: true }); @@ -70,32 +69,34 @@ export default { 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); + if (totalPages) { + const start = Math.max(page - PAGINATION_UI_BUTTON_LIMIT, 1); + const end = Math.min(page + PAGINATION_UI_BUTTON_LIMIT, totalPages); - for (let i = start; i <= end; i += 1) { - const isActive = i === page; - items.push({ title: i, active: isActive, page: true }); - } + 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 (totalPages - 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) { + if (nextPage) { items.push({ title: NEXT, next: true }); + } else { + items.push({ title: NEXT, disabled: true, next: true }); } - if (total - page >= 1) { + if (totalPages && totalPages - page >= 1) { items.push({ title: LAST, last: true }); } return items; }, showPagination() { - return this.pageInfo.totalPages > 1; + return this.pageInfo.nextPage || this.pageInfo.previousPage; }, }, methods: { diff --git a/app/assets/javascripts/vue_shared/mixins/related_issuable_mixin.js b/app/assets/javascripts/vue_shared/mixins/related_issuable_mixin.js new file mode 100644 index 00000000000..455ae832234 --- /dev/null +++ b/app/assets/javascripts/vue_shared/mixins/related_issuable_mixin.js @@ -0,0 +1,155 @@ +import _ from 'underscore'; +import { formatDate } from '~/lib/utils/datetime_utility'; +import tooltip from '~/vue_shared/directives/tooltip'; +import icon from '~/vue_shared/components/icon.vue'; +import timeagoMixin from '~/vue_shared/mixins/timeago'; + +const mixins = { + data() { + return { + removeDisabled: false, + }; + }, + props: { + idKey: { + type: Number, + required: true, + }, + displayReference: { + type: String, + required: true, + }, + pathIdSeparator: { + type: String, + required: true, + }, + eventNamespace: { + type: String, + required: false, + default: '', + }, + confidential: { + type: Boolean, + required: false, + default: false, + }, + title: { + type: String, + required: false, + default: '', + }, + path: { + type: String, + required: false, + default: '', + }, + state: { + type: String, + required: false, + default: '', + }, + createdAt: { + type: String, + required: false, + default: '', + }, + closedAt: { + type: String, + required: false, + default: '', + }, + milestone: { + type: Object, + required: false, + default: () => ({}), + }, + dueDate: { + type: String, + required: false, + default: '', + }, + assignees: { + type: Array, + required: false, + default: () => [], + }, + weight: { + type: Number, + required: false, + default: 0, + }, + canRemove: { + type: Boolean, + required: false, + default: false, + }, + }, + components: { + icon, + }, + directives: { + tooltip, + }, + mixins: [timeagoMixin], + computed: { + hasState() { + return this.state && this.state.length > 0; + }, + isOpen() { + return this.state === 'opened'; + }, + isClosed() { + return this.state === 'closed'; + }, + hasTitle() { + return this.title.length > 0; + }, + hasMilestone() { + return !_.isEmpty(this.milestone); + }, + iconName() { + return this.isOpen ? 'issue-open-m' : 'issue-close'; + }, + iconClass() { + return this.isOpen ? 'issue-token-state-icon-open' : 'issue-token-state-icon-closed'; + }, + computedLinkElementType() { + return this.path.length > 0 ? 'a' : 'span'; + }, + computedPath() { + return this.path.length ? this.path : null; + }, + itemPath() { + return this.displayReference.split(this.pathIdSeparator)[0]; + }, + itemId() { + return this.displayReference.split(this.pathIdSeparator).pop(); + }, + createdAtInWords() { + return this.createdAt ? this.timeFormated(this.createdAt) : ''; + }, + createdAtTimestamp() { + return this.createdAt ? formatDate(new Date(this.createdAt)) : ''; + }, + closedAtInWords() { + return this.closedAt ? this.timeFormated(this.closedAt) : ''; + }, + closedAtTimestamp() { + return this.closedAt ? formatDate(new Date(this.closedAt)) : ''; + }, + }, + methods: { + onRemoveRequest() { + let namespacePrefix = ''; + if (this.eventNamespace && this.eventNamespace.length > 0) { + namespacePrefix = `${this.eventNamespace}`; + } + + this.$emit(`${namespacePrefix}RemoveRequest`, this.idKey); + + this.removeDisabled = true; + }, + }, +}; + +export default mixins; |