diff options
Diffstat (limited to 'app/assets/javascripts/vue_shared/components')
35 files changed, 490 insertions, 317 deletions
diff --git a/app/assets/javascripts/vue_shared/components/changed_file_icon.vue b/app/assets/javascripts/vue_shared/components/changed_file_icon.vue index 8684005e0fb..c5fbaf87b00 100644 --- a/app/assets/javascripts/vue_shared/components/changed_file_icon.vue +++ b/app/assets/javascripts/vue_shared/components/changed_file_icon.vue @@ -1,5 +1,5 @@ <script> -import tooltip from '~/vue_shared/directives/tooltip'; +import { GlTooltipDirective } from '@gitlab/ui'; import Icon from '~/vue_shared/components/icon.vue'; import { pluralize } from '~/lib/utils/text_utility'; import { __, sprintf } from '~/locale'; @@ -10,7 +10,7 @@ export default { Icon, }, directives: { - tooltip, + GlTooltip: GlTooltipDirective, }, props: { file: { @@ -79,10 +79,8 @@ export default { <template> <span - v-tooltip + v-gl-tooltip.right :title="tooltipTitle" - data-container="body" - data-placement="right" class="file-changed-icon ml-auto" > <icon diff --git a/app/assets/javascripts/vue_shared/components/ci_badge_link.vue b/app/assets/javascripts/vue_shared/components/ci_badge_link.vue index c60052fec50..b0962684430 100644 --- a/app/assets/javascripts/vue_shared/components/ci_badge_link.vue +++ b/app/assets/javascripts/vue_shared/components/ci_badge_link.vue @@ -1,6 +1,6 @@ <script> +import { GlTooltipDirective } from '@gitlab/ui'; import CiIcon from './ci_icon.vue'; -import tooltip from '../directives/tooltip'; /** * Renders CI Badge link with CI icon and status text based on * API response shared between all places where it is used. @@ -27,7 +27,7 @@ export default { CiIcon, }, directives: { - tooltip, + GlTooltip: GlTooltipDirective, }, props: { status: { @@ -50,7 +50,7 @@ export default { </script> <template> <a - v-tooltip + v-gl-tooltip :href="status.details_path" :class="cssClass" :title="!showText ? status.text : ''" diff --git a/app/assets/javascripts/vue_shared/components/clipboard_button.vue b/app/assets/javascripts/vue_shared/components/clipboard_button.vue index 945a33d9622..671b4909839 100644 --- a/app/assets/javascripts/vue_shared/components/clipboard_button.vue +++ b/app/assets/javascripts/vue_shared/components/clipboard_button.vue @@ -12,20 +12,18 @@ * css-class="btn-transparent" * /> */ -import tooltip from '../directives/tooltip'; +import { GlButton, GlTooltipDirective } from '@gitlab/ui'; import Icon from '../components/icon.vue'; export default { name: 'ClipboardButton', - directives: { - tooltip, + GlTooltip: GlTooltipDirective, }, - components: { + GlButton, Icon, }, - props: { text: { type: String, @@ -68,16 +66,12 @@ export default { </script> <template> - <button - v-tooltip + <gl-button + v-gl-tooltip="{ placement: tooltipPlacement, container: tooltipContainer }" :class="cssClass" :title="title" :data-clipboard-text="clipboardText" - :data-container="tooltipContainer" - :data-placement="tooltipPlacement" - type="button" - class="btn" > <icon name="duplicate" /> - </button> + </gl-button> </template> diff --git a/app/assets/javascripts/vue_shared/components/commit.vue b/app/assets/javascripts/vue_shared/components/commit.vue index 13bca99dcb3..420bd25b496 100644 --- a/app/assets/javascripts/vue_shared/components/commit.vue +++ b/app/assets/javascripts/vue_shared/components/commit.vue @@ -1,11 +1,11 @@ <script> +import { GlTooltipDirective } from '@gitlab/ui'; import UserAvatarLink from './user_avatar/user_avatar_link.vue'; -import tooltip from '../directives/tooltip'; import Icon from '../../vue_shared/components/icon.vue'; export default { directives: { - tooltip, + GlTooltip: GlTooltipDirective, }, components: { UserAvatarLink, @@ -13,7 +13,7 @@ export default { }, props: { /** - * Indicates the existance of a tag. + * Indicates the existence of a tag. * Used to render the correct icon, if true will render `fa-tag` icon, * if false will render a svg sprite fork icon */ @@ -124,11 +124,10 @@ export default { </div> <a - v-tooltip + v-gl-tooltip :href="commitRef.ref_url" :title="commitRef.name" class="ref-name" - data-container="body" > {{ commitRef.name }} </a> diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue index a07d63a495d..97bdd9915c5 100644 --- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue @@ -1,11 +1,11 @@ <script> -import { Link } from '@gitlab-org/gitlab-ui'; +import { GlLink } from '@gitlab/ui'; import Icon from '../../icon.vue'; import { numberToHumanSize } from '../../../../lib/utils/number_utils'; export default { components: { - 'gl-link': Link, + GlLink, Icon, }, props: { diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue index 8163947cd0c..6f2f0f98690 100644 --- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue @@ -17,19 +17,37 @@ export default { type: Boolean, default: true, }, + innerCssClasses: { + type: [Array, Object, String], + required: false, + default: '', + }, }, data() { return { width: 0, height: 0, - isZoomable: false, - isZoomed: false, + isLoaded: false, }; }, computed: { fileSizeReadable() { return numberToHumanSize(this.fileSize); }, + dimensionStyles() { + if (!this.isLoaded) return {}; + + return { + width: `${this.width}px`, + height: `${this.height}px`, + }; + }, + hasFileSize() { + return this.fileSize > 0; + }, + hasDimensions() { + return this.width && this.height; + }, }, beforeDestroy() { window.removeEventListener('resize', this.resizeThrottled, false); @@ -48,51 +66,52 @@ export default { const { contentImg } = this.$refs; if (contentImg) { - this.isZoomable = - contentImg.naturalWidth > contentImg.width || - contentImg.naturalHeight > contentImg.height; - this.width = contentImg.naturalWidth; this.height = contentImg.naturalHeight; - this.$emit('imgLoaded', { - width: this.width, - height: this.height, - renderedWidth: contentImg.clientWidth, - renderedHeight: contentImg.clientHeight, + this.$nextTick(() => { + this.isLoaded = true; + + this.$emit('imgLoaded', { + width: this.width, + height: this.height, + renderedWidth: contentImg.clientWidth, + renderedHeight: contentImg.clientHeight, + }); }); } }, - onImgClick() { - if (this.isZoomable) this.isZoomed = !this.isZoomed; - }, }, }; </script> <template> - <div class="file-container"> - <div class="file-content image_file"> + <div> + <div + :class="innerCssClasses" + :style="dimensionStyles" + class="position-relative" + > <img ref="contentImg" - :class="{ 'is-zoomable': isZoomable, 'is-zoomed': isZoomed }" :src="path" - :alt="path" @load="onImgLoad" - @click="onImgClick"/> - <p - v-if="renderInfo" - class="file-info prepend-top-10"> - <template v-if="fileSize>0"> - {{ fileSizeReadable }} - </template> - <template v-if="fileSize>0 && width && height"> - | - </template> - <template v-if="width && height"> - W: {{ width }} | H: {{ height }} - </template> - </p> + /> + <slot name="image-overlay"></slot> </div> + <p + v-if="renderInfo" + class="image-info" + > + <template v-if="hasFileSize"> + {{ fileSizeReadable }} + </template> + <template v-if="hasFileSize && hasDimensions"> + | + </template> + <template v-if="hasDimensions"> + <strong>W</strong>: {{ width }} | <strong>H</strong>: {{ height }} + </template> + </p> </div> </template> diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue index 807e049caf6..a084cfdf612 100644 --- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue @@ -2,14 +2,14 @@ import axios from '~/lib/utils/axios_utils'; import { __ } from '~/locale'; import $ from 'jquery'; -import { SkeletonLoading } from '@gitlab-org/gitlab-ui'; +import { GlSkeletonLoading } from '@gitlab/ui'; const { CancelToken } = axios; let axiosSource; export default { components: { - SkeletonLoading, + GlSkeletonLoading, }, props: { content: { @@ -81,7 +81,7 @@ export default { <div ref="markdown-preview" class="md md-previewer"> - <skeleton-loading v-if="isLoading" /> + <gl-skeleton-loading v-if="isLoading" /> <div v-else v-html="previewContent"> diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue index cfc5343217c..9c3f3e7f7a9 100644 --- a/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue @@ -69,6 +69,13 @@ export default { :new-path="fullNewPath" :old-path="fullOldPath" :project-path="projectPath" - /> + > + <slot + slot="image-overlay" + name="image-overlay" + > + </slot> + </component> + <slot></slot> </div> </template> diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/onion_skin_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/onion_skin_viewer.vue index 38e881d17a2..cd0c1e850af 100644 --- a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/onion_skin_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/onion_skin_viewer.vue @@ -15,11 +15,6 @@ export default { type: String, required: true, }, - projectPath: { - type: String, - required: false, - default: '', - }, }, data() { return { @@ -120,7 +115,6 @@ export default { key="onionOldImg" :render-info="false" :path="oldPath" - :project-path="projectPath" @imgLoaded="onionOldImgLoaded" /> </div> @@ -136,9 +130,14 @@ export default { key="onionNewImg" :render-info="false" :path="newPath" - :project-path="projectPath" @imgLoaded="onionNewImgLoaded" - /> + > + <slot + slot="image-overlay" + name="image-overlay" + > + </slot> + </image-viewer> </div> <div class="controls"> <div class="transparent"></div> diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue index 86366c799a2..c3cfe54eb4d 100644 --- a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue @@ -16,11 +16,6 @@ export default { type: String, required: true, }, - projectPath: { - type: String, - required: false, - default: '', - }, }, data() { return { @@ -117,16 +112,14 @@ export default { 'height': swipeMaxPixelHeight, }" class="swipe-frame"> - <div class="frame deleted"> - <image-viewer - key="swipeOldImg" - ref="swipeOldImg" - :render-info="false" - :path="oldPath" - :project-path="projectPath" - @imgLoaded="swipeOldImgLoaded" - /> - </div> + <image-viewer + key="swipeOldImg" + ref="swipeOldImg" + :render-info="false" + :path="oldPath" + class="frame deleted" + @imgLoaded="swipeOldImgLoaded" + /> <div ref="swipeWrap" :style="{ @@ -134,15 +127,19 @@ export default { 'height': swipeMaxPixelHeight, }" class="swipe-wrap"> - <div class="frame added"> - <image-viewer - key="swipeNewImg" - :render-info="false" - :path="newPath" - :project-path="projectPath" - @imgLoaded="swipeNewImgLoaded" - /> - </div> + <image-viewer + key="swipeNewImg" + :render-info="false" + :path="newPath" + class="frame added" + @imgLoaded="swipeNewImgLoaded" + > + <slot + slot="image-overlay" + name="image-overlay" + > + </slot> + </image-viewer> </div> <span ref="swipeBar" diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/two_up_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/two_up_viewer.vue index 9c19266ecdf..9806d65e940 100644 --- a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/two_up_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/two_up_viewer.vue @@ -14,28 +14,29 @@ export default { type: String, required: true, }, - projectPath: { - type: String, - required: false, - default: '', - }, }, }; </script> <template> - <div class="two-up view row"> - <div class="col-sm-6 frame deleted"> - <image-viewer - :path="oldPath" - :project-path="projectPath" - /> - </div> - <div class="col-sm-6 frame added"> - <image-viewer - :path="newPath" - :project-path="projectPath" - /> - </div> + <div class="two-up view"> + <image-viewer + :path="oldPath" + :render-info="true" + inner-css-classes="frame deleted" + class="wrap" + /> + <image-viewer + :path="newPath" + :render-info="true" + :inner-css-classes="['frame', 'added']" + class="wrap" + > + <slot + slot="image-overlay" + name="image-overlay" + > + </slot> + </image-viewer> </div> </template> diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue index 1af85283277..d7f24c1afc5 100644 --- a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue @@ -8,9 +8,6 @@ import { diffModes, imageViewMode } from '../constants'; export default { components: { ImageViewer, - TwoUpViewer, - SwipeViewer, - OnionSkinViewer, }, props: { diffMode: { @@ -25,17 +22,35 @@ export default { type: String, required: true, }, - projectPath: { - type: String, - required: false, - default: '', - }, }, data() { return { mode: imageViewMode.twoup, }; }, + computed: { + imageViewComponent() { + switch (this.mode) { + case imageViewMode.twoup: + return TwoUpViewer; + case imageViewMode.swipe: + return SwipeViewer; + case imageViewMode.onion: + return OnionSkinViewer; + default: + return undefined; + } + }, + isNew() { + return this.diffMode === diffModes.new; + }, + isRenamed() { + return this.diffMode === diffModes.renamed; + }, + imagePath() { + return this.isNew || this.isRenamed ? this.newPath : this.oldPath; + }, + }, methods: { changeMode(newMode) { this.mode = newMode; @@ -52,15 +67,16 @@ export default { v-if="diffMode === $options.diffModes.replaced" class="diff-viewer"> <div class="image js-replaced-image"> - <two-up-viewer - v-if="mode === $options.imageViewMode.twoup" - v-bind="$props"/> - <swipe-viewer - v-else-if="mode === $options.imageViewMode.swipe" - v-bind="$props"/> - <onion-skin-viewer - v-else-if="mode === $options.imageViewMode.onion" - v-bind="$props"/> + <component + :is="imageViewComponent" + v-bind="$props" + > + <slot + slot="image-overlay" + name="image-overlay" + > + </slot> + </component> </div> <div class="view-modes"> <ul class="view-modes-menu"> @@ -87,23 +103,27 @@ export default { </li> </ul> </div> - <div class="note-container"></div> - </div> - <div - v-else-if="diffMode === $options.diffModes.new" - class="diff-viewer added"> - <image-viewer - :path="newPath" - :project-path="projectPath" - /> </div> <div v-else - class="diff-viewer deleted"> - <image-viewer - :path="oldPath" - :project-path="projectPath" - /> + class="diff-viewer" + > + <div class="image"> + <image-viewer + :path="imagePath" + :inner-css-classes="['frame', { + 'added': isNew, + 'deleted': diffMode === $options.diffModes.deleted + }]" + > + <slot + v-if="isNew || isRenamed" + slot="image-overlay" + name="image-overlay" + > + </slot> + </image-viewer> + </div> </div> </div> </template> diff --git a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue index 31087017968..0f4effda79f 100644 --- a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue +++ b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue @@ -1,7 +1,11 @@ <script> import { __ } from '~/locale'; +import { GlLoadingIcon } from '@gitlab/ui'; export default { + components: { + GlLoadingIcon, + }, props: { isDisabled: { type: Boolean, diff --git a/app/assets/javascripts/vue_shared/components/file_icon.vue b/app/assets/javascripts/vue_shared/components/file_icon.vue index 408f7d7965f..545be568ad3 100644 --- a/app/assets/javascripts/vue_shared/components/file_icon.vue +++ b/app/assets/javascripts/vue_shared/components/file_icon.vue @@ -1,4 +1,5 @@ <script> +import { GlLoadingIcon } from '@gitlab/ui'; import getIconForFile from './file_icon/file_icon_map'; import icon from '../../vue_shared/components/icon.vue'; @@ -17,6 +18,7 @@ import icon from '../../vue_shared/components/icon.vue'; export default { components: { icon, + GlLoadingIcon, }, props: { fileName: { diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue b/app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue index 460fa6ad72e..388a2f4ca36 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue +++ b/app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue @@ -56,12 +56,14 @@ export default { filteredResults() { if (this.filter !== '') { return this.items.filter( - item => item[this.filterKey] && item[this.filterKey].toLowerCase().includes(this.filter.toLowerCase()), + item => + item[this.filterKey] && + item[this.filterKey].toLowerCase().includes(this.filter.toLowerCase()), ); } return this.items.slice(0, this.visibleItems); - } + }, }, mounted() { /** diff --git a/app/assets/javascripts/vue_shared/components/gl_countdown.vue b/app/assets/javascripts/vue_shared/components/gl_countdown.vue new file mode 100644 index 00000000000..97f7998f461 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/gl_countdown.vue @@ -0,0 +1,53 @@ +<script> +import { calculateRemainingMilliseconds, formatTime } from '~/lib/utils/datetime_utility'; +import { GlTooltipDirective } from '@gitlab/ui'; + +/** + * Counts down to a given end date. + */ +export default { + directives: { + GlTooltip: GlTooltipDirective, + }, + props: { + endDateString: { + type: String, + required: true, + validator(value) { + return !Number.isNaN(new Date(value).getTime()); + }, + }, + }, + + data() { + return { + remainingTime: formatTime(0), + countdownUpdateIntervalId: null, + }; + }, + + mounted() { + const updateRemainingTime = () => { + const remainingMilliseconds = calculateRemainingMilliseconds(this.endDateString); + this.remainingTime = formatTime(remainingMilliseconds); + }; + + updateRemainingTime(); + this.countdownUpdateIntervalId = window.setInterval(updateRemainingTime, 1000); + }, + + beforeDestroy() { + window.clearInterval(this.countdownUpdateIntervalId); + }, +}; +</script> + +<template> + <time + v-gl-tooltip + :datetime="endDateString" + :title="endDateString" + > + {{ remainingTime }} + </time> +</template> diff --git a/app/assets/javascripts/vue_shared/components/header_ci_component.vue b/app/assets/javascripts/vue_shared/components/header_ci_component.vue index aee88cae48d..1a91a8b81e3 100644 --- a/app/assets/javascripts/vue_shared/components/header_ci_component.vue +++ b/app/assets/javascripts/vue_shared/components/header_ci_component.vue @@ -1,8 +1,9 @@ <script> +import { GlTooltipDirective, GlLink, GlButton } from '@gitlab/ui'; import CiIconBadge from './ci_badge_link.vue'; import TimeagoTooltip from './time_ago_tooltip.vue'; -import tooltip from '../directives/tooltip'; import UserAvatarImage from './user_avatar/user_avatar_image.vue'; +import LoadingButton from '~/vue_shared/components/loading_button.vue'; /** * Renders header component for job and pipeline page based on UI mockups @@ -16,9 +17,12 @@ export default { CiIconBadge, TimeagoTooltip, UserAvatarImage, + GlLink, + GlButton, + LoadingButton, }, directives: { - tooltip, + GlTooltip: GlTooltipDirective, }, props: { status: { @@ -98,8 +102,8 @@ export default { by <template v-if="user"> - <a - v-tooltip + <gl-link + v-gl-tooltip :href="user.path" :title="user.email" class="js-user-link commit-committer-link" @@ -113,7 +117,7 @@ export default { /> {{ user.name }} - </a> + </gl-link> <span v-if="user.status_tooltip_html" v-html="user.status_tooltip_html"></span> @@ -127,16 +131,16 @@ export default { <template v-for="(action, i) in actions" > - <a + <gl-link v-if="action.type === 'link'" :key="i" :href="action.path" :class="action.cssClass" > {{ action.label }} - </a> + </gl-link> - <a + <gl-link v-else-if="action.type === 'ujs-link'" :key="i" :href="action.path" @@ -145,31 +149,24 @@ export default { rel="nofollow" > {{ action.label }} - </a> + </gl-link> - <button + <loading-button v-else-if="action.type === 'button'" :key="i" + :loading="action.isLoading" :disabled="action.isLoading" :class="action.cssClass" - type="button" + container-class="d-inline" + :label="action.label" @click="onClickAction(action)" - > - {{ action.label }} - <i - v-show="action.isLoading" - class="fa fa-spin fa-spinner" - aria-hidden="true" - > - </i> - </button> + /> </template> </section> - <button + <gl-button v-if="hasSidebarButton" id="toggleSidebar" - type="button" - class="btn btn-default d-block d-sm-none + class="d-block d-sm-none sidebar-toggle-btn js-sidebar-build-toggle js-sidebar-build-toggle-header" @click="onClickSidebarButton" > @@ -179,6 +176,6 @@ sidebar-toggle-btn js-sidebar-build-toggle js-sidebar-build-toggle-header" aria-labelledby="toggleSidebar" > </i> - </button> + </gl-button> </header> </template> diff --git a/app/assets/javascripts/vue_shared/components/icon.vue b/app/assets/javascripts/vue_shared/components/icon.vue index 26f9d5ddc91..a25841fc02f 100644 --- a/app/assets/javascripts/vue_shared/components/icon.vue +++ b/app/assets/javascripts/vue_shared/components/icon.vue @@ -1,6 +1,6 @@ <script> // only allow classes in images.scss e.g. s12 -const validSizes = [8, 10, 12, 16, 18, 24, 32, 48, 72]; +const validSizes = [8, 10, 12, 14, 16, 18, 24, 32, 48, 72]; let iconValidator = () => true; /* @@ -8,7 +8,7 @@ let iconValidator = () => true; */ if (process.env.NODE_ENV !== 'production') { // eslint-disable-next-line global-require - const data = require('@gitlab-org/gitlab-svgs/dist/icons.json'); + const data = require('@gitlab/svgs/dist/icons.json'); const { icons } = data; iconValidator = value => { if (icons.includes(value)) { diff --git a/app/assets/javascripts/vue_shared/components/loading_button.vue b/app/assets/javascripts/vue_shared/components/loading_button.vue index f9b7fd5b1f9..9bae8a32a8c 100644 --- a/app/assets/javascripts/vue_shared/components/loading_button.vue +++ b/app/assets/javascripts/vue_shared/components/loading_button.vue @@ -1,4 +1,5 @@ <script> +import { GlLoadingIcon } from '@gitlab/ui'; /* 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 @@ -18,6 +19,9 @@ */ export default { + components: { + GlLoadingIcon, + }, props: { loading: { type: Boolean, diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue index 3ddb39730c4..ca9e57a9b00 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/header.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue @@ -1,17 +1,17 @@ <script> import $ from 'jquery'; -import Tooltip from '../../directives/tooltip'; +import { GlTooltipDirective } from '@gitlab/ui'; import ToolbarButton from './toolbar_button.vue'; import Icon from '../icon.vue'; export default { - directives: { - Tooltip, - }, components: { ToolbarButton, Icon, }, + directives: { + GlTooltip: GlTooltipDirective, + }, props: { previewMarkdown: { type: Boolean, @@ -147,7 +147,7 @@ export default { icon="table" /> <button - v-tooltip + v-gl-tooltip aria-label="Go full screen" class="toolbar-btn toolbar-fullscreen-btn js-zen-enter" data-container="body" diff --git a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue index feb7b8f227e..3cb48023002 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue @@ -1,9 +1,9 @@ <script> -import { Link } from '@gitlab-org/gitlab-ui'; +import { GlLink } from '@gitlab/ui'; export default { components: { - 'gl-link': Link, + GlLink, }, props: { markdownDocsPath: { diff --git a/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue b/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue index 3e89e1c1e75..13af4b627de 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue @@ -1,13 +1,13 @@ <script> -import tooltip from '../../directives/tooltip'; -import icon from '../icon.vue'; +import { GlTooltipDirective } from '@gitlab/ui'; +import Icon from '../icon.vue'; export default { components: { - icon, + Icon, }, directives: { - tooltip, + GlTooltip: GlTooltipDirective, }, props: { buttonTitle: { @@ -43,7 +43,7 @@ export default { <template> <button - v-tooltip + v-gl-tooltip :data-md-tag="tag" :data-md-select="tagSelect" :data-md-block="tagBlock" diff --git a/app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue b/app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue index 1d9c9220469..2dcd161b4fb 100644 --- a/app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue +++ b/app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue @@ -1,23 +1,23 @@ <script> -import { SkeletonLoading } from '@gitlab-org/gitlab-ui'; +import { GlSkeletonLoading } from '@gitlab/ui'; export default { name: 'SkeletonNote', components: { - SkeletonLoading, + GlSkeletonLoading, }, }; </script> <template> - <li class="timeline-entry note"> + <li class="timeline-entry note note-wrapper"> <div class="timeline-entry-inner"> <div class="timeline-icon"> </div> <div class="timeline-content"> <div class="note-header"></div> <div class="note-body"> - <skeleton-loading /> + <gl-skeleton-loading /> </div> </div> </div> diff --git a/app/assets/javascripts/vue_shared/components/notes/system_note.vue b/app/assets/javascripts/vue_shared/components/notes/system_note.vue index de3c7a80365..6a44e6a29ed 100644 --- a/app/assets/javascripts/vue_shared/components/notes/system_note.vue +++ b/app/assets/javascripts/vue_shared/components/notes/system_note.vue @@ -76,7 +76,7 @@ export default { <li :id="noteAnchorId" :class="{ target: isTargetNote }" - class="note system-note timeline-entry"> + class="note system-note timeline-entry note-wrapper"> <div class="timeline-entry-inner"> <div class="timeline-icon" diff --git a/app/assets/javascripts/vue_shared/components/pagination_links.vue b/app/assets/javascripts/vue_shared/components/pagination_links.vue index 1f2a679c145..0b44f8578cb 100644 --- a/app/assets/javascripts/vue_shared/components/pagination_links.vue +++ b/app/assets/javascripts/vue_shared/components/pagination_links.vue @@ -1,7 +1,11 @@ <script> +import { GlPagination } from '@gitlab/ui'; import { s__ } from '../../locale'; export default { + components: { + GlPagination, + }, props: { change: { type: Function, diff --git a/app/assets/javascripts/vue_shared/components/pikaday.vue b/app/assets/javascripts/vue_shared/components/pikaday.vue index 782d8e3abf6..26c99aecae4 100644 --- a/app/assets/javascripts/vue_shared/components/pikaday.vue +++ b/app/assets/javascripts/vue_shared/components/pikaday.vue @@ -1,6 +1,6 @@ <script> import Pikaday from 'pikaday'; -import { parsePikadayDate, pikadayToString } from '../../lib/utils/datefix'; +import { parsePikadayDate, pikadayToString } from '~/lib/utils/datetime_utility'; export default { name: 'DatePicker', diff --git a/app/assets/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon.vue b/app/assets/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon.vue index 7f1eb6bcec4..5841db52704 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon.vue @@ -1,34 +1,50 @@ <script> - export default { - name: 'CollapsedCalendarIcon', - props: { - containerClass: { - type: String, - required: false, - default: '', - }, - text: { - type: String, - required: false, - default: '', - }, - showIcon: { - type: Boolean, - required: false, - default: true, - }, +import tooltip from '~/vue_shared/directives/tooltip'; + +export default { + name: 'CollapsedCalendarIcon', + directives: { + tooltip, + }, + props: { + containerClass: { + type: String, + required: false, + default: '', + }, + text: { + type: String, + required: false, + default: '', + }, + showIcon: { + type: Boolean, + required: false, + default: true, + }, + tooltipText: { + type: String, + required: false, + default: '', }, - methods: { - click() { - this.$emit('click'); - }, + }, + methods: { + click() { + this.$emit('click'); }, - }; + }, +}; </script> <template> <div + v-tooltip :class="containerClass" + :title="tooltipText" + data-container="body" + data-placement="left" + data-html="true" + data-boundary="viewport" @click="click" > <i diff --git a/app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue b/app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue index dac438a702d..174c29809ac 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue @@ -1,88 +1,87 @@ <script> - import { dateInWords } from '../../../lib/utils/datetime_utility'; - import toggleSidebar from './toggle_sidebar.vue'; - import collapsedCalendarIcon from './collapsed_calendar_icon.vue'; +import { __ } from '~/locale'; +import timeagoMixin from '~/vue_shared/mixins/timeago'; +import { dateInWords, timeFor } from '~/lib/utils/datetime_utility'; +import collapsedCalendarIcon from './collapsed_calendar_icon.vue'; - export default { - name: 'SidebarCollapsedGroupedDatePicker', - components: { - toggleSidebar, - collapsedCalendarIcon, +export default { + name: 'SidebarCollapsedGroupedDatePicker', + components: { + collapsedCalendarIcon, + }, + mixins: [timeagoMixin], + props: { + collapsed: { + type: Boolean, + required: false, + default: true, }, - props: { - collapsed: { - type: Boolean, - required: false, - default: true, - }, - showToggleSidebar: { - type: Boolean, - required: false, - default: false, - }, - minDate: { - type: Date, - required: false, - default: null, - }, - maxDate: { - type: Date, - required: false, - default: null, - }, - disableClickableIcons: { - type: Boolean, - required: false, - default: false, - }, + minDate: { + type: Date, + required: false, + default: null, }, - computed: { - hasMinAndMaxDates() { - return this.minDate && this.maxDate; - }, - hasNoMinAndMaxDates() { - return !this.minDate && !this.maxDate; - }, - showMinDateBlock() { - return this.minDate || this.hasNoMinAndMaxDates; - }, - showFromText() { - return !this.maxDate && this.minDate; - }, - iconClass() { - const disabledClass = this.disableClickableIcons ? 'disabled' : ''; - return `block sidebar-collapsed-icon calendar-icon ${disabledClass}`; - }, + maxDate: { + type: Date, + required: false, + default: null, }, - methods: { - toggleSidebar() { - this.$emit('toggleCollapse'); - }, - dateText(dateType = 'min') { - const date = this[`${dateType}Date`]; - const dateWords = dateInWords(date, true); - const parsedDateWords = dateWords ? dateWords.replace(',', '') : dateWords; + disableClickableIcons: { + type: Boolean, + required: false, + default: false, + }, + }, + computed: { + hasMinAndMaxDates() { + return this.minDate && this.maxDate; + }, + hasNoMinAndMaxDates() { + return !this.minDate && !this.maxDate; + }, + showMinDateBlock() { + return this.minDate || this.hasNoMinAndMaxDates; + }, + showFromText() { + return !this.maxDate && this.minDate; + }, + iconClass() { + const disabledClass = this.disableClickableIcons ? 'disabled' : ''; + return `sidebar-collapsed-icon calendar-icon ${disabledClass}`; + }, + }, + methods: { + toggleSidebar() { + this.$emit('toggleCollapse'); + }, + dateText(dateType = 'min') { + const date = this[`${dateType}Date`]; + const dateWords = dateInWords(date, true); + const parsedDateWords = dateWords ? dateWords.replace(',', '') : dateWords; + + return date ? parsedDateWords : __('None'); + }, + tooltipText(dateType = 'min') { + const defaultText = dateType === 'min' ? __('Start date') : __('Due date'); + const date = this[`${dateType}Date`]; + const timeAgo = dateType === 'min' ? this.timeFormated(date) : timeFor(date); + const dateText = date ? [this.dateText(dateType), `(${timeAgo})`].join(' ') : ''; - return date ? parsedDateWords : 'None'; - }, + if (date) { + return [defaultText, dateText].join('<br />'); + } + return __('Start and due date'); }, - }; + }, +}; </script> <template> <div class="block sidebar-grouped-item"> - <div - v-if="showToggleSidebar" - class="issuable-sidebar-header" - > - <toggle-sidebar - :collapsed="collapsed" - @toggle="toggleSidebar" - /> - </div> <collapsed-calendar-icon v-if="showMinDateBlock" :container-class="iconClass" + :tooltip-text="tooltipText('min')" @click="toggleSidebar" > <span class="sidebar-collapsed-value"> @@ -99,7 +98,7 @@ <collapsed-calendar-icon v-if="maxDate" :container-class="iconClass" - :show-icon="!minDate" + :tooltip-text="tooltipText('max')" @click="toggleSidebar" > <span class="sidebar-collapsed-value"> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue b/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue index 500586302cf..6aa880603b9 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue @@ -1,4 +1,5 @@ <script> +import { GlLoadingIcon } from '@gitlab/ui'; import datePicker from '../pikaday.vue'; import toggleSidebar from './toggle_sidebar.vue'; import collapsedCalendarIcon from './collapsed_calendar_icon.vue'; @@ -10,6 +11,7 @@ export default { datePicker, toggleSidebar, collapsedCalendarIcon, + GlLoadingIcon, }, props: { blockClass: { diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue index 3df286de129..98b8b6460fe 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue @@ -4,6 +4,7 @@ import { __ } from '~/locale'; import LabelsSelect from '~/labels_select'; import DropdownHiddenInput from '~/vue_shared/components/dropdown/dropdown_hidden_input.vue'; +import { GlLoadingIcon } from '@gitlab/ui'; import DropdownTitle from './dropdown_title.vue'; import DropdownValue from './dropdown_value.vue'; import DropdownValueCollapsed from './dropdown_value_collapsed.vue'; @@ -24,6 +25,7 @@ export default { DropdownSearchInput, DropdownFooter, DropdownCreateLabel, + GlLoadingIcon, }, props: { showCreate: { diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed.vue index af297f3c408..0d5fc07e6e3 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed.vue @@ -14,7 +14,10 @@ export default { }, computed: { labelsList() { - const labelsString = this.labels.slice(0, 5).map(label => label.title).join(', '); + const labelsString = this.labels + .slice(0, 5) + .map(label => label.title) + .join(', '); if (this.labels.length > 5) { return sprintf(s__('LabelSelect|%{labelsString}, and %{remainingLabelCount} more'), { diff --git a/app/assets/javascripts/vue_shared/components/smart_virtual_list.vue b/app/assets/javascripts/vue_shared/components/smart_virtual_list.vue new file mode 100644 index 00000000000..63034a45f77 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/smart_virtual_list.vue @@ -0,0 +1,42 @@ +<script> +import VirtualList from 'vue-virtual-scroll-list'; + +export default { + name: 'SmartVirtualList', + components: { VirtualList }, + props: { + size: { type: Number, required: true }, + length: { type: Number, required: true }, + remain: { type: Number, required: true }, + rtag: { type: String, default: 'div' }, + wtag: { type: String, default: 'div' }, + wclass: { type: String, default: null }, + }, +}; +</script> +<template> + <virtual-list + v-if="length > remain" + v-bind="$attrs" + :size="remain" + :remain="remain" + :rtag="rtag" + :wtag="wtag" + :wclass="wclass" + class="js-virtual-list" + > + <slot></slot> + </virtual-list> + <component + :is="rtag" + v-else + class="js-plain-element" + > + <component + :is="wtag" + :class="wclass" + > + <slot></slot> + </component> + </component> +</template> diff --git a/app/assets/javascripts/vue_shared/components/toggle_button.vue b/app/assets/javascripts/vue_shared/components/toggle_button.vue index 4e9289cbed8..5d1c92c3b3d 100644 --- a/app/assets/javascripts/vue_shared/components/toggle_button.vue +++ b/app/assets/javascripts/vue_shared/components/toggle_button.vue @@ -1,4 +1,5 @@ <script> +import { GlLoadingIcon } from '@gitlab/ui'; import { s__ } from '../../locale'; import icon from './icon.vue'; @@ -10,6 +11,7 @@ const LABEL_OFF = s__('ToggleButton|Toggle Status: OFF'); export default { components: { icon, + GlLoadingIcon, }, model: { diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue index 7737b9f2697..c78d98ccd9e 100644 --- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue +++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue @@ -15,14 +15,14 @@ */ +import { GlTooltip } from '@gitlab/ui'; import defaultAvatarUrl from 'images/no_avatar.png'; import { placeholderImage } from '../../../lazy_loader'; -import tooltip from '../../directives/tooltip'; export default { name: 'UserAvatarImage', - directives: { - tooltip, + components: { + GlTooltip, }, props: { lazy: { @@ -73,9 +73,6 @@ export default { resultantSrcAttribute() { return this.lazy ? placeholderImage : this.sanitizedSource; }, - tooltipContainer() { - return this.tooltipText ? 'body' : null; - }, avatarSizeClass() { return `s${this.size}`; }, @@ -84,22 +81,30 @@ export default { </script> <template> - <img - v-tooltip - :class="{ - lazy: lazy, - [avatarSizeClass]: true, - [cssClasses]: true - }" - :src="resultantSrcAttribute" - :width="size" - :height="size" - :alt="imgAlt" - :data-src="sanitizedSource" - :data-container="tooltipContainer" - :data-placement="tooltipPlacement" - :title="tooltipText" - class="avatar" - data-boundary="window" - /> + <span> + <img + ref="userAvatarImage" + :class="{ + lazy: lazy, + [avatarSizeClass]: true, + [cssClasses]: true + }" + :src="resultantSrcAttribute" + :width="size" + :height="size" + :alt="imgAlt" + :data-src="sanitizedSource" + class="avatar" + /> + <gl-tooltip + :target="() => $refs.userAvatarImage" + :placement="tooltipPlacement" + boundary="window" + class="js-user-avatar-image-toolip" + > + <slot> + {{ tooltipText }} + </slot> + </gl-tooltip> + </span> </template> diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue index 14cb44b8619..6dd519ea56d 100644 --- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue +++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue @@ -17,18 +17,17 @@ */ -import { Link } from '@gitlab-org/gitlab-ui'; +import { GlLink, GlTooltipDirective } from '@gitlab/ui'; import userAvatarImage from './user_avatar_image.vue'; -import tooltip from '../../directives/tooltip'; export default { name: 'UserAvatarLink', components: { - 'gl-link': Link, + GlLink, userAvatarImage, }, directives: { - tooltip, + GlTooltip: GlTooltipDirective, }, props: { linkHref: { @@ -94,11 +93,14 @@ export default { :size="imgSize" :tooltip-text="avatarTooltipText" :tooltip-placement="tooltipPlacement" - /><span + > + <slot></slot> + </user-avatar-image><span v-if="shouldShowUsername" - v-tooltip + v-gl-tooltip :title="tooltipText" :tooltip-placement="tooltipPlacement" - >{{ username }}</span> + class="js-user-avatar-link-username" + >{{ username }}</span><slot name="avatar-badge"></slot> </gl-link> </template> |