diff options
Diffstat (limited to 'app/assets/javascripts/sidebar/components/labels/labels_select_vue/store')
6 files changed, 299 insertions, 0 deletions
diff --git a/app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/actions.js b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/actions.js new file mode 100644 index 00000000000..2dab97826b9 --- /dev/null +++ b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/actions.js @@ -0,0 +1,69 @@ +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/sidebar/components/labels/labels_select_vue/store/getters.js b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/getters.js new file mode 100644 index 00000000000..ef3eedd9bb2 --- /dev/null +++ b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/getters.js @@ -0,0 +1,53 @@ +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/sidebar/components/labels/labels_select_vue/store/index.js b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/index.js new file mode 100644 index 00000000000..5f61cb732c8 --- /dev/null +++ b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/index.js @@ -0,0 +1,12 @@ +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/sidebar/components/labels/labels_select_vue/store/mutation_types.js b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/mutation_types.js new file mode 100644 index 00000000000..f26e36031f4 --- /dev/null +++ b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/mutation_types.js @@ -0,0 +1,22 @@ +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/sidebar/components/labels/labels_select_vue/store/mutations.js b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/mutations.js new file mode 100644 index 00000000000..c85d9befcbb --- /dev/null +++ b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/mutations.js @@ -0,0 +1,113 @@ +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/sidebar/components/labels/labels_select_vue/store/state.js b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/state.js new file mode 100644 index 00000000000..0185d5f88e1 --- /dev/null +++ b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/state.js @@ -0,0 +1,30 @@ +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, +}); |