diff options
Diffstat (limited to 'app/assets/javascripts/vue_shared/components/sidebar')
50 files changed, 0 insertions, 3466 deletions
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/copyable_field.vue b/app/assets/javascripts/vue_shared/components/sidebar/copyable_field.vue deleted file mode 100644 index 6538de085b0..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/copyable_field.vue +++ /dev/null @@ -1,86 +0,0 @@ -<script> -import { GlLoadingIcon, GlSprintf } from '@gitlab/ui'; -import { s__, __, sprintf } from '~/locale'; -import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; - -/** - * Renders an inline field, whose value can be copied to the clipboard, - * for use in the GitLab sidebar (issues, MRs, etc.). - */ -export default { - name: 'CopyableField', - components: { - ClipboardButton, - GlLoadingIcon, - GlSprintf, - }, - props: { - value: { - type: String, - required: true, - }, - name: { - type: String, - required: true, - }, - isLoading: { - type: Boolean, - required: false, - default: false, - }, - clipboardTooltipText: { - type: String, - required: false, - default: undefined, - }, - }, - computed: { - clipboardProps() { - return { - category: 'tertiary', - tooltipBoundary: 'viewport', - tooltipPlacement: 'left', - text: this.value, - title: - this.clipboardTooltipText || - sprintf(this.$options.i18n.clipboardTooltip, { name: this.name }), - }; - }, - loadingIconLabel() { - return sprintf(this.$options.i18n.loadingIconLabel, { name: this.name }); - }, - }, - i18n: { - loadingIconLabel: __('Loading %{name}'), - clipboardTooltip: __('Copy %{name}'), - templateText: s__('Sidebar|%{name}: %{value}'), - }, -}; -</script> - -<template> - <div> - <clipboard-button - v-if="!isLoading" - css-class="sidebar-collapsed-icon js-dont-change-state gl-rounded-0! gl-hover-bg-transparent" - v-bind="clipboardProps" - /> - - <div - class="gl-display-flex gl-align-items-center gl-justify-content-space-between hide-collapsed" - > - <span - class="gl-overflow-hidden gl-text-overflow-ellipsis gl-white-space-nowrap" - :title="value" - > - <gl-sprintf :message="$options.i18n.templateText"> - <template #name>{{ name }}</template> - <template #value>{{ value }}</template> - </gl-sprintf> - </span> - - <gl-loading-icon v-if="isLoading" size="sm" inline :label="loadingIconLabel" /> - <clipboard-button v-else size="small" v-bind="clipboardProps" /> - </div> - </div> -</template> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue b/app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue deleted file mode 100644 index 02323e5a0c6..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue +++ /dev/null @@ -1,217 +0,0 @@ -<script> -import { - GlIcon, - GlLoadingIcon, - GlDropdown, - GlDropdownForm, - GlDropdownItem, - GlSearchBoxByType, - GlButton, - GlTooltipDirective as GlTooltip, -} from '@gitlab/ui'; - -import axios from '~/lib/utils/axios_utils'; - -export default { - components: { - GlIcon, - GlLoadingIcon, - GlDropdown, - GlDropdownForm, - GlDropdownItem, - GlSearchBoxByType, - GlButton, - }, - directives: { - GlTooltip, - }, - props: { - projectsFetchPath: { - type: String, - required: true, - }, - dropdownButtonTitle: { - type: String, - required: true, - }, - dropdownHeaderTitle: { - type: String, - required: true, - }, - moveInProgress: { - type: Boolean, - required: false, - default: false, - }, - disabled: { - type: Boolean, - required: false, - default: false, - }, - }, - data() { - return { - projectsListLoading: false, - projectsListLoadFailed: false, - searchKey: '', - projects: [], - selectedProject: null, - projectItemClick: false, - }; - }, - computed: { - hasNoSearchResults() { - return Boolean( - !this.projectsListLoading && - !this.projectsListLoadFailed && - this.searchKey && - !this.projects.length, - ); - }, - failedToLoadResults() { - return !this.projectsListLoading && this.projectsListLoadFailed; - }, - }, - watch: { - searchKey(value = '') { - this.fetchProjects(value); - }, - }, - methods: { - fetchProjects(search = '') { - this.projectsListLoading = true; - this.projectsListLoadFailed = false; - return axios - .get(this.projectsFetchPath, { - params: { - search, - }, - }) - .then(({ data }) => { - this.projects = data; - this.$refs.searchInput.focusInput(); - }) - .catch(() => { - this.projectsListLoadFailed = true; - }) - .finally(() => { - this.projectsListLoading = false; - }); - }, - isSelectedProject(project) { - if (this.selectedProject) { - return this.selectedProject.id === project.id; - } - return false; - }, - /** - * This handler is to prevent dropdown - * from closing when an item is selected - * and emit an event only when dropdown closes. - */ - handleDropdownHide(e) { - if (this.projectItemClick) { - e.preventDefault(); - this.projectItemClick = false; - } else { - this.$emit('dropdown-close'); - } - }, - handleDropdownCloseClick() { - this.$refs.dropdown.hide(); - }, - handleProjectSelect(project) { - this.selectedProject = project.id === this.selectedProject?.id ? null : project; - this.projectItemClick = true; - }, - handleMoveClick() { - this.$refs.dropdown.hide(); - this.$emit('move-issuable', this.selectedProject); - }, - }, -}; -</script> - -<template> - <div class="js-issuable-move-block issuable-move-dropdown sidebar-move-issue-dropdown"> - <div - v-gl-tooltip.left.viewport - data-testid="move-collapsed" - :title="dropdownButtonTitle" - class="sidebar-collapsed-icon" - @click="$emit('toggle-collapse')" - > - <gl-icon name="arrow-right" /> - </div> - <gl-dropdown - ref="dropdown" - :block="true" - :disabled="moveInProgress || disabled" - class="hide-collapsed" - toggle-class="js-sidebar-dropdown-toggle" - @shown="fetchProjects" - @hide="handleDropdownHide" - > - <template #button-content - ><gl-loading-icon v-if="moveInProgress" size="sm" class="gl-mr-3" />{{ - dropdownButtonTitle - }}</template - > - <gl-dropdown-form class="gl-pt-0"> - <div - data-testid="header" - class="gl-display-flex gl-pb-3 gl-border-1 gl-border-b-solid gl-border-gray-100" - > - <span class="gl-flex-grow-1 gl-text-center gl-font-weight-bold gl-py-1">{{ - dropdownHeaderTitle - }}</span> - <gl-button - variant="link" - icon="close" - class="gl-mr-2 gl-w-auto! gl-p-2!" - :aria-label="__('Close')" - @click.prevent="handleDropdownCloseClick" - /> - </div> - <gl-search-box-by-type - ref="searchInput" - v-model.trim="searchKey" - :placeholder="__('Search project')" - :debounce="300" - /> - <div data-testid="content" class="dropdown-content"> - <gl-loading-icon v-if="projectsListLoading" size="lg" class="gl-p-5" /> - <ul v-else> - <gl-dropdown-item - v-for="project in projects" - :key="project.id" - is-check-item - :is-checked="isSelectedProject(project)" - @click.stop.prevent="handleProjectSelect(project)" - >{{ project.name_with_namespace }}</gl-dropdown-item - > - </ul> - <div v-if="hasNoSearchResults" class="gl-text-center gl-p-3"> - {{ __('No matching results') }} - </div> - <div v-if="failedToLoadResults" class="gl-text-center gl-p-3"> - {{ __('Failed to load projects') }} - </div> - </div> - <div - data-testid="footer" - class="gl-pt-3 gl-px-3 gl-border-1 gl-border-t-solid gl-border-gray-100" - > - <gl-button - category="primary" - variant="confirm" - :disabled="!Boolean(selectedProject)" - class="gl-text-center! issuable-move-button" - @click="handleMoveClick" - >{{ __('Move') }}</gl-button - > - </div> - </gl-dropdown-form> - </gl-dropdown> - </div> -</template> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/constants.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/constants.js deleted file mode 100644 index 00c54313292..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/constants.js +++ /dev/null @@ -1,5 +0,0 @@ -export const DropdownVariant = { - Sidebar: 'sidebar', - Standalone: 'standalone', - Embedded: 'embedded', -}; diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_button.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_button.vue deleted file mode 100644 index 9388ef4ba45..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_button.vue +++ /dev/null @@ -1,45 +0,0 @@ -<script> -import { GlButton, GlIcon } from '@gitlab/ui'; -import { mapActions, mapGetters } from 'vuex'; - -// @deprecated This component should only be used when there is no GraphQL API. -// In most cases you should use -// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget` instead. -export default { - components: { - GlButton, - GlIcon, - }, - computed: { - ...mapGetters([ - 'dropdownButtonText', - 'isDropdownVariantStandalone', - 'isDropdownVariantEmbedded', - ]), - }, - methods: { - ...mapActions(['toggleDropdownContents']), - handleButtonClick(e) { - if (this.isDropdownVariantStandalone || this.isDropdownVariantEmbedded) { - this.toggleDropdownContents(); - } - - if (this.isDropdownVariantStandalone) { - e.stopPropagation(); - } - }, - }, -}; -</script> - -<template> - <gl-button - class="labels-select-dropdown-button js-dropdown-button w-100 text-left" - @click="handleButtonClick" - > - <span class="dropdown-toggle-text gl-pointer-events-none flex-fill"> - {{ dropdownButtonText }} - </span> - <gl-icon name="chevron-down" class="gl-pointer-events-none float-right" /> - </gl-button> -</template> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents.vue deleted file mode 100644 index 1064cbc26e3..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents.vue +++ /dev/null @@ -1,48 +0,0 @@ -<script> -import { mapGetters, mapState } from 'vuex'; - -import DropdownContentsCreateView from './dropdown_contents_create_view.vue'; -import DropdownContentsLabelsView from './dropdown_contents_labels_view.vue'; - -// @deprecated This component should only be used when there is no GraphQL API. -// In most cases you should use -// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue` instead. -export default { - components: { - DropdownContentsLabelsView, - DropdownContentsCreateView, - }, - props: { - renderOnTop: { - type: Boolean, - required: false, - default: false, - }, - }, - computed: { - ...mapState(['showDropdownContentsCreateView']), - ...mapGetters(['isDropdownVariantSidebar']), - dropdownContentsView() { - if (this.showDropdownContentsCreateView) { - return 'dropdown-contents-create-view'; - } - return 'dropdown-contents-labels-view'; - }, - directionStyle() { - const bottom = this.isDropdownVariantSidebar ? '3rem' : '2rem'; - return this.renderOnTop ? { bottom } : {}; - }, - }, -}; -</script> - -<template> - <div - class="labels-select-dropdown-contents gl-w-full gl-my-2 gl-py-3 gl-rounded-base gl-absolute" - data-testid="labels-select-dropdown-contents" - data-qa-selector="labels_dropdown_content" - :style="directionStyle" - > - <component :is="dropdownContentsView" /> - </div> -</template> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view.vue deleted file mode 100644 index 3ff3755de46..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view.vue +++ /dev/null @@ -1,122 +0,0 @@ -<script> -import { GlTooltipDirective, GlButton, GlFormInput, GlLink, GlLoadingIcon } from '@gitlab/ui'; -import { mapState, mapActions } from 'vuex'; - -// @deprecated This component should only be used when there is no GraphQL API. -// In most cases you should use -// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue` instead. -export default { - components: { - GlButton, - GlFormInput, - GlLink, - GlLoadingIcon, - }, - directives: { - GlTooltip: GlTooltipDirective, - }, - data() { - return { - labelTitle: '', - selectedColor: '', - }; - }, - computed: { - ...mapState(['labelsCreateTitle', 'labelCreateInProgress']), - disableCreate() { - return !this.labelTitle.length || !this.selectedColor.length || this.labelCreateInProgress; - }, - suggestedColors() { - const colorsMap = gon.suggested_label_colors; - return Object.keys(colorsMap).map((color) => ({ [color]: colorsMap[color] })); - }, - }, - methods: { - ...mapActions(['toggleDropdownContents', 'toggleDropdownContentsCreateView', 'createLabel']), - getColorCode(color) { - return Object.keys(color).pop(); - }, - getColorName(color) { - return Object.values(color).pop(); - }, - handleColorClick(color) { - this.selectedColor = this.getColorCode(color); - }, - handleCreateClick() { - this.createLabel({ - title: this.labelTitle, - color: this.selectedColor, - }); - }, - }, -}; -</script> - -<template> - <div class="labels-select-contents-create js-labels-create"> - <div class="dropdown-title d-flex align-items-center pt-0 pb-2 gl-mb-0"> - <gl-button - :aria-label="__('Go back')" - category="tertiary" - size="small" - class="js-btn-back dropdown-header-button p-0" - icon="arrow-left" - @click="toggleDropdownContentsCreateView" - /> - <span class="flex-grow-1">{{ labelsCreateTitle }}</span> - <gl-button - :aria-label="__('Close')" - category="tertiary" - size="small" - class="dropdown-header-button p-0" - icon="close" - @click="toggleDropdownContents" - /> - </div> - <div class="dropdown-input"> - <gl-form-input - v-model.trim="labelTitle" - :placeholder="__('Name new label')" - :autofocus="true" - /> - </div> - <div class="dropdown-content px-2"> - <div class="suggest-colors suggest-colors-dropdown mt-0 mb-2"> - <gl-link - v-for="(color, index) in suggestedColors" - :key="index" - v-gl-tooltip:tooltipcontainer - :style="{ backgroundColor: getColorCode(color) }" - :title="getColorName(color)" - @click.prevent="handleColorClick(color)" - /> - </div> - <div class="color-input-container gl-display-flex"> - <span - class="dropdown-label-color-preview position-relative position-relative d-inline-block" - :style="{ backgroundColor: selectedColor }" - ></span> - <gl-form-input - v-model.trim="selectedColor" - class="gl-rounded-top-left-none gl-rounded-bottom-left-none gl-mb-2" - :placeholder="__('Use custom color #FF0000')" - /> - </div> - </div> - <div class="dropdown-actions clearfix pt-2 px-2"> - <gl-button - :disabled="disableCreate" - category="primary" - variant="confirm" - class="float-left d-flex align-items-center" - @click="handleCreateClick" - > - <gl-loading-icon v-show="labelCreateInProgress" size="sm" :inline="true" class="mr-1" /> - {{ __('Create') }} - </gl-button> - <gl-button class="float-right js-btn-cancel-create" @click="toggleDropdownContentsCreateView"> - {{ __('Cancel') }} - </gl-button> - </div> - </div> -</template> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue deleted file mode 100644 index e235bfde394..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue +++ /dev/null @@ -1,230 +0,0 @@ -<script> -import { - GlIntersectionObserver, - GlLoadingIcon, - GlButton, - GlSearchBoxByType, - GlLink, -} from '@gitlab/ui'; -import fuzzaldrinPlus from 'fuzzaldrin-plus'; -import { mapState, mapGetters, mapActions } from 'vuex'; - -import { UP_KEY_CODE, DOWN_KEY_CODE, ENTER_KEY_CODE, ESC_KEY_CODE } from '~/lib/utils/keycodes'; - -import LabelItem from './label_item.vue'; - -// @deprecated This component should only be used when there is no GraphQL API. -// In most cases you should use -// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue` instead. -export default { - components: { - GlIntersectionObserver, - GlLoadingIcon, - GlButton, - GlSearchBoxByType, - GlLink, - LabelItem, - }, - data() { - return { - searchKey: '', - currentHighlightItem: -1, - }; - }, - computed: { - ...mapState([ - 'allowLabelCreate', - 'allowMultiselect', - 'labelsManagePath', - 'labels', - 'labelsFetchInProgress', - 'labelsListTitle', - 'footerCreateLabelTitle', - 'footerManageLabelTitle', - ]), - ...mapGetters(['selectedLabelsList', 'isDropdownVariantSidebar', 'isDropdownVariantEmbedded']), - visibleLabels() { - if (this.searchKey) { - return fuzzaldrinPlus.filter(this.labels, this.searchKey, { - key: ['title'], - }); - } - return this.labels; - }, - showDropdownFooter() { - return ( - (this.isDropdownVariantSidebar || this.isDropdownVariantEmbedded) && - (this.allowLabelCreate || this.labelsManagePath) - ); - }, - showNoMatchingResultsMessage() { - return Boolean(this.searchKey) && this.visibleLabels.length === 0; - }, - }, - watch: { - searchKey(value) { - // When there is search string present - // and there are matching results, - // highlight first item by default. - if (value && this.visibleLabels.length) { - this.currentHighlightItem = 0; - } - }, - }, - methods: { - ...mapActions([ - 'toggleDropdownContents', - 'toggleDropdownContentsCreateView', - 'fetchLabels', - 'receiveLabelsSuccess', - 'updateSelectedLabels', - 'toggleDropdownContents', - ]), - isLabelSelected(label) { - return this.selectedLabelsList.includes(label.id); - }, - /** - * This method scrolls item from dropdown into - * the view if it is off the viewable area of the - * container. - */ - scrollIntoViewIfNeeded() { - const highlightedLabel = this.$refs.labelsListContainer.querySelector('.is-focused'); - - if (highlightedLabel) { - const container = this.$refs.labelsListContainer.getBoundingClientRect(); - const label = highlightedLabel.getBoundingClientRect(); - - if (label.bottom > container.bottom) { - this.$refs.labelsListContainer.scrollTop += label.bottom - container.bottom; - } else if (label.top < container.top) { - this.$refs.labelsListContainer.scrollTop -= container.top - label.top; - } - } - }, - handleComponentAppear() { - // We can avoid putting `catch` block here - // as failure is handled within actions.js already. - return this.fetchLabels().then(() => { - this.$refs.searchInput.focusInput(); - }); - }, - /** - * We want to remove loaded labels to ensure component - * fetches fresh set of labels every time when shown. - */ - handleComponentDisappear() { - this.receiveLabelsSuccess([]); - }, - handleCreateLabelClick() { - this.receiveLabelsSuccess([]); - this.toggleDropdownContentsCreateView(); - }, - /** - * This method enables keyboard navigation support for - * the dropdown. - */ - handleKeyDown(e) { - if (e.keyCode === UP_KEY_CODE && this.currentHighlightItem > 0) { - this.currentHighlightItem -= 1; - } else if ( - e.keyCode === DOWN_KEY_CODE && - this.currentHighlightItem < this.visibleLabels.length - 1 - ) { - this.currentHighlightItem += 1; - } else if (e.keyCode === ENTER_KEY_CODE && this.currentHighlightItem > -1) { - this.updateSelectedLabels([this.visibleLabels[this.currentHighlightItem]]); - this.searchKey = ''; - - // Prevent parent form submission upon hitting enter. - e.preventDefault(); - } else if (e.keyCode === ESC_KEY_CODE) { - this.toggleDropdownContents(); - } - - if (e.keyCode !== ESC_KEY_CODE) { - // Scroll the list only after highlighting - // styles are rendered completely. - this.$nextTick(() => { - this.scrollIntoViewIfNeeded(); - }); - } - }, - handleLabelClick(label) { - this.updateSelectedLabels([label]); - if (!this.allowMultiselect) this.toggleDropdownContents(); - }, - }, -}; -</script> - -<template> - <gl-intersection-observer @appear="handleComponentAppear" @disappear="handleComponentDisappear"> - <div class="labels-select-contents-list js-labels-list" @keydown="handleKeyDown"> - <div - v-if="isDropdownVariantSidebar || isDropdownVariantEmbedded" - class="dropdown-title gl-display-flex gl-align-items-center gl-pt-0 gl-pb-3!" - data-testid="dropdown-title" - > - <span class="flex-grow-1">{{ labelsListTitle }}</span> - <gl-button - :aria-label="__('Close')" - category="tertiary" - size="small" - class="dropdown-header-button gl-p-0!" - icon="close" - @click="toggleDropdownContents" - /> - </div> - <div class="dropdown-input" @click.stop="() => {}"> - <gl-search-box-by-type - ref="searchInput" - v-model="searchKey" - :disabled="labelsFetchInProgress" - data-qa-selector="dropdown_input_field" - /> - </div> - <div ref="labelsListContainer" class="dropdown-content" data-testid="dropdown-content"> - <gl-loading-icon - v-if="labelsFetchInProgress" - class="labels-fetch-loading gl-align-items-center w-100 h-100" - size="lg" - /> - <ul v-else class="list-unstyled gl-mb-0 gl-word-break-word"> - <label-item - v-for="(label, index) in visibleLabels" - :key="label.id" - :label="label" - :is-label-set="label.set" - :is-label-indeterminate="label.indeterminate" - :highlight="index === currentHighlightItem" - @clickLabel="handleLabelClick(label)" - /> - <li v-show="showNoMatchingResultsMessage" class="gl-p-3 gl-text-center"> - {{ __('No matching results') }} - </li> - </ul> - </div> - <div v-if="showDropdownFooter" class="dropdown-footer" data-testid="dropdown-footer"> - <ul class="list-unstyled"> - <li v-if="allowLabelCreate"> - <gl-link - class="gl-display-flex w-100 flex-row text-break-word label-item" - @click="handleCreateLabelClick" - > - {{ footerCreateLabelTitle }} - </gl-link> - </li> - <li v-if="labelsManagePath"> - <gl-link - :href="labelsManagePath" - class="gl-display-flex flex-row text-break-word label-item" - > - {{ footerManageLabelTitle }} - </gl-link> - </li> - </ul> - </div> - </div> - </gl-intersection-observer> -</template> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_title.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_title.vue deleted file mode 100644 index e4325492334..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_title.vue +++ /dev/null @@ -1,46 +0,0 @@ -<script> -import { GlButton, GlLoadingIcon } from '@gitlab/ui'; -import { mapState, mapActions } from 'vuex'; - -// @deprecated This component should only be used when there is no GraphQL API. -// In most cases you should use -// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_header.vue` instead. -export default { - components: { - GlButton, - GlLoadingIcon, - }, - props: { - labelsSelectInProgress: { - type: Boolean, - required: true, - }, - }, - computed: { - ...mapState(['allowLabelEdit', 'labelsFetchInProgress']), - }, - methods: { - ...mapActions(['toggleDropdownContents']), - }, -}; -</script> - -<template> - <div - class="hide-collapsed gl-line-height-20 gl-mb-2 gl-text-gray-900 gl-font-weight-bold gl-mb-0" - > - {{ __('Labels') }} - <template v-if="allowLabelEdit"> - <gl-loading-icon v-show="labelsSelectInProgress" size="sm" inline /> - <gl-button - category="tertiary" - size="small" - class="float-right js-sidebar-dropdown-toggle gl-mr-n2" - data-qa-selector="labels_edit_button" - @click="toggleDropdownContents" - > - {{ __('Edit') }} - </gl-button> - </template> - </div> -</template> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_value.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_value.vue deleted file mode 100644 index e59d150dd43..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_value.vue +++ /dev/null @@ -1,74 +0,0 @@ -<script> -import { GlLabel } from '@gitlab/ui'; -import { sortBy } from 'lodash'; -import { mapState } from 'vuex'; - -import { isScopedLabel } from '~/lib/utils/common_utils'; - -// @deprecated This component should only be used when there is no GraphQL API. -// In most cases you should use -// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue` instead. -export default { - components: { - GlLabel, - }, - props: { - disableLabels: { - type: Boolean, - required: false, - default: false, - }, - }, - computed: { - ...mapState([ - 'selectedLabels', - 'allowLabelRemove', - 'allowScopedLabels', - 'labelsFilterBasePath', - 'labelsFilterParam', - ]), - sortedSelectedLabels() { - return sortBy(this.selectedLabels, (label) => (isScopedLabel(label) ? 0 : 1)); - }, - }, - methods: { - labelFilterUrl(label) { - return `${this.labelsFilterBasePath}?${this.labelsFilterParam}[]=${encodeURIComponent( - label.title, - )}`; - }, - scopedLabel(label) { - return this.allowScopedLabels && isScopedLabel(label); - }, - }, -}; -</script> - -<template> - <div - :class="{ - 'has-labels': selectedLabels.length, - }" - class="hide-collapsed value issuable-show-labels js-value" - > - <span v-if="!selectedLabels.length" class="text-secondary"> - <slot></slot> - </span> - <template v-for="label in sortedSelectedLabels" v-else> - <gl-label - :key="label.id" - data-qa-selector="selected_label_content" - :data-qa-label-name="label.title" - :title="label.title" - :description="label.description" - :background-color="label.color" - :target="labelFilterUrl(label)" - :scoped="scopedLabel(label)" - :show-close-button="allowLabelRemove" - :disabled="disableLabels" - tooltip-placement="top" - @close="$emit('onLabelRemove', label.id)" - /> - </template> - </div> -</template> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_value_collapsed.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_value_collapsed.vue deleted file mode 100644 index 5966c78aa51..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_value_collapsed.vue +++ /dev/null @@ -1,53 +0,0 @@ -<script> -import { GlIcon, GlTooltipDirective } from '@gitlab/ui'; -import { s__, sprintf } from '~/locale'; - -// @deprecated This component should only be used when there is no GraphQL API. -// In most cases you should use -// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget` instead. -export default { - directives: { - GlTooltip: GlTooltipDirective, - }, - components: { - GlIcon, - }, - props: { - labels: { - type: Array, - required: true, - }, - }, - computed: { - labelsList() { - const labelsString = this.labels.length - ? this.labels - .slice(0, 5) - .map((label) => label.title) - .join(', ') - : s__('LabelSelect|Labels'); - - if (this.labels.length > 5) { - return sprintf(s__('LabelSelect|%{labelsString}, and %{remainingLabelCount} more'), { - labelsString, - remainingLabelCount: this.labels.length - 5, - }); - } - - return labelsString; - }, - }, - methods: { - handleClick() { - this.$emit('onValueClick'); - }, - }, -}; -</script> - -<template> - <div v-gl-tooltip.left.viewport="labelsList" class="sidebar-collapsed-icon" @click="handleClick"> - <gl-icon name="labels" /> - <span>{{ labels.length }}</span> - </div> -</template> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue deleted file mode 100644 index 154e3013acd..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue +++ /dev/null @@ -1,109 +0,0 @@ -<script> -import { GlLink, GlIcon } from '@gitlab/ui'; -import { __ } from '~/locale'; - -// @deprecated This component should only be used when there is no GraphQL API. -// In most cases you should use -// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/label_item.vue` instead. -export default { - functional: true, - props: { - label: { - type: Object, - required: true, - }, - isLabelSet: { - type: Boolean, - required: true, - }, - isLabelIndeterminate: { - type: Boolean, - required: false, - default: false, - }, - highlight: { - type: Boolean, - required: false, - default: false, - }, - }, - render(h, { props, listeners }) { - const { label, highlight, isLabelSet, isLabelIndeterminate } = props; - - const labelColorBox = h('span', { - class: 'dropdown-label-box gl-flex-shrink-0 gl-top-0 gl-mr-3', - style: { - backgroundColor: label.color, - }, - attrs: { - 'data-testid': 'label-color-box', - }, - }); - - const checkedIcon = h(GlIcon, { - class: { - 'gl-mr-3 gl-flex-shrink-0 has-tooltip': true, - hidden: !isLabelSet, - }, - attrs: { - title: __('Selected for all items.'), - 'data-testid': 'checked-icon', - }, - props: { - name: 'mobile-issue-close', - }, - }); - - const indeterminateIcon = h(GlIcon, { - class: { - 'gl-mr-3 gl-flex-shrink-0 has-tooltip': true, - hidden: !isLabelIndeterminate, - }, - attrs: { - title: __('Selected for some items.'), - 'data-testid': 'indeterminate-icon', - }, - props: { - name: 'dash', - }, - }); - - const noIcon = h('span', { - class: { - 'gl-mr-5 gl-pr-3': true, - hidden: isLabelSet || isLabelIndeterminate, - }, - attrs: { - 'data-testid': 'no-icon', - }, - }); - - const labelTitle = h('span', label.title); - - const labelLink = h( - GlLink, - { - class: 'gl-display-flex gl-align-items-center label-item gl-text-body', - on: { - click: () => { - listeners.clickLabel(label); - }, - }, - }, - [noIcon, checkedIcon, indeterminateIcon, labelColorBox, labelTitle], - ); - - return h( - 'li', - { - class: { - 'gl-display-block': true, - 'gl-text-left': true, - 'is-focused': highlight, - }, - }, - [labelLink], - ); - }, -}; -</script> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue deleted file mode 100644 index e6c29e24f0c..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue +++ /dev/null @@ -1,345 +0,0 @@ -<script> -import $ from 'jquery'; -import Vue from 'vue'; -import Vuex, { mapState, mapActions, mapGetters } from 'vuex'; -import { isInViewport } from '~/lib/utils/common_utils'; -import { __ } from '~/locale'; - -import { DropdownVariant } from './constants'; -import DropdownButton from './dropdown_button.vue'; -import DropdownContents from './dropdown_contents.vue'; -import DropdownTitle from './dropdown_title.vue'; -import DropdownValue from './dropdown_value.vue'; -import DropdownValueCollapsed from './dropdown_value_collapsed.vue'; -import labelsSelectModule from './store'; - -Vue.use(Vuex); - -// @deprecated This component should only be used when there is no GraphQL API. -// In most cases you should use -// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue` instead. -export default { - store: new Vuex.Store(labelsSelectModule()), - components: { - DropdownTitle, - DropdownValue, - DropdownButton, - DropdownContents, - DropdownValueCollapsed, - }, - props: { - allowLabelRemove: { - type: Boolean, - required: false, - default: false, - }, - allowLabelEdit: { - type: Boolean, - required: false, - default: false, - }, - allowLabelCreate: { - type: Boolean, - required: false, - default: false, - }, - allowMultiselect: { - type: Boolean, - required: false, - default: false, - }, - allowScopedLabels: { - type: Boolean, - required: false, - default: false, - }, - allowMultipleScopedLabels: { - type: Boolean, - required: false, - default: false, - }, - variant: { - type: String, - required: false, - default: DropdownVariant.Sidebar, - }, - selectedLabels: { - type: Array, - required: false, - default: () => [], - }, - hideCollapsedView: { - type: Boolean, - required: false, - default: false, - }, - labelsSelectInProgress: { - type: Boolean, - required: false, - default: false, - }, - labelsFetchPath: { - type: String, - required: false, - default: '', - }, - labelsManagePath: { - type: String, - required: false, - default: '', - }, - labelsFilterBasePath: { - type: String, - required: false, - default: '', - }, - labelsFilterParam: { - type: String, - required: false, - default: 'label_name', - }, - dropdownButtonText: { - type: String, - required: false, - default: __('Label'), - }, - labelsListTitle: { - type: String, - required: false, - default: __('Assign labels'), - }, - labelsCreateTitle: { - type: String, - required: false, - default: __('Create group label'), - }, - footerCreateLabelTitle: { - type: String, - required: false, - default: __('Create group label'), - }, - footerManageLabelTitle: { - type: String, - required: false, - default: __('Manage group labels'), - }, - isEditing: { - type: Boolean, - required: false, - default: false, - }, - }, - data() { - return { - contentIsOnViewport: true, - }; - }, - computed: { - ...mapState(['showDropdownButton', 'showDropdownContents']), - ...mapGetters([ - 'isDropdownVariantSidebar', - 'isDropdownVariantStandalone', - 'isDropdownVariantEmbedded', - ]), - dropdownButtonVisible() { - return this.isDropdownVariantSidebar ? this.showDropdownButton : true; - }, - }, - watch: { - selectedLabels(selectedLabels) { - this.setInitialState({ - selectedLabels, - }); - setTimeout(() => this.updateLabelsSetState(), 100); - }, - showDropdownContents(showDropdownContents) { - this.setContentIsOnViewport(showDropdownContents); - }, - isEditing(newVal) { - if (newVal) { - this.toggleDropdownContents(); - } - }, - }, - mounted() { - this.setInitialState({ - variant: this.variant, - allowLabelRemove: this.allowLabelRemove, - allowLabelEdit: this.allowLabelEdit, - allowLabelCreate: this.allowLabelCreate, - allowMultiselect: this.allowMultiselect, - allowScopedLabels: this.allowScopedLabels, - allowMultipleScopedLabels: this.allowMultipleScopedLabels, - dropdownButtonText: this.dropdownButtonText, - selectedLabels: this.selectedLabels, - labelsFetchPath: this.labelsFetchPath, - labelsManagePath: this.labelsManagePath, - labelsFilterBasePath: this.labelsFilterBasePath, - labelsFilterParam: this.labelsFilterParam, - labelsListTitle: this.labelsListTitle, - labelsCreateTitle: this.labelsCreateTitle, - footerCreateLabelTitle: this.footerCreateLabelTitle, - footerManageLabelTitle: this.footerManageLabelTitle, - }); - - this.$store.subscribeAction({ - after: this.handleVuexActionDispatch, - }); - - document.addEventListener('mousedown', this.handleDocumentMousedown); - document.addEventListener('click', this.handleDocumentClick); - - this.updateLabelsSetState(); - }, - beforeDestroy() { - document.removeEventListener('mousedown', this.handleDocumentMousedown); - document.removeEventListener('click', this.handleDocumentClick); - }, - methods: { - ...mapActions(['setInitialState', 'toggleDropdownContents', 'updateLabelsSetState']), - /** - * This method differentiates between - * dispatched actions and calls necessary method. - */ - handleVuexActionDispatch(action, state) { - if ( - action.type === 'toggleDropdownContents' && - !state.showDropdownButton && - !state.showDropdownContents - ) { - const filterTouchedLabelsFn = (label) => label.touched; - const filterSetLabelsFn = (label) => label.set; - const labels = this.isDropdownVariantEmbedded - ? state.labels.filter(filterSetLabelsFn) - : state.labels.filter(filterTouchedLabelsFn); - this.handleDropdownClose(labels, state.labels.filter(filterTouchedLabelsFn)); - } - }, - /** - * This method stores a mousedown event's target. - * Required by the click listener because the click - * event itself has no reference to this element. - */ - handleDocumentMousedown({ target }) { - this.mousedownTarget = target; - }, - /** - * This method listens for document-wide click event - * and toggle dropdown if user clicks anywhere outside - * the dropdown while dropdown is visible. - */ - handleDocumentClick({ target }) { - // We also perform the toggle exception check for the - // last mousedown event's target to avoid hiding the - // box when the mousedown happened inside the box and - // only the mouseup did not. - if ( - this.showDropdownContents && - !this.preventDropdownToggleOnClick(target) && - !this.preventDropdownToggleOnClick(this.mousedownTarget) - ) { - this.toggleDropdownContents(); - } - }, - /** - * This method checks whether a given click target - * should prevent the dropdown from being toggled. - */ - preventDropdownToggleOnClick(target) { - // This approach of element detection is needed - // as the dropdown wrapper is not using `GlDropdown` as - // it will also require us to use `BDropdownForm` - // which is yet to be implemented in GitLab UI. - const hasExceptionClass = [ - 'js-dropdown-button', - 'js-btn-cancel-create', - 'js-sidebar-dropdown-toggle', - ].some( - (className) => - target?.classList.contains(className) || - target?.parentElement?.classList.contains(className), - ); - - const hasExceptionParent = ['.js-btn-back', '.js-labels-list'].some( - (className) => $(target).parents(className).length, - ); - - const isInDropdownButtonCollapsed = this.$refs.dropdownButtonCollapsed?.$el.contains(target); - - const isInDropdownContents = this.$refs.dropdownContents?.$el.contains(target); - - return ( - hasExceptionClass || - hasExceptionParent || - isInDropdownButtonCollapsed || - isInDropdownContents - ); - }, - handleDropdownClose(labels, touchedLabels) { - // Only emit label updates if there are any - // labels to update on UI. - if (labels.length) this.$emit('updateSelectedLabels', labels); - this.$emit('onDropdownClose', touchedLabels); - }, - handleCollapsedValueClick() { - this.$emit('toggleCollapse'); - }, - setContentIsOnViewport(showDropdownContents) { - if (!showDropdownContents) { - this.contentIsOnViewport = true; - - return; - } - - this.$nextTick(() => { - if (this.$refs.dropdownContents) { - this.contentIsOnViewport = isInViewport(this.$refs.dropdownContents.$el); - } - }); - }, - }, -}; -</script> - -<template> - <div - class="labels-select-wrapper position-relative" - :class="{ - 'is-standalone': isDropdownVariantStandalone, - 'is-embedded': isDropdownVariantEmbedded, - }" - > - <template v-if="isDropdownVariantSidebar"> - <dropdown-value-collapsed - v-if="!hideCollapsedView" - ref="dropdownButtonCollapsed" - :labels="selectedLabels" - @onValueClick="handleCollapsedValueClick" - /> - <dropdown-title - :allow-label-edit="allowLabelEdit" - :labels-select-in-progress="labelsSelectInProgress" - /> - <dropdown-value - :disable-labels="labelsSelectInProgress" - @onLabelRemove="$emit('onLabelRemove', $event)" - > - <slot></slot> - </dropdown-value> - <dropdown-button v-show="dropdownButtonVisible" class="gl-mt-2" /> - <dropdown-contents - v-if="dropdownButtonVisible && showDropdownContents" - ref="dropdownContents" - :render-on-top="!contentIsOnViewport" - /> - </template> - <template v-if="isDropdownVariantStandalone || isDropdownVariantEmbedded"> - <dropdown-button v-show="dropdownButtonVisible" /> - <dropdown-contents - v-if="dropdownButtonVisible && showDropdownContents" - ref="dropdownContents" - :render-on-top="!contentIsOnViewport" - /> - </template> - </div> -</template> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/actions.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/actions.js deleted file mode 100644 index 2dab97826b9..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/actions.js +++ /dev/null @@ -1,69 +0,0 @@ -import { createAlert } from '~/flash'; -import axios from '~/lib/utils/axios_utils'; -import { __ } from '~/locale'; -import * as types from './mutation_types'; - -export const setInitialState = ({ commit }, props) => commit(types.SET_INITIAL_STATE, props); - -export const toggleDropdownButton = ({ commit }) => commit(types.TOGGLE_DROPDOWN_BUTTON); -export const toggleDropdownContents = ({ commit }) => commit(types.TOGGLE_DROPDOWN_CONTENTS); - -export const toggleDropdownContentsCreateView = ({ commit }) => - commit(types.TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW); - -export const requestLabels = ({ commit }) => commit(types.REQUEST_LABELS); -export const receiveLabelsSuccess = ({ commit }, labels) => - commit(types.RECEIVE_SET_LABELS_SUCCESS, labels); -export const receiveLabelsFailure = ({ commit }) => { - commit(types.RECEIVE_SET_LABELS_FAILURE); - createAlert({ - message: __('Error fetching labels.'), - }); -}; -export const fetchLabels = ({ state, dispatch }, options) => { - if (state.labelsFetched && (!options || !options.refetch)) { - return Promise.resolve(); - } - - dispatch('requestLabels'); - return axios - .get(state.labelsFetchPath) - .then(({ data }) => { - dispatch('receiveLabelsSuccess', data); - }) - .catch(() => dispatch('receiveLabelsFailure')); -}; - -export const requestCreateLabel = ({ commit }) => commit(types.REQUEST_CREATE_LABEL); -export const receiveCreateLabelSuccess = ({ commit }) => commit(types.RECEIVE_CREATE_LABEL_SUCCESS); -export const receiveCreateLabelFailure = ({ commit }) => { - commit(types.RECEIVE_CREATE_LABEL_FAILURE); - createAlert({ - message: __('Error creating label.'), - }); -}; -export const createLabel = ({ state, dispatch }, label) => { - dispatch('requestCreateLabel'); - axios - .post(state.labelsManagePath, { - label, - }) - .then(({ data }) => { - if (data.id) { - dispatch('fetchLabels', { refetch: true }); - dispatch('receiveCreateLabelSuccess'); - dispatch('toggleDropdownContentsCreateView'); - } else { - // eslint-disable-next-line @gitlab/require-i18n-strings - throw new Error('Error Creating Label'); - } - }) - .catch(() => { - dispatch('receiveCreateLabelFailure'); - }); -}; - -export const updateSelectedLabels = ({ commit }, labels) => - commit(types.UPDATE_SELECTED_LABELS, { labels }); - -export const updateLabelsSetState = ({ commit }) => commit(types.UPDATE_LABELS_SET_STATE); diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/getters.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/getters.js deleted file mode 100644 index ef3eedd9bb2..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/getters.js +++ /dev/null @@ -1,53 +0,0 @@ -import { __, s__, sprintf } from '~/locale'; -import { DropdownVariant } from '../constants'; - -/** - * Returns string representing current labels - * selection on dropdown button. - * - * @param {object} state - */ -export const dropdownButtonText = (state, getters) => { - const selectedLabels = - getters.isDropdownVariantSidebar || getters.isDropdownVariantEmbedded - ? state.labels.filter((label) => label.set || label.indeterminate) - : state.selectedLabels; - - if (!selectedLabels.length) { - return state.dropdownButtonText || __('Label'); - } else if (selectedLabels.length > 1) { - return sprintf(s__('LabelSelect|%{firstLabelName} +%{remainingLabelCount} more'), { - firstLabelName: selectedLabels[0].title, - remainingLabelCount: selectedLabels.length - 1, - }); - } - return selectedLabels[0].title; -}; - -/** - * Returns array containing only label IDs from - * selectedLabels array. - * @param {object} state - */ -export const selectedLabelsList = (state) => state.selectedLabels.map((label) => label.id); - -/** - * Returns boolean representing whether dropdown variant - * is `sidebar` - * @param {object} state - */ -export const isDropdownVariantSidebar = (state) => state.variant === DropdownVariant.Sidebar; - -/** - * Returns boolean representing whether dropdown variant - * is `standalone` - * @param {object} state - */ -export const isDropdownVariantStandalone = (state) => state.variant === DropdownVariant.Standalone; - -/** - * Returns boolean representing whether dropdown variant - * is `embedded` - * @param {object} state - */ -export const isDropdownVariantEmbedded = (state) => state.variant === DropdownVariant.Embedded; diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/index.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/index.js deleted file mode 100644 index 5f61cb732c8..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/index.js +++ /dev/null @@ -1,12 +0,0 @@ -import * as actions from './actions'; -import * as getters from './getters'; -import mutations from './mutations'; -import state from './state'; - -export default () => ({ - namespaced: true, - state: state(), - actions, - getters, - mutations, -}); diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutation_types.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutation_types.js deleted file mode 100644 index f26e36031f4..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutation_types.js +++ /dev/null @@ -1,22 +0,0 @@ -export const SET_INITIAL_STATE = 'SET_INITIAL_STATE'; - -export const REQUEST_LABELS = 'REQUEST_LABELS'; -export const RECEIVE_LABELS_SUCCESS = 'RECEIVE_LABELS_SUCCESS'; -export const RECEIVE_LABELS_FAILURE = 'RECEIVE_LABELS_FAILURE'; - -export const REQUEST_SET_LABELS = 'REQUEST_SET_LABELS'; -export const RECEIVE_SET_LABELS_SUCCESS = 'RECEIVE_SET_LABELS_SUCCESS'; -export const RECEIVE_SET_LABELS_FAILURE = 'RECEIVE_SET_LABELS_FAILURE'; - -export const REQUEST_CREATE_LABEL = 'REQUEST_CREATE_LABEL'; -export const RECEIVE_CREATE_LABEL_SUCCESS = 'RECEIVE_CREATE_LABEL_SUCCESS'; -export const RECEIVE_CREATE_LABEL_FAILURE = 'RECEIVE_CREATE_LABEL_FAILURE'; - -export const TOGGLE_DROPDOWN_BUTTON = 'TOGGLE_DROPDOWN_VISIBILITY'; -export const TOGGLE_DROPDOWN_CONTENTS = 'TOGGLE_DROPDOWN_CONTENTS'; - -export const UPDATE_SELECTED_LABELS = 'UPDATE_SELECTED_LABELS'; - -export const TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW = 'TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW'; - -export const UPDATE_LABELS_SET_STATE = 'UPDATE_LABELS_SET_STATE'; diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutations.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutations.js deleted file mode 100644 index c85d9befcbb..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutations.js +++ /dev/null @@ -1,113 +0,0 @@ -import { isScopedLabel, scopedLabelKey } from '~/lib/utils/common_utils'; -import { DropdownVariant } from '../constants'; -import * as types from './mutation_types'; - -const transformLabels = (labels, selectedLabels) => - labels.map((label) => { - const selectedLabel = selectedLabels.find(({ id }) => id === label.id); - - return { - ...label, - set: Boolean(selectedLabel?.set), - indeterminate: Boolean(selectedLabel?.indeterminate), - }; - }); - -export default { - [types.SET_INITIAL_STATE](state, props) { - // We need to ensure that selectedLabels have - // `set` & `indeterminate` properties defined. - if (props.selectedLabels?.length) { - props.selectedLabels.forEach((label) => { - /* eslint-disable no-param-reassign */ - if (label.set === undefined && label.indeterminate === undefined) { - label.set = true; - label.indeterminate = false; - } else if (label.set === undefined && label.indeterminate !== undefined) { - label.set = false; - } else if (label.set !== undefined && label.indeterminate === undefined) { - label.indeterminate = false; - } else { - label.set = false; - label.indeterminate = false; - } - /* eslint-enable no-param-reassign */ - }); - } - - Object.assign(state, { ...props }); - }, - - [types.TOGGLE_DROPDOWN_BUTTON](state) { - state.showDropdownButton = !state.showDropdownButton; - }, - - [types.TOGGLE_DROPDOWN_CONTENTS](state) { - if (state.variant === DropdownVariant.Sidebar) { - state.showDropdownButton = !state.showDropdownButton; - } - state.showDropdownContents = !state.showDropdownContents; - // Ensure that Create View is hidden by default - // when dropdown contents are revealed. - if (state.showDropdownContents) { - state.showDropdownContentsCreateView = false; - } - }, - - [types.TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW](state) { - state.showDropdownContentsCreateView = !state.showDropdownContentsCreateView; - }, - - [types.REQUEST_LABELS](state) { - state.labelsFetchInProgress = true; - }, - [types.RECEIVE_SET_LABELS_SUCCESS](state, labels) { - // Iterate over every label and add a `set` prop - // to determine whether it is already a part of - // selectedLabels array. - state.labelsFetchInProgress = false; - state.labelsFetched = true; - state.labels = transformLabels(labels, state.selectedLabels); - }, - [types.RECEIVE_SET_LABELS_FAILURE](state) { - state.labelsFetchInProgress = false; - }, - - [types.REQUEST_CREATE_LABEL](state) { - state.labelCreateInProgress = true; - }, - [types.RECEIVE_CREATE_LABEL_SUCCESS](state) { - state.labelCreateInProgress = false; - }, - [types.RECEIVE_CREATE_LABEL_FAILURE](state) { - state.labelCreateInProgress = false; - }, - - [types.UPDATE_SELECTED_LABELS](state, { labels }) { - // Find the label to update from all the labels - // and change `set` prop value to represent their current state. - const labelId = labels.pop()?.id; - const candidateLabel = state.labels.find((label) => labelId === label.id); - if (candidateLabel) { - candidateLabel.touched = true; - candidateLabel.set = candidateLabel.indeterminate ? true : !candidateLabel.set; - candidateLabel.indeterminate = false; - } - - if (isScopedLabel(candidateLabel) && !state.allowMultipleScopedLabels) { - const currentActiveScopedLabel = state.labels.find( - ({ set, title }) => - set && - title !== candidateLabel.title && - scopedLabelKey({ title }) === scopedLabelKey(candidateLabel), - ); - if (currentActiveScopedLabel) { - currentActiveScopedLabel.set = false; - } - } - }, - - [types.UPDATE_LABELS_SET_STATE](state) { - state.labels = transformLabels(state.labels, state.selectedLabels); - }, -}; diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/state.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/state.js deleted file mode 100644 index 0185d5f88e1..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/state.js +++ /dev/null @@ -1,30 +0,0 @@ -export default () => ({ - // Initial Data - labels: [], - labelsFetched: false, - selectedLabels: [], - labelsListTitle: '', - labelsCreateTitle: '', - footerCreateLabelTitle: '', - footerManageLabelTitle: '', - dropdownButtonText: '', - - // Paths - namespace: '', - labelsFetchPath: '', - labelsFilterBasePath: '', - - // UI Flags - variant: '', - allowLabelRemove: false, - allowLabelCreate: false, - allowLabelEdit: false, - allowScopedLabels: false, - allowMultiselect: false, - showDropdownButton: false, - showDropdownContents: false, - showDropdownContentsCreateView: false, - labelsFetchInProgress: false, - labelCreateInProgress: false, - selectedLabelsUpdated: false, -}); diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/constants.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/constants.js deleted file mode 100644 index cd671b4d8f5..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/constants.js +++ /dev/null @@ -1,13 +0,0 @@ -export const SCOPED_LABEL_DELIMITER = '::'; -export const DEBOUNCE_DROPDOWN_DELAY = 200; - -export const DropdownVariant = { - Sidebar: 'sidebar', - Standalone: 'standalone', - Embedded: 'embedded', -}; - -export const LabelType = { - group: 'group', - project: 'project', -}; diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue deleted file mode 100644 index 27186281c42..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue +++ /dev/null @@ -1,241 +0,0 @@ -<script> -import { GlButton, GlDropdown, GlDropdownItem, GlLink } from '@gitlab/ui'; -import { debounce } from 'lodash'; -import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants'; -import { __, s__, sprintf } from '~/locale'; -import DropdownContentsCreateView from './dropdown_contents_create_view.vue'; -import DropdownContentsLabelsView from './dropdown_contents_labels_view.vue'; -import DropdownFooter from './dropdown_footer.vue'; -import DropdownHeader from './dropdown_header.vue'; -import { isDropdownVariantStandalone, isDropdownVariantSidebar } from './utils'; - -export default { - components: { - DropdownContentsLabelsView, - DropdownContentsCreateView, - DropdownHeader, - DropdownFooter, - GlButton, - GlDropdown, - GlDropdownItem, - GlLink, - }, - props: { - labelsCreateTitle: { - type: String, - required: true, - }, - selectedLabels: { - type: Array, - required: true, - }, - allowMultiselect: { - type: Boolean, - required: true, - }, - labelsListTitle: { - type: String, - required: true, - }, - dropdownButtonText: { - type: String, - required: true, - }, - footerCreateLabelTitle: { - type: String, - required: true, - }, - footerManageLabelTitle: { - type: String, - required: true, - }, - variant: { - type: String, - required: true, - }, - isVisible: { - type: Boolean, - required: false, - default: false, - }, - fullPath: { - type: String, - required: true, - }, - workspaceType: { - type: String, - required: true, - }, - attrWorkspacePath: { - type: String, - required: true, - }, - labelCreateType: { - type: String, - required: true, - }, - }, - data() { - return { - showDropdownContentsCreateView: false, - localSelectedLabels: [...this.selectedLabels], - isDirty: false, - searchKey: '', - }; - }, - computed: { - dropdownContentsView() { - if (this.showDropdownContentsCreateView) { - return 'dropdown-contents-create-view'; - } - return 'dropdown-contents-labels-view'; - }, - dropdownTitle() { - return this.showDropdownContentsCreateView ? this.labelsCreateTitle : this.labelsListTitle; - }, - buttonText() { - if (!this.localSelectedLabels.length) { - return this.dropdownButtonText || __('Label'); - } else if (this.localSelectedLabels.length > 1) { - return sprintf(s__('LabelSelect|%{firstLabelName} +%{remainingLabelCount} more'), { - firstLabelName: this.localSelectedLabels[0].title, - remainingLabelCount: this.localSelectedLabels.length - 1, - }); - } - return this.localSelectedLabels[0].title; - }, - showDropdownFooter() { - return !this.showDropdownContentsCreateView && !this.isStandalone; - }, - isStandalone() { - return isDropdownVariantStandalone(this.variant); - }, - }, - watch: { - localSelectedLabels: { - handler() { - this.isDirty = true; - }, - deep: true, - }, - isVisible(newVal) { - if (newVal) { - this.$refs.dropdown.show(); - this.isDirty = false; - this.localSelectedLabels = this.selectedLabels; - } else { - this.$refs.dropdown.hide(); - this.setLabels(); - } - }, - selectedLabels(newVal) { - if (!this.isDirty) { - this.localSelectedLabels = newVal; - } - }, - }, - created() { - this.debouncedSearchKeyUpdate = debounce(this.setSearchKey, DEFAULT_DEBOUNCE_AND_THROTTLE_MS); - }, - beforeDestroy() { - this.debouncedSearchKeyUpdate.cancel(); - }, - methods: { - toggleDropdownContentsCreateView() { - this.showDropdownContentsCreateView = !this.showDropdownContentsCreateView; - }, - toggleDropdownContent() { - this.toggleDropdownContentsCreateView(); - // Required to recalculate dropdown position as its size changes - if (this.$refs.dropdown?.$refs.dropdown) { - this.$refs.dropdown.$refs.dropdown.$_popper.scheduleUpdate(); - } - }, - setLabels() { - if (!this.isDirty) { - return; - } - this.$emit('setLabels', this.localSelectedLabels); - }, - handleDropdownHide() { - this.$emit('closeDropdown'); - if (!isDropdownVariantSidebar(this.variant)) { - this.setLabels(); - } - }, - setSearchKey(value) { - this.searchKey = value; - }, - setFocus() { - this.$refs.header.focusInput(); - }, - hideDropdown() { - this.$refs.dropdown.hide(); - }, - showDropdown() { - this.$refs.dropdown.show(); - }, - clearSearch() { - if (!this.allowMultiselect || this.isStandalone) { - return; - } - this.searchKey = ''; - this.setFocus(); - }, - selectFirstItem() { - this.$refs.dropdownContentsView.selectFirstItem(); - }, - }, -}; -</script> - -<template> - <gl-dropdown - ref="dropdown" - :text="buttonText" - class="gl-w-full" - block - data-testid="labels-select-dropdown-contents" - data-qa-selector="labels_dropdown_content" - @hide="handleDropdownHide" - @shown="setFocus" - > - <template #header> - <dropdown-header - ref="header" - :search-key="searchKey" - :labels-create-title="labelsCreateTitle" - :labels-list-title="labelsListTitle" - :show-dropdown-contents-create-view="showDropdownContentsCreateView" - :is-standalone="isStandalone" - @toggleDropdownContentsCreateView="toggleDropdownContent" - @closeDropdown="hideDropdown" - @input="debouncedSearchKeyUpdate" - @searchEnter="selectFirstItem" - /> - </template> - <template #default> - <component - :is="dropdownContentsView" - ref="dropdownContentsView" - v-model="localSelectedLabels" - :search-key="searchKey" - :allow-multiselect="allowMultiselect" - :full-path="fullPath" - :workspace-type="workspaceType" - :attr-workspace-path="attrWorkspacePath" - :label-create-type="labelCreateType" - @hideCreateView="toggleDropdownContent" - @input="clearSearch" - /> - </template> - <template #footer> - <dropdown-footer - v-if="showDropdownFooter" - :footer-create-label-title="footerCreateLabelTitle" - :footer-manage-label-title="footerManageLabelTitle" - @toggleDropdownContentsCreateView="toggleDropdownContent" - /> - </template> - </gl-dropdown> -</template> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue deleted file mode 100644 index ce93ad216ec..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue +++ /dev/null @@ -1,200 +0,0 @@ -<script> -import { - GlAlert, - GlTooltipDirective, - GlButton, - GlFormInput, - GlLink, - GlLoadingIcon, -} from '@gitlab/ui'; -import produce from 'immer'; -import { createAlert } from '~/flash'; -import { __ } from '~/locale'; -import { workspaceLabelsQueries } from '~/sidebar/constants'; -import createLabelMutation from './graphql/create_label.mutation.graphql'; -import { LabelType } from './constants'; - -const errorMessage = __('Error creating label.'); - -export default { - components: { - GlAlert, - GlButton, - GlFormInput, - GlLink, - GlLoadingIcon, - }, - directives: { - GlTooltip: GlTooltipDirective, - }, - props: { - fullPath: { - type: String, - required: true, - }, - attrWorkspacePath: { - type: String, - required: true, - }, - labelCreateType: { - type: String, - required: true, - }, - workspaceType: { - type: String, - required: true, - }, - }, - data() { - return { - labelTitle: '', - selectedColor: '', - labelCreateInProgress: false, - error: undefined, - }; - }, - computed: { - disableCreate() { - return !this.labelTitle.length || !this.selectedColor.length || this.labelCreateInProgress; - }, - suggestedColors() { - const colorsMap = gon.suggested_label_colors; - return Object.keys(colorsMap).map((color) => ({ [color]: colorsMap[color] })); - }, - mutationVariables() { - const attributePath = this.labelCreateType === LabelType.group ? 'groupPath' : 'projectPath'; - - return { - title: this.labelTitle, - color: this.selectedColor, - [attributePath]: this.attrWorkspacePath, - }; - }, - }, - methods: { - getColorCode(color) { - return Object.keys(color).pop(); - }, - getColorName(color) { - return Object.values(color).pop(); - }, - handleColorClick(color) { - this.selectedColor = this.getColorCode(color); - }, - updateLabelsInCache(store, label) { - const { query } = workspaceLabelsQueries[this.workspaceType]; - - const sourceData = store.readQuery({ - query, - variables: { fullPath: this.fullPath, searchTerm: '' }, - }); - - const collator = new Intl.Collator('en'); - const data = produce(sourceData, (draftData) => { - const { nodes } = draftData.workspace.labels; - nodes.push(label); - nodes.sort((a, b) => collator.compare(a.title, b.title)); - }); - - store.writeQuery({ - query, - variables: { fullPath: this.fullPath, searchTerm: '' }, - data, - }); - }, - async createLabel() { - this.labelCreateInProgress = true; - try { - const { - data: { labelCreate }, - } = await this.$apollo.mutate({ - mutation: createLabelMutation, - variables: this.mutationVariables, - update: ( - store, - { - data: { - labelCreate: { label }, - }, - }, - ) => { - if (label) { - this.updateLabelsInCache(store, label); - } - }, - }); - if (labelCreate.errors.length) { - [this.error] = labelCreate.errors; - } else { - this.$emit('hideCreateView'); - } - } catch { - createAlert({ message: errorMessage }); - } - this.labelCreateInProgress = false; - }, - }, -}; -</script> - -<template> - <div class="labels-select-contents-create js-labels-create"> - <div class="dropdown-input"> - <gl-alert v-if="error" variant="danger" :dismissible="false" class="gl-mt-3"> - {{ error }} - </gl-alert> - <gl-form-input - v-model.trim="labelTitle" - class="gl-mt-3" - :placeholder="__('Name new label')" - :autofocus="true" - data-testid="label-title-input" - /> - </div> - <div class="dropdown-content gl-px-3"> - <div class="suggest-colors suggest-colors-dropdown gl-mt-0! gl-mb-3! gl-mb-0"> - <gl-link - v-for="(color, index) in suggestedColors" - :key="index" - v-gl-tooltip:tooltipcontainer - :style="{ backgroundColor: getColorCode(color) }" - :title="getColorName(color)" - @click.prevent="handleColorClick(color)" - /> - </div> - <div class="color-input-container gl-display-flex"> - <span - class="dropdown-label-color-preview gl-relative gl-display-inline-block" - data-testid="selected-color" - :style="{ backgroundColor: selectedColor }" - ></span> - <gl-form-input - v-model.trim="selectedColor" - class="gl-rounded-top-left-none gl-rounded-bottom-left-none gl-mb-2" - :placeholder="__('Use custom color #FF0000')" - data-testid="selected-color-text" - /> - </div> - </div> - <div class="dropdown-actions gl-display-flex gl-justify-content-space-between gl-pt-3 gl-px-3"> - <gl-button - :disabled="disableCreate" - category="primary" - variant="confirm" - class="gl-display-flex gl-align-items-center" - data-testid="create-button" - @click="createLabel" - > - <gl-loading-icon v-if="labelCreateInProgress" size="sm" :inline="true" class="mr-1" /> - {{ __('Create') }} - </gl-button> - <gl-button - class="js-btn-cancel-create" - data-testid="cancel-button" - @click.stop="$emit('hideCreateView')" - > - {{ __('Cancel') }} - </gl-button> - </div> - </div> -</template> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue deleted file mode 100644 index 1d854505d11..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue +++ /dev/null @@ -1,177 +0,0 @@ -<script> -import { GlDropdownForm, GlDropdownItem, GlLoadingIcon, GlIntersectionObserver } from '@gitlab/ui'; -import fuzzaldrinPlus from 'fuzzaldrin-plus'; -import { createAlert } from '~/flash'; -import { getIdFromGraphQLId } from '~/graphql_shared/utils'; -import { __ } from '~/locale'; -import { workspaceLabelsQueries } from '~/sidebar/constants'; -import LabelItem from './label_item.vue'; - -export default { - components: { - GlDropdownForm, - GlDropdownItem, - GlLoadingIcon, - GlIntersectionObserver, - LabelItem, - }, - model: { - prop: 'localSelectedLabels', - }, - props: { - allowMultiselect: { - type: Boolean, - required: true, - }, - localSelectedLabels: { - type: Array, - required: true, - }, - fullPath: { - type: String, - required: true, - }, - searchKey: { - type: String, - required: true, - }, - workspaceType: { - type: String, - required: true, - }, - }, - data() { - return { - labels: [], - isVisible: false, - }; - }, - apollo: { - labels: { - query() { - return workspaceLabelsQueries[this.workspaceType].query; - }, - variables() { - return { - fullPath: this.fullPath, - searchTerm: this.searchKey, - }; - }, - skip() { - return this.searchKey.length === 1 || !this.isVisible; - }, - update: (data) => data.workspace?.labels?.nodes || [], - error() { - createAlert({ message: __('Error fetching labels.') }); - }, - }, - }, - computed: { - labelsFetchInProgress() { - return this.$apollo.queries.labels.loading; - }, - localSelectedLabelsIds() { - return this.localSelectedLabels.map((label) => getIdFromGraphQLId(label.id)); - }, - visibleLabels() { - if (this.searchKey) { - return fuzzaldrinPlus.filter(this.labels, this.searchKey, { - key: ['title'], - }); - } - return this.labels; - }, - showNoMatchingResultsMessage() { - return Boolean(this.searchKey) && this.visibleLabels.length === 0; - }, - shouldHighlightFirstItem() { - return this.searchKey !== '' && this.visibleLabels.length > 0; - }, - }, - methods: { - isLabelSelected(label) { - return this.localSelectedLabelsIds.includes(getIdFromGraphQLId(label.id)); - }, - /** - * This method scrolls item from dropdown into - * the view if it is off the viewable area of the - * container. - */ - scrollIntoViewIfNeeded() { - const highlightedLabel = this.$refs.labelsListContainer.querySelector('.is-focused'); - - if (highlightedLabel) { - const container = this.$refs.labelsListContainer.getBoundingClientRect(); - const label = highlightedLabel.getBoundingClientRect(); - - if (label.bottom > container.bottom) { - this.$refs.labelsListContainer.scrollTop += label.bottom - container.bottom; - } else if (label.top < container.top) { - this.$refs.labelsListContainer.scrollTop -= container.top - label.top; - } - } - }, - updateSelectedLabels(label) { - let labels; - if (this.isLabelSelected(label)) { - labels = this.localSelectedLabels.filter( - ({ id }) => id !== getIdFromGraphQLId(label.id) && id !== label.id, - ); - } else { - labels = [...this.localSelectedLabels, label]; - } - this.$emit('input', labels); - }, - handleLabelClick(label) { - this.updateSelectedLabels(label); - if (!this.allowMultiselect) { - this.$emit('closeDropdown', this.localSelectedLabels); - } - }, - onDropdownAppear() { - this.isVisible = true; - }, - selectFirstItem() { - if (this.shouldHighlightFirstItem) { - this.handleLabelClick(this.visibleLabels[0]); - } - }, - }, -}; -</script> - -<template> - <gl-intersection-observer @appear="onDropdownAppear"> - <gl-dropdown-form class="labels-select-contents-list js-labels-list"> - <div ref="labelsListContainer" data-testid="dropdown-content"> - <gl-loading-icon - v-if="labelsFetchInProgress" - class="labels-fetch-loading gl-align-items-center gl-w-full gl-h-full gl-mb-3" - size="lg" - /> - <template v-else> - <gl-dropdown-item - v-for="(label, index) in visibleLabels" - :key="label.id" - :is-checked="isLabelSelected(label)" - is-check-centered - is-check-item - :active="shouldHighlightFirstItem && index === 0" - active-class="is-focused" - data-testid="labels-list" - @click.native.capture.stop="handleLabelClick(label)" - > - <label-item :label="label" /> - </gl-dropdown-item> - <gl-dropdown-item - v-show="showNoMatchingResultsMessage" - class="gl-p-3 gl-text-center" - data-testid="no-results" - > - {{ __('No matching results') }} - </gl-dropdown-item> - </template> - </div> - </gl-dropdown-form> - </gl-intersection-observer> -</template> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_footer.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_footer.vue deleted file mode 100644 index e67e704ffb8..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_footer.vue +++ /dev/null @@ -1,35 +0,0 @@ -<script> -import { GlDropdownItem } from '@gitlab/ui'; - -export default { - components: { - GlDropdownItem, - }, - inject: ['allowLabelCreate', 'labelsManagePath'], - props: { - footerCreateLabelTitle: { - type: String, - required: true, - }, - footerManageLabelTitle: { - type: String, - required: true, - }, - }, -}; -</script> - -<template> - <div data-testid="dropdown-footer"> - <gl-dropdown-item - v-if="allowLabelCreate" - data-testid="create-label-button" - @click.capture.native.stop="$emit('toggleDropdownContentsCreateView')" - > - {{ footerCreateLabelTitle }} - </gl-dropdown-item> - <gl-dropdown-item :href="labelsManagePath" @click.capture.native.stop> - {{ footerManageLabelTitle }} - </gl-dropdown-item> - </div> -</template> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_header.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_header.vue deleted file mode 100644 index 154a8e866d0..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_header.vue +++ /dev/null @@ -1,91 +0,0 @@ -<script> -import { GlButton, GlSearchBoxByType } from '@gitlab/ui'; - -export default { - components: { - GlButton, - GlSearchBoxByType, - }, - props: { - labelsCreateTitle: { - type: String, - required: true, - }, - labelsListTitle: { - type: String, - required: true, - }, - showDropdownContentsCreateView: { - type: Boolean, - required: true, - }, - labelsFetchInProgress: { - type: Boolean, - required: false, - default: false, - }, - searchKey: { - type: String, - required: true, - }, - isStandalone: { - type: Boolean, - required: false, - default: false, - }, - }, - computed: { - dropdownTitle() { - return this.showDropdownContentsCreateView ? this.labelsCreateTitle : this.labelsListTitle; - }, - }, - methods: { - focusInput() { - this.$refs.searchInput?.focusInput(); - }, - }, -}; -</script> - -<template> - <div data-testid="dropdown-header"> - <div - v-if="!isStandalone" - class="dropdown-title gl-display-flex gl-align-items-center gl-pt-0 gl-pb-3! gl-mb-0" - data-testid="dropdown-header-title" - > - <gl-button - v-if="showDropdownContentsCreateView" - :aria-label="__('Go back')" - variant="link" - size="small" - class="js-btn-back dropdown-header-button gl-p-0" - icon="arrow-left" - data-testid="go-back-button" - @click.stop="$emit('toggleDropdownContentsCreateView')" - /> - <span class="gl-flex-grow-1">{{ dropdownTitle }}</span> - <gl-button - :aria-label="__('Close')" - variant="link" - size="small" - class="dropdown-header-button gl-p-0!" - icon="close" - data-testid="close-button" - data-qa-selector="close_labels_dropdown_button" - @click="$emit('closeDropdown')" - /> - </div> - <gl-search-box-by-type - v-if="!showDropdownContentsCreateView" - ref="searchInput" - :value="searchKey" - :placeholder="__('Search labels')" - :disabled="labelsFetchInProgress" - data-qa-selector="dropdown_input_field" - data-testid="dropdown-input-field" - @input="$emit('input', $event)" - @keydown.enter="$emit('searchEnter', $event)" - /> - </div> -</template> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue deleted file mode 100644 index 57e3ee4aaa5..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue +++ /dev/null @@ -1,125 +0,0 @@ -<script> -import { GlIcon, GlLabel, GlTooltipDirective } from '@gitlab/ui'; -import { sortBy } from 'lodash'; -import { isScopedLabel } from '~/lib/utils/common_utils'; -import { s__, sprintf } from '~/locale'; - -export default { - directives: { - GlTooltip: GlTooltipDirective, - }, - components: { - GlIcon, - GlLabel, - }, - inject: ['allowScopedLabels'], - props: { - disableLabels: { - type: Boolean, - required: false, - default: false, - }, - selectedLabels: { - type: Array, - required: true, - }, - allowLabelRemove: { - type: Boolean, - required: true, - }, - labelsFilterBasePath: { - type: String, - required: true, - }, - labelsFilterParam: { - type: String, - required: true, - }, - }, - computed: { - sortedSelectedLabels() { - return sortBy(this.selectedLabels, (label) => (isScopedLabel(label) ? 0 : 1)); - }, - labelsList() { - const labelsString = this.selectedLabels.length - ? this.selectedLabels - .slice(0, 5) - .map((label) => label.title) - .join(', ') - : s__('LabelSelect|Labels'); - - if (this.selectedLabels.length > 5) { - return sprintf(s__('LabelSelect|%{labelsString}, and %{remainingLabelCount} more'), { - labelsString, - remainingLabelCount: this.selectedLabels.length - 5, - }); - } - - return labelsString; - }, - }, - methods: { - labelFilterUrl(label) { - return `${this.labelsFilterBasePath}?${this.labelsFilterParam}[]=${encodeURIComponent( - label.title, - )}`; - }, - scopedLabel(label) { - return this.allowScopedLabels && isScopedLabel(label); - }, - removeLabel(labelId) { - this.$emit('onLabelRemove', labelId); - }, - handleCollapsedClick() { - this.$emit('onCollapsedValueClick'); - }, - }, -}; -</script> - -<template> - <div - :class="{ - 'has-labels': selectedLabels.length, - }" - class="value issuable-show-labels js-value" - data-testid="value-wrapper" - > - <div - v-gl-tooltip.left.viewport - :title="labelsList" - class="sidebar-collapsed-icon" - @click="handleCollapsedClick" - > - <gl-icon name="labels" /> - <span class="collapse-truncated-title gl-pt-2 gl-px-3 gl-font-sm">{{ - selectedLabels.length - }}</span> - </div> - <span - v-if="!selectedLabels.length" - class="text-secondary hide-collapsed" - data-testid="empty-placeholder" - > - <slot></slot> - </span> - <template v-else> - <gl-label - v-for="label in sortedSelectedLabels" - :key="label.id" - class="hide-collapsed" - data-qa-selector="selected_label_content" - :data-qa-label-name="label.title" - :title="label.title" - :description="label.description" - :background-color="label.color" - :target="labelFilterUrl(label)" - :scoped="scopedLabel(label)" - :show-close-button="allowLabelRemove" - :disabled="disableLabels" - tooltip-placement="top" - @close="removeLabel(label.id)" - /> - </template> - </div> -</template> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/create_label.mutation.graphql b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/create_label.mutation.graphql deleted file mode 100644 index a9c791091fc..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/create_label.mutation.graphql +++ /dev/null @@ -1,12 +0,0 @@ -#import "~/graphql_shared/fragments/label.fragment.graphql" - -mutation createLabel($title: String!, $color: String, $projectPath: ID, $groupPath: ID) { - labelCreate( - input: { title: $title, color: $color, projectPath: $projectPath, groupPath: $groupPath } - ) { - label { - ...Label - } - errors - } -} diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/epic_labels.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/epic_labels.query.graphql deleted file mode 100644 index c442c17eb88..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/epic_labels.query.graphql +++ /dev/null @@ -1,15 +0,0 @@ -#import "~/graphql_shared/fragments/label.fragment.graphql" - -query epicLabels($fullPath: ID!, $iid: ID) { - workspace: group(fullPath: $fullPath) { - id - issuable: epic(iid: $iid) { - id - labels { - nodes { - ...Label - } - } - } - } -} diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/epic_update_labels.mutation.graphql b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/epic_update_labels.mutation.graphql deleted file mode 100644 index cb054e2968f..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/epic_update_labels.mutation.graphql +++ /dev/null @@ -1,15 +0,0 @@ -#import "~/graphql_shared/fragments/label.fragment.graphql" - -mutation updateEpicLabels($input: UpdateEpicInput!) { - updateIssuableLabels: updateEpic(input: $input) { - issuable: epic { - id - labels { - nodes { - ...Label - } - } - } - errors - } -} diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/group_labels.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/group_labels.query.graphql deleted file mode 100644 index ce1a69f84c0..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/group_labels.query.graphql +++ /dev/null @@ -1,12 +0,0 @@ -#import "~/graphql_shared/fragments/label.fragment.graphql" - -query groupLabels($fullPath: ID!, $searchTerm: String) { - workspace: group(fullPath: $fullPath) { - id - labels(searchTerm: $searchTerm, onlyGroupLabels: true, includeAncestorGroups: true) { - nodes { - ...Label - } - } - } -} diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/issue_labels.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/issue_labels.query.graphql deleted file mode 100644 index 2904857270e..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/issue_labels.query.graphql +++ /dev/null @@ -1,15 +0,0 @@ -#import "~/graphql_shared/fragments/label.fragment.graphql" - -query issueLabels($fullPath: ID!, $iid: String) { - workspace: project(fullPath: $fullPath) { - id - issuable: issue(iid: $iid) { - id - labels { - nodes { - ...Label - } - } - } - } -} diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/merge_request_labels.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/merge_request_labels.query.graphql deleted file mode 100644 index e0cdfd91658..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/merge_request_labels.query.graphql +++ /dev/null @@ -1,15 +0,0 @@ -#import "~/graphql_shared/fragments/label.fragment.graphql" - -query mergeRequestLabels($fullPath: ID!, $iid: String!) { - workspace: project(fullPath: $fullPath) { - id - issuable: mergeRequest(iid: $iid) { - id - labels { - nodes { - ...Label - } - } - } - } -} diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql deleted file mode 100644 index a7c24620aad..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql +++ /dev/null @@ -1,12 +0,0 @@ -#import "~/graphql_shared/fragments/label.fragment.graphql" - -query projectLabels($fullPath: ID!, $searchTerm: String) { - workspace: project(fullPath: $fullPath) { - id - labels(searchTerm: $searchTerm, includeAncestorGroups: true) { - nodes { - ...Label - } - } - } -} diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/label_item.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/label_item.vue deleted file mode 100644 index 314ffbaf84c..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/label_item.vue +++ /dev/null @@ -1,21 +0,0 @@ -<script> -export default { - props: { - label: { - type: Object, - required: true, - }, - }, -}; -</script> - -<template> - <div class="gl-display-flex gl-align-items-center gl-word-break-word"> - <span - class="dropdown-label-box gl-flex-shrink-0 gl-top-0 gl-mr-3" - :style="{ 'background-color': label.color }" - data-testid="label-color-box" - ></span> - <span>{{ label.title }}</span> - </div> -</template> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue deleted file mode 100644 index 2c27a69d587..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue +++ /dev/null @@ -1,406 +0,0 @@ -<script> -import { debounce } from 'lodash'; -import issuableLabelsSubscription from 'ee_else_ce/sidebar/queries/issuable_labels.subscription.graphql'; -import { MutationOperationMode, getIdFromGraphQLId } from '~/graphql_shared/utils'; -import { createAlert } from '~/flash'; -import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; -import { IssuableType } from '~/issues/constants'; - -import { __ } from '~/locale'; -import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue'; -import { issuableLabelsQueries } from '~/sidebar/constants'; -import { DEBOUNCE_DROPDOWN_DELAY, DropdownVariant } from './constants'; -import DropdownContents from './dropdown_contents.vue'; -import DropdownValue from './dropdown_value.vue'; -import { - isDropdownVariantSidebar, - isDropdownVariantStandalone, - isDropdownVariantEmbedded, -} from './utils'; - -export default { - components: { - DropdownValue, - DropdownContents, - SidebarEditableItem, - }, - mixins: [glFeatureFlagsMixin()], - inject: { - allowLabelEdit: { - default: false, - }, - }, - props: { - iid: { - type: String, - required: false, - default: '', - }, - fullPath: { - type: String, - required: true, - }, - allowLabelRemove: { - type: Boolean, - required: false, - default: false, - }, - allowMultiselect: { - type: Boolean, - required: false, - default: false, - }, - variant: { - type: String, - required: false, - default: DropdownVariant.Sidebar, - }, - labelsFilterBasePath: { - type: String, - required: false, - default: '', - }, - labelsFilterParam: { - type: String, - required: false, - default: 'label_name', - }, - dropdownButtonText: { - type: String, - required: false, - default: __('Label'), - }, - labelsListTitle: { - type: String, - required: false, - default: __('Assign labels'), - }, - labelsCreateTitle: { - type: String, - required: false, - default: __('Create group label'), - }, - footerCreateLabelTitle: { - type: String, - required: false, - default: __('Create group label'), - }, - footerManageLabelTitle: { - type: String, - required: false, - default: __('Manage group labels'), - }, - issuableType: { - type: String, - required: true, - }, - workspaceType: { - type: String, - required: true, - }, - attrWorkspacePath: { - type: String, - required: true, - }, - labelCreateType: { - type: String, - required: true, - }, - }, - data() { - return { - contentIsOnViewport: true, - issuable: null, - labelsSelectInProgress: false, - oldIid: null, - sidebarExpandedOnClick: false, - }; - }, - computed: { - isLoading() { - return this.labelsSelectInProgress || this.$apollo.queries.issuable.loading; - }, - issuableLabelIds() { - return this.issuableLabels.map((label) => label.id); - }, - issuableLabels() { - return this.issuable?.labels.nodes || []; - }, - issuableId() { - return this.issuable?.id; - }, - }, - apollo: { - issuable: { - query() { - return issuableLabelsQueries[this.issuableType].issuableQuery; - }, - skip() { - return !isDropdownVariantSidebar(this.variant); - }, - variables() { - return { - iid: this.iid, - fullPath: this.fullPath, - }; - }, - update(data) { - return data.workspace?.issuable; - }, - error() { - createAlert({ message: __('Error fetching labels.') }); - }, - subscribeToMore: { - document() { - return issuableLabelsSubscription; - }, - variables() { - return { - issuableId: this.issuableId, - }; - }, - skip() { - return !this.issuableId || !this.isDropdownVariantSidebar; - }, - updateQuery( - _, - { - subscriptionData: { - data: { issuableLabelsUpdated }, - }, - }, - ) { - if (issuableLabelsUpdated) { - const { - id, - labels: { nodes }, - } = issuableLabelsUpdated; - this.$emit('updateSelectedLabels', { id, labels: nodes }); - } - }, - }, - }, - }, - watch: { - iid(_, oldVal) { - this.oldIid = oldVal; - }, - }, - mounted() { - document.addEventListener('toggleSidebarRevealLabelsDropdown', this.handleCollapsedValueClick); - }, - beforeDestroy() { - document.removeEventListener( - 'toggleSidebarRevealLabelsDropdown', - this.handleCollapsedValueClick, - ); - }, - methods: { - handleDropdownClose(labels) { - if (this.iid !== '') { - this.updateSelectedLabels(this.getUpdateVariables(labels)); - } else { - this.$emit('updateSelectedLabels', { labels }); - } - - this.collapseEditableItem(); - }, - collapseEditableItem() { - this.$refs.editable?.collapse(); - if (this.sidebarExpandedOnClick) { - this.sidebarExpandedOnClick = false; - this.$emit('toggleCollapse'); - } - }, - handleCollapsedValueClick() { - this.sidebarExpandedOnClick = true; - this.$emit('toggleCollapse'); - debounce(() => { - this.$refs.editable.toggle(); - this.$refs.dropdownContents.showDropdown(); - }, DEBOUNCE_DROPDOWN_DELAY)(); - }, - getUpdateVariables(labels) { - let labelIds = []; - - labelIds = labels.map(({ id }) => id); - const currentIid = this.oldIid || this.iid; - - const updateVariables = { - iid: currentIid, - projectPath: this.fullPath, - labelIds, - }; - - switch (this.issuableType) { - case IssuableType.Issue: - return updateVariables; - case IssuableType.MergeRequest: - return { - ...updateVariables, - operationMode: MutationOperationMode.Replace, - }; - case IssuableType.Epic: - return { - iid: currentIid, - groupPath: this.fullPath, - addLabelIds: labelIds.map((id) => getIdFromGraphQLId(id)), - removeLabelIds: this.issuableLabelIds - .filter((id) => !labelIds.includes(id)) - .map((id) => getIdFromGraphQLId(id)), - }; - default: - return {}; - } - }, - updateSelectedLabels(inputVariables) { - this.labelsSelectInProgress = true; - - this.$apollo - .mutate({ - mutation: issuableLabelsQueries[this.issuableType].mutation, - variables: { input: inputVariables }, - }) - .then(({ data }) => { - if (data.updateIssuableLabels?.errors?.length) { - throw new Error(); - } - - this.$emit('updateSelectedLabels', { - id: data.updateIssuableLabels?.issuable?.id, - labels: data.updateIssuableLabels?.issuable?.labels?.nodes, - }); - }) - .catch((error) => - createAlert({ - message: __('An error occurred while updating labels.'), - captureError: true, - error, - }), - ) - .finally(() => { - this.labelsSelectInProgress = false; - }); - }, - getRemoveVariables(labelId) { - const removeVariables = { - iid: this.iid, - projectPath: this.fullPath, - }; - - switch (this.issuableType) { - case IssuableType.Issue: - return { - ...removeVariables, - removeLabelIds: [labelId], - }; - case IssuableType.MergeRequest: - return { - ...removeVariables, - labelIds: [labelId], - operationMode: MutationOperationMode.Remove, - }; - case IssuableType.Epic: - return { - iid: this.iid, - removeLabelIds: [getIdFromGraphQLId(labelId)], - groupPath: this.fullPath, - }; - default: - return {}; - } - }, - handleLabelRemove(labelId) { - this.updateSelectedLabels(this.getRemoveVariables(labelId)); - this.$emit('onLabelRemove', labelId); - }, - isDropdownVariantSidebar, - isDropdownVariantStandalone, - isDropdownVariantEmbedded, - }, -}; -</script> - -<template> - <div - class="labels-select-wrapper gl-relative" - :class="{ - 'is-standalone': isDropdownVariantStandalone(variant), - 'is-embedded': isDropdownVariantEmbedded(variant), - }" - data-testid="sidebar-labels" - data-qa-selector="labels_block" - > - <template v-if="isDropdownVariantSidebar(variant)"> - <sidebar-editable-item - ref="editable" - :title="__('Labels')" - :loading="isLoading" - :can-edit="allowLabelEdit" - @open="oldIid = null" - > - <template #collapsed> - <dropdown-value - :disable-labels="labelsSelectInProgress" - :selected-labels="issuableLabels" - :allow-label-remove="allowLabelRemove" - :labels-filter-base-path="labelsFilterBasePath" - :labels-filter-param="labelsFilterParam" - @onLabelRemove="handleLabelRemove" - @onCollapsedValueClick="handleCollapsedValueClick" - > - <slot></slot> - </dropdown-value> - </template> - <template #default="{ edit }"> - <dropdown-value - :disable-labels="labelsSelectInProgress" - :selected-labels="issuableLabels" - :allow-label-remove="allowLabelRemove" - :labels-filter-base-path="labelsFilterBasePath" - :labels-filter-param="labelsFilterParam" - class="gl-mb-2" - @onLabelRemove="handleLabelRemove" - > - <slot></slot> - </dropdown-value> - <dropdown-contents - ref="dropdownContents" - :dropdown-button-text="dropdownButtonText" - :allow-multiselect="allowMultiselect" - :labels-list-title="labelsListTitle" - :footer-create-label-title="footerCreateLabelTitle" - :footer-manage-label-title="footerManageLabelTitle" - :labels-create-title="labelsCreateTitle" - :selected-labels="issuableLabels" - :variant="variant" - :is-visible="edit" - :full-path="fullPath" - :workspace-type="workspaceType" - :attr-workspace-path="attrWorkspacePath" - :label-create-type="labelCreateType" - @setLabels="handleDropdownClose" - @closeDropdown="collapseEditableItem" - /> - </template> - </sidebar-editable-item> - </template> - <dropdown-contents - v-else - ref="dropdownContents" - :allow-multiselect="allowMultiselect" - :dropdown-button-text="dropdownButtonText" - :labels-list-title="labelsListTitle" - :footer-create-label-title="footerCreateLabelTitle" - :footer-manage-label-title="footerManageLabelTitle" - :labels-create-title="labelsCreateTitle" - :selected-labels="issuableLabels" - :variant="variant" - :full-path="fullPath" - :workspace-type="workspaceType" - :attr-workspace-path="attrWorkspacePath" - :label-create-type="labelCreateType" - @setLabels="handleDropdownClose" - /> - </div> -</template> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/utils.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/utils.js deleted file mode 100644 index b5cd946a189..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/utils.js +++ /dev/null @@ -1,22 +0,0 @@ -import { DropdownVariant } from './constants'; - -/** - * Returns boolean representing whether dropdown variant - * is `sidebar` - * @param {string} variant - */ -export const isDropdownVariantSidebar = (variant) => variant === DropdownVariant.Sidebar; - -/** - * Returns boolean representing whether dropdown variant - * is `standalone` - * @param {string} variant - */ -export const isDropdownVariantStandalone = (variant) => variant === DropdownVariant.Standalone; - -/** - * Returns boolean representing whether dropdown variant - * is `embedded` - * @param {string} variant - */ -export const isDropdownVariantEmbedded = (variant) => variant === DropdownVariant.Embedded; diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_alert_assignees.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/queries/get_alert_assignees.query.graphql deleted file mode 100644 index bb6c7181e5c..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_alert_assignees.query.graphql +++ /dev/null @@ -1,21 +0,0 @@ -#import "~/graphql_shared/fragments/user.fragment.graphql" -#import "~/graphql_shared/fragments/user_availability.fragment.graphql" - -query alertAssignees( - $domain: AlertManagementDomainFilter = threat_monitoring - $fullPath: ID! - $iid: String! -) { - workspace: project(fullPath: $fullPath) { - id - issuable: alertManagementAlert(domain: $domain, iid: $iid) { - iid - assignees { - nodes { - ...User - ...UserAvailability - } - } - } - } -} diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_assignees.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_assignees.query.graphql deleted file mode 100644 index 4af07366a6d..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_assignees.query.graphql +++ /dev/null @@ -1,21 +0,0 @@ -#import "~/graphql_shared/fragments/user.fragment.graphql" -#import "~/graphql_shared/fragments/user_availability.fragment.graphql" - -query issueAssignees($fullPath: ID!, $iid: String!) { - workspace: project(fullPath: $fullPath) { - id - issuable: issue(iid: $iid) { - id - author { - ...User - ...UserAvailability - } - assignees { - nodes { - ...User - ...UserAvailability - } - } - } - } -} diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_participants.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_participants.query.graphql deleted file mode 100644 index eae5e96ac46..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_participants.query.graphql +++ /dev/null @@ -1,17 +0,0 @@ -#import "~/graphql_shared/fragments/user.fragment.graphql" -#import "~/graphql_shared/fragments/user_availability.fragment.graphql" - -query issueParticipants($fullPath: ID!, $iid: String!, $getStatus: Boolean = false) { - workspace: project(fullPath: $fullPath) { - id - issuable: issue(iid: $iid) { - id - participants { - nodes { - ...User - ...UserAvailability @include(if: $getStatus) - } - } - } - } -} diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_timelogs.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_timelogs.query.graphql deleted file mode 100644 index b127b8ec5a9..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_timelogs.query.graphql +++ /dev/null @@ -1,13 +0,0 @@ -#import "~/graphql_shared/fragments/issuable_timelogs.fragment.graphql" - -query issueTimeTrackingReport($id: IssueID!) { - issuable: issue(id: $id) { - id - title - timelogs { - nodes { - ...TimelogFragment - } - } - } -} diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_merge_request_reviewers.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/queries/get_merge_request_reviewers.query.graphql deleted file mode 100644 index f087ca6c982..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_merge_request_reviewers.query.graphql +++ /dev/null @@ -1,26 +0,0 @@ -#import "~/graphql_shared/fragments/user.fragment.graphql" -#import "~/graphql_shared/fragments/user_availability.fragment.graphql" - -query mergeRequestReviewers($fullPath: ID!, $iid: String!) { - workspace: project(fullPath: $fullPath) { - id - issuable: mergeRequest(iid: $iid) { - id - reviewers { - nodes { - ...User - ...UserAvailability - mergeRequestInteraction { - canMerge - canUpdate - approved - reviewed - } - } - } - userPermissions { - adminMergeRequest - } - } - } -} diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_assignees.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_assignees.query.graphql deleted file mode 100644 index f70cd723f2e..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_assignees.query.graphql +++ /dev/null @@ -1,30 +0,0 @@ -#import "~/graphql_shared/fragments/user.fragment.graphql" -#import "~/graphql_shared/fragments/user_availability.fragment.graphql" - -query getMrAssignees($fullPath: ID!, $iid: String!) { - workspace: project(fullPath: $fullPath) { - id - issuable: mergeRequest(iid: $iid) { - id - author { - ...User - ...UserAvailability - mergeRequestInteraction { - canMerge - } - } - assignees { - nodes { - ...User - ...UserAvailability - mergeRequestInteraction { - canMerge - } - } - } - userPermissions { - canMerge - } - } - } -} diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_participants.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_participants.query.graphql deleted file mode 100644 index 2781ac71f31..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_participants.query.graphql +++ /dev/null @@ -1,17 +0,0 @@ -#import "~/graphql_shared/fragments/user.fragment.graphql" -#import "~/graphql_shared/fragments/user_availability.fragment.graphql" - -query getMrParticipants($fullPath: ID!, $iid: String!, $getStatus: Boolean = false) { - workspace: project(fullPath: $fullPath) { - id - issuable: mergeRequest(iid: $iid) { - id - participants { - nodes { - ...User - ...UserAvailability @include(if: $getStatus) - } - } - } - } -} diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_timelogs.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_timelogs.query.graphql deleted file mode 100644 index 17f548b44b5..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_timelogs.query.graphql +++ /dev/null @@ -1,13 +0,0 @@ -#import "~/graphql_shared/fragments/issuable_timelogs.fragment.graphql" - -query mrTimeTrackingReport($id: MergeRequestID!) { - issuable: mergeRequest(id: $id) { - id - title - timelogs { - nodes { - ...TimelogFragment - } - } - } -} diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/merge_request_reviewers.subscription.graphql b/app/assets/javascripts/vue_shared/components/sidebar/queries/merge_request_reviewers.subscription.graphql deleted file mode 100644 index a1b16b378b3..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/queries/merge_request_reviewers.subscription.graphql +++ /dev/null @@ -1,22 +0,0 @@ -#import "~/graphql_shared/fragments/user.fragment.graphql" -#import "~/graphql_shared/fragments/user_availability.fragment.graphql" - -subscription mergeRequestReviewersUpdated($issuableId: IssuableID!) { - mergeRequestReviewersUpdated(issuableId: $issuableId) { - ... on MergeRequest { - id - reviewers { - nodes { - ...User - ...UserAvailability - mergeRequestInteraction { - canMerge - canUpdate - approved - reviewed - } - } - } - } - } -} diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/update_issue_assignees.mutation.graphql b/app/assets/javascripts/vue_shared/components/sidebar/queries/update_issue_assignees.mutation.graphql deleted file mode 100644 index 24de5ea4fe3..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/queries/update_issue_assignees.mutation.graphql +++ /dev/null @@ -1,18 +0,0 @@ -#import "~/graphql_shared/fragments/user.fragment.graphql" -#import "~/graphql_shared/fragments/user_availability.fragment.graphql" - -mutation issueSetAssignees($iid: String!, $assigneeUsernames: [String!]!, $fullPath: ID!) { - issuableSetAssignees: issueSetAssignees( - input: { iid: $iid, assigneeUsernames: $assigneeUsernames, projectPath: $fullPath } - ) { - issuable: issue { - id - assignees { - nodes { - ...User - ...UserAvailability - } - } - } - } -} diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/update_mr_assignees.mutation.graphql b/app/assets/javascripts/vue_shared/components/sidebar/queries/update_mr_assignees.mutation.graphql deleted file mode 100644 index 5fec2ccbdfb..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/queries/update_mr_assignees.mutation.graphql +++ /dev/null @@ -1,21 +0,0 @@ -#import "~/graphql_shared/fragments/user.fragment.graphql" -#import "~/graphql_shared/fragments/user_availability.fragment.graphql" - -mutation mergeRequestSetAssignees($iid: String!, $assigneeUsernames: [String!]!, $fullPath: ID!) { - issuableSetAssignees: mergeRequestSetAssignees( - input: { iid: $iid, assigneeUsernames: $assigneeUsernames, projectPath: $fullPath } - ) { - issuable: mergeRequest { - id - assignees { - nodes { - ...User - ...UserAvailability - mergeRequestInteraction { - canMerge - } - } - } - } - } -} diff --git a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.stories.js b/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.stories.js deleted file mode 100644 index 465ee9aa0d4..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.stories.js +++ /dev/null @@ -1,21 +0,0 @@ -import TodoButton from './todo_button.vue'; - -export default { - component: TodoButton, - title: 'vue_shared/sidebar/todo_toggle/todo_button', -}; - -const Template = (args, { argTypes }) => ({ - components: { TodoButton }, - props: Object.keys(argTypes), - template: '<todo-button v-bind="$props" v-on="$props" />', -}); - -export const Default = Template.bind({}); -Default.argTypes = { - isTodo: { - description: 'True if to-do is unresolved (i.e. not "done")', - control: { type: 'boolean' }, - }, - click: { action: 'clicked' }, -}; diff --git a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.vue b/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.vue deleted file mode 100644 index cdc7422c7df..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.vue +++ /dev/null @@ -1,44 +0,0 @@ -<script> -import { GlButton } from '@gitlab/ui'; -import { todoLabel, updateGlobalTodoCount } from './utils'; - -export default { - components: { - GlButton, - }, - props: { - isTodo: { - type: Boolean, - required: false, - default: true, - }, - }, - computed: { - buttonLabel() { - return todoLabel(this.isTodo); - }, - }, - methods: { - incrementGlobalTodoCount() { - updateGlobalTodoCount(1); - }, - decrementGlobalTodoCount() { - updateGlobalTodoCount(-1); - }, - onToggle(event) { - if (this.isTodo) { - this.decrementGlobalTodoCount(); - } else { - this.incrementGlobalTodoCount(); - } - this.$emit('click', event); - }, - }, -}; -</script> - -<template> - <gl-button v-bind="$attrs" :aria-label="buttonLabel" @click="onToggle($event)"> - {{ buttonLabel }} - </gl-button> -</template> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/utils.js b/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/utils.js deleted file mode 100644 index 098ab72dfb5..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/utils.js +++ /dev/null @@ -1,21 +0,0 @@ -import { __ } from '~/locale'; - -export const todoLabel = (hasTodo) => { - return hasTodo ? __('Mark as done') : __('Add a to do'); -}; - -export const updateGlobalTodoCount = (additionalTodoCount) => { - const countContainer = document.querySelector('.js-todos-count'); - - if (countContainer === null) return; - - const currentCount = parseInt(countContainer.innerText, 10); - - const todoToggleEvent = new CustomEvent('todo:toggle', { - detail: { - count: Math.max(currentCount + additionalTodoCount, 0), - }, - }); - - document.dispatchEvent(todoToggleEvent); -}; diff --git a/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue b/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue deleted file mode 100644 index 6dacf4e10d3..00000000000 --- a/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue +++ /dev/null @@ -1,55 +0,0 @@ -<script> -import { GlButton, GlTooltipDirective } from '@gitlab/ui'; -import { __ } from '~/locale'; - -export default { - name: 'ToggleSidebar', - components: { - GlButton, - }, - directives: { - GlTooltip: GlTooltipDirective, - }, - props: { - collapsed: { - type: Boolean, - required: true, - }, - cssClasses: { - type: String, - required: false, - default: '', - }, - }, - computed: { - tooltipLabel() { - return this.collapsed ? __('Expand sidebar') : __('Collapse sidebar'); - }, - buttonIcon() { - return this.collapsed ? 'chevron-double-lg-left' : 'chevron-double-lg-right'; - }, - allCssClasses() { - return [this.cssClasses, { 'js-sidebar-collapsed': this.collapsed }]; - }, - }, - methods: { - toggle() { - this.$emit('toggle'); - }, - }, -}; -</script> - -<template> - <gl-button - v-gl-tooltip:body.viewport.left - :title="tooltipLabel" - :class="allCssClasses" - class="gutter-toggle btn-sidebar-action js-sidebar-vue-toggle" - :icon="buttonIcon" - category="tertiary" - size="small" - :aria-label="__('toggle collapse')" - @click="toggle" - /> -</template> |