diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-06-09 18:09:21 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-06-09 18:09:21 +0300 |
commit | d2675fa4de909714fcc6dc1bdd7bee9ce5e3af34 (patch) | |
tree | 0c0fbdd55fbb3a1616b10775113bf8320c9529c8 /app/assets/javascripts/vue_shared/components/color_select_dropdown | |
parent | 48d25238c386a89e8a6af218eeb290936a8f7595 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/vue_shared/components/color_select_dropdown')
10 files changed, 538 insertions, 0 deletions
diff --git a/app/assets/javascripts/vue_shared/components/color_select_dropdown/color_item.vue b/app/assets/javascripts/vue_shared/components/color_select_dropdown/color_item.vue new file mode 100644 index 00000000000..92817d5fa70 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/color_select_dropdown/color_item.vue @@ -0,0 +1,25 @@ +<script> +export default { + props: { + color: { + type: String, + required: true, + }, + title: { + type: String, + required: true, + }, + }, +}; +</script> + +<template> + <div> + <span + class="dropdown-label-box gl-flex-shrink-0 gl-top-1 gl-mr-0" + data-testid="color-item" + :style="{ backgroundColor: color }" + ></span> + <span class="hide-collapsed">{{ title }}</span> + </div> +</template> diff --git a/app/assets/javascripts/vue_shared/components/color_select_dropdown/color_select_root.vue b/app/assets/javascripts/vue_shared/components/color_select_dropdown/color_select_root.vue new file mode 100644 index 00000000000..6b79883d76b --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/color_select_dropdown/color_select_root.vue @@ -0,0 +1,214 @@ +<script> +import createFlash from '~/flash'; +import { s__ } from '~/locale'; +import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue'; +import { DEFAULT_COLOR, COLOR_WIDGET_COLOR, DROPDOWN_VARIANT, ISSUABLE_COLORS } from './constants'; +import DropdownContents from './dropdown_contents.vue'; +import DropdownValue from './dropdown_value.vue'; +import { isDropdownVariantSidebar, isDropdownVariantEmbedded } from './utils'; +import epicColorQuery from './graphql/epic_color.query.graphql'; +import updateEpicColorMutation from './graphql/epic_update_color.mutation.graphql'; + +export default { + i18n: { + assignColor: s__('ColorWidget|Assign epic color'), + dropdownButtonText: COLOR_WIDGET_COLOR, + fetchingError: s__('ColorWidget|Error fetching epic color.'), + updatingError: s__('ColorWidget|An error occurred while updating color.'), + widgetTitle: COLOR_WIDGET_COLOR, + }, + components: { + DropdownValue, + DropdownContents, + SidebarEditableItem, + }, + props: { + allowEdit: { + type: Boolean, + required: false, + default: false, + }, + iid: { + type: String, + required: false, + default: '', + }, + fullPath: { + type: String, + required: true, + }, + variant: { + type: String, + required: false, + default: DROPDOWN_VARIANT.Sidebar, + }, + dropdownButtonText: { + type: String, + required: false, + default: COLOR_WIDGET_COLOR, + }, + dropdownTitle: { + type: String, + required: false, + default: s__('ColorWidget|Assign epic color'), + }, + }, + data() { + return { + issuableColor: { + color: '', + title: '', + }, + colorUpdateInProgress: false, + oldIid: null, + sidebarExpandedOnClick: false, + }; + }, + apollo: { + issuableColor: { + query: epicColorQuery, + skip() { + return !isDropdownVariantSidebar(this.variant); + }, + variables() { + return { + iid: this.iid, + fullPath: this.fullPath, + }; + }, + update(data) { + const issuableColor = data.workspace?.issuable?.color; + + if (issuableColor) { + return ISSUABLE_COLORS.find((color) => color.color === issuableColor) ?? DEFAULT_COLOR; + } + + return DEFAULT_COLOR; + }, + error() { + createFlash({ + message: this.$options.i18n.fetchingError, + captureError: true, + }); + }, + }, + }, + computed: { + isLoading() { + return this.colorUpdateInProgress || this.$apollo.queries.issuableColor.loading; + }, + }, + watch: { + iid(_, oldVal) { + this.oldIid = oldVal; + }, + }, + methods: { + handleDropdownClose(color) { + if (this.iid !== '') { + this.updateSelectedColor(this.getUpdateVariables(color)); + } else { + this.$emit('updateSelectedColor', color); + } + + this.collapseEditableItem(); + }, + collapseEditableItem() { + this.$refs.editable?.collapse(); + if (this.sidebarExpandedOnClick) { + this.sidebarExpandedOnClick = false; + this.$emit('toggleCollapse'); + } + }, + getUpdateVariables(color) { + const currentIid = this.oldIid || this.iid; + + return { + iid: currentIid, + groupPath: this.fullPath, + color: color.color, + }; + }, + updateSelectedColor(inputVariables) { + this.colorUpdateInProgress = true; + + this.$apollo + .mutate({ + mutation: updateEpicColorMutation, + variables: { input: inputVariables }, + }) + .then(({ data }) => { + if (data.updateIssuableColor?.errors?.length) { + throw new Error(); + } + + this.$emit('updateSelectedColor', { + id: data.updateIssuableColor?.issuable?.id, + color: data.updateIssuableColor?.issuable?.color, + }); + }) + .catch((error) => + createFlash({ + message: this.$options.i18n.updatingError, + captureError: true, + error, + }), + ) + .finally(() => { + this.colorUpdateInProgress = false; + }); + }, + isDropdownVariantSidebar, + isDropdownVariantEmbedded, + }, +}; +</script> + +<template> + <div + class="labels-select-wrapper gl-relative" + :class="{ + 'is-embedded': isDropdownVariantEmbedded(variant), + }" + > + <template v-if="isDropdownVariantSidebar(variant)"> + <sidebar-editable-item + ref="editable" + :title="$options.i18n.widgetTitle" + :loading="isLoading" + :can-edit="allowEdit" + @open="oldIid = null" + > + <template #collapsed> + <dropdown-value :selected-color="issuableColor"> + <slot></slot> + </dropdown-value> + </template> + <template #default="{ edit }"> + <dropdown-value :selected-color="issuableColor" class="gl-mb-2"> + <slot></slot> + </dropdown-value> + <dropdown-contents + ref="dropdownContents" + :dropdown-button-text="dropdownButtonText" + :dropdown-title="dropdownTitle" + :selected-color="issuableColor" + :variant="variant" + :is-visible="edit" + @setColor="handleDropdownClose" + @closeDropdown="collapseEditableItem" + /> + </template> + </sidebar-editable-item> + </template> + <dropdown-contents + v-else + ref="dropdownContents" + :dropdown-button-text="dropdownButtonText" + :dropdown-title="dropdownTitle" + :selected-color="issuableColor" + :variant="variant" + @setColor="handleDropdownClose" + /> + </div> +</template> diff --git a/app/assets/javascripts/vue_shared/components/color_select_dropdown/constants.js b/app/assets/javascripts/vue_shared/components/color_select_dropdown/constants.js new file mode 100644 index 00000000000..c70785abd1e --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/color_select_dropdown/constants.js @@ -0,0 +1,30 @@ +import { __, s__ } from '~/locale'; + +export const COLOR_WIDGET_COLOR = s__('ColorWidget|Color'); + +export const DROPDOWN_VARIANT = { + Sidebar: 'sidebar', + Embedded: 'embedded', +}; + +export const DEFAULT_COLOR = { title: __('SuggestedColors|Blue'), color: '#1068bf' }; + +export const ISSUABLE_COLORS = [ + DEFAULT_COLOR, + { + title: s__('SuggestedColors|Green'), + color: '#217645', + }, + { + title: s__('SuggestedColors|Red'), + color: '#c91c00', + }, + { + title: s__('SuggestedColors|Orange'), + color: '#9e5400', + }, + { + title: s__('SuggestedColors|Purple'), + color: '#694cc0', + }, +]; diff --git a/app/assets/javascripts/vue_shared/components/color_select_dropdown/dropdown_contents.vue b/app/assets/javascripts/vue_shared/components/color_select_dropdown/dropdown_contents.vue new file mode 100644 index 00000000000..4eb1d3d08ca --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/color_select_dropdown/dropdown_contents.vue @@ -0,0 +1,109 @@ +<script> +import { GlDropdown } from '@gitlab/ui'; +import DropdownContentsColorView from './dropdown_contents_color_view.vue'; +import DropdownHeader from './dropdown_header.vue'; +import { isDropdownVariantSidebar } from './utils'; + +export default { + components: { + DropdownContentsColorView, + DropdownHeader, + GlDropdown, + }, + props: { + dropdownTitle: { + type: String, + required: true, + }, + selectedColor: { + type: Object, + required: true, + }, + dropdownButtonText: { + type: String, + required: true, + }, + variant: { + type: String, + required: true, + }, + isVisible: { + type: Boolean, + required: false, + default: false, + }, + }, + data() { + return { + showDropdownContentsCreateView: false, + localSelectedColor: this.selectedColor, + isDirty: false, + }; + }, + computed: { + buttonText() { + if (!this.localSelectedColor?.title) { + return this.dropdownButtonText; + } + + return this.localSelectedColor.title; + }, + }, + watch: { + localSelectedColor: { + handler() { + this.isDirty = true; + }, + deep: true, + }, + isVisible(newVal) { + if (newVal) { + this.$refs.dropdown.show(); + this.isDirty = false; + this.localSelectedColor = this.selectedColor; + } else { + this.$refs.dropdown.hide(); + this.setColor(); + } + }, + selectedColor(newVal) { + if (!this.isDirty) { + this.localSelectedColor = newVal; + } + }, + }, + methods: { + setColor() { + if (!this.isDirty) { + return; + } + this.$emit('setColor', this.localSelectedColor); + }, + handleDropdownHide() { + this.$emit('closeDropdown'); + if (!isDropdownVariantSidebar(this.variant)) { + this.setColor(); + } + this.$refs.dropdown.hide(); + }, + }, +}; +</script> + +<template> + <gl-dropdown ref="dropdown" :text="buttonText" class="gl-w-full" @hide="handleDropdownHide"> + <template #header> + <dropdown-header + ref="header" + :dropdown-title="dropdownTitle" + @closeDropdown="handleDropdownHide" + /> + </template> + <template #default> + <dropdown-contents-color-view + v-model="localSelectedColor" + @closeDropdown="handleDropdownHide" + /> + </template> + </gl-dropdown> +</template> diff --git a/app/assets/javascripts/vue_shared/components/color_select_dropdown/dropdown_contents_color_view.vue b/app/assets/javascripts/vue_shared/components/color_select_dropdown/dropdown_contents_color_view.vue new file mode 100644 index 00000000000..62f4cf59c14 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/color_select_dropdown/dropdown_contents_color_view.vue @@ -0,0 +1,53 @@ +<script> +import { GlDropdownForm, GlDropdownItem } from '@gitlab/ui'; +import ColorItem from './color_item.vue'; +import { ISSUABLE_COLORS } from './constants'; + +export default { + components: { + GlDropdownForm, + GlDropdownItem, + ColorItem, + }, + model: { + prop: 'selectedColor', + }, + props: { + selectedColor: { + type: Object, + required: true, + }, + }, + data() { + return { + colors: ISSUABLE_COLORS, + }; + }, + methods: { + isColorSelected(color) { + return this.selectedColor.color === color.color; + }, + handleColorClick(color) { + this.$emit('input', color); + this.$emit('closeDropdown', this.selectedColor); + }, + }, +}; +</script> + +<template> + <gl-dropdown-form> + <div> + <gl-dropdown-item + v-for="color in colors" + :key="color.color" + :is-checked="isColorSelected(color)" + :is-check-centered="true" + :is-check-item="true" + @click.native.capture.stop="handleColorClick(color)" + > + <color-item :color="color.color" :title="color.title" /> + </gl-dropdown-item> + </div> + </gl-dropdown-form> +</template> diff --git a/app/assets/javascripts/vue_shared/components/color_select_dropdown/dropdown_header.vue b/app/assets/javascripts/vue_shared/components/color_select_dropdown/dropdown_header.vue new file mode 100644 index 00000000000..a32b1570f5f --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/color_select_dropdown/dropdown_header.vue @@ -0,0 +1,31 @@ +<script> +import { GlButton } from '@gitlab/ui'; + +export default { + components: { + GlButton, + }, + props: { + dropdownTitle: { + type: String, + required: true, + }, + }, +}; +</script> + +<template> + <div> + <div class="dropdown-title gl-display-flex gl-align-items-center gl-pt-0 gl-pb-3!"> + <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" + @click="$emit('closeDropdown')" + /> + </div> + </div> +</template> diff --git a/app/assets/javascripts/vue_shared/components/color_select_dropdown/dropdown_value.vue b/app/assets/javascripts/vue_shared/components/color_select_dropdown/dropdown_value.vue new file mode 100644 index 00000000000..4cba66eefd2 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/color_select_dropdown/dropdown_value.vue @@ -0,0 +1,43 @@ +<script> +import { GlIcon, GlTooltipDirective } from '@gitlab/ui'; +import { COLOR_WIDGET_COLOR } from './constants'; +import ColorItem from './color_item.vue'; + +export default { + i18n: { + dropdownTitle: COLOR_WIDGET_COLOR, + }, + directives: { + GlTooltip: GlTooltipDirective, + }, + components: { + GlIcon, + ColorItem, + }, + props: { + selectedColor: { + type: Object, + required: true, + }, + }, +}; +</script> + +<template> + <div class="value js-value"> + <div + v-gl-tooltip.left.viewport + :title="$options.i18n.dropdownTitle" + class="sidebar-collapsed-icon" + > + <gl-icon name="appearance" /> + <color-item + :color="selectedColor.color" + :title="selectedColor.title" + class="gl-font-base gl-line-height-24" + /> + </div> + + <color-item class="hide-collapsed" :color="selectedColor.color" :title="selectedColor.title" /> + </div> +</template> diff --git a/app/assets/javascripts/vue_shared/components/color_select_dropdown/graphql/epic_color.query.graphql b/app/assets/javascripts/vue_shared/components/color_select_dropdown/graphql/epic_color.query.graphql new file mode 100644 index 00000000000..959e0f8c1a5 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/color_select_dropdown/graphql/epic_color.query.graphql @@ -0,0 +1,9 @@ +query epicColor($fullPath: ID!, $iid: ID) { + workspace: group(fullPath: $fullPath) { + id + issuable: epic(iid: $iid) { + id + color + } + } +} diff --git a/app/assets/javascripts/vue_shared/components/color_select_dropdown/graphql/epic_update_color.mutation.graphql b/app/assets/javascripts/vue_shared/components/color_select_dropdown/graphql/epic_update_color.mutation.graphql new file mode 100644 index 00000000000..2975b42253f --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/color_select_dropdown/graphql/epic_update_color.mutation.graphql @@ -0,0 +1,9 @@ +mutation updateEpicColor($input: UpdateEpicInput!) { + updateIssuableColor: updateEpic(input: $input) { + issuable: epic { + id + color + } + errors + } +} diff --git a/app/assets/javascripts/vue_shared/components/color_select_dropdown/utils.js b/app/assets/javascripts/vue_shared/components/color_select_dropdown/utils.js new file mode 100644 index 00000000000..46196e793b3 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/color_select_dropdown/utils.js @@ -0,0 +1,15 @@ +import { DROPDOWN_VARIANT } from './constants'; + +/** + * Returns boolean representing whether dropdown variant + * is `sidebar` + * @param {string} variant + */ +export const isDropdownVariantSidebar = (variant) => variant === DROPDOWN_VARIANT.Sidebar; + +/** + * Returns boolean representing whether dropdown variant + * is `embedded` + * @param {string} variant + */ +export const isDropdownVariantEmbedded = (variant) => variant === DROPDOWN_VARIANT.Embedded; |