diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-05 18:10:05 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-05 18:10:05 +0300 |
commit | cf05fd7f3956f0b1a17caf313e89bb7b3315d947 (patch) | |
tree | 8d847ad538180a03a6a25e7ee81605d2f86358d5 /app/assets | |
parent | 023e050d82ed11d9060ce5bdaec99c3871b98164 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets')
4 files changed, 240 insertions, 3 deletions
diff --git a/app/assets/javascripts/boards/components/board_card.vue b/app/assets/javascripts/boards/components/board_card.vue index 3e9c663a036..2821b799cef 100644 --- a/app/assets/javascripts/boards/components/board_card.vue +++ b/app/assets/javascripts/boards/components/board_card.vue @@ -1,5 +1,5 @@ <script> -import { mapActions, mapGetters, mapState } from 'vuex'; +import { mapActions, mapState } from 'vuex'; import BoardCardInner from './board_card_inner.vue'; export default { @@ -31,7 +31,6 @@ export default { }, computed: { ...mapState(['selectedBoardItems', 'activeId']), - ...mapGetters(['isSwimlanesOn']), isActive() { return this.item.id === this.activeId; }, diff --git a/app/assets/javascripts/lib/utils/recurrence.js b/app/assets/javascripts/lib/utils/recurrence.js new file mode 100644 index 00000000000..b9afb939090 --- /dev/null +++ b/app/assets/javascripts/lib/utils/recurrence.js @@ -0,0 +1,154 @@ +import { uuids } from '../../diffs/utils/uuids'; + +/** + * @module recurrence + */ + +const instances = {}; + +/** + * Create a new unique {@link module:recurrence~RecurInstance|RecurInstance} + * @returns {module:recurrence.RecurInstance} The newly created {@link module:recurrence~RecurInstance|RecurInstance} + */ +export function create() { + const id = uuids()[0]; + let handlers = {}; + let count = 0; + + /** + * @namespace RecurInstance + * @description A RecurInstance tracks the count of any occurrence as registered by calls to <code>occur</code>. + * <br /><br /> + * It maintains an internal counter and a registry of handlers that can be arbitrarily assigned by a user. + * <br /><br /> + * While a RecurInstance isn't specific to any particular use-case, it may be useful for: + * <br /> + * <ul> + * <li>Tracking repeated errors across multiple - but not linked - network requests</li> + * <li>Tracking repeated user interactions (e.g. multiple clicks)</li> + * </ul> + * @summary A closure to track repeated occurrences of any arbitrary event. + * */ + const instance = { + /** + * @type {module:uuids~UUIDv4} + * @description A randomly generated {@link module:uuids~UUIDv4|UUID} for this particular recurrence instance + * @memberof module:recurrence~RecurInstance + * @readonly + * @inner + */ + get id() { + return id; + }, + /** + * @type {Number} + * @description The number of times this particular instance of recurrence has been triggered + * @memberof module:recurrence~RecurInstance + * @readonly + * @inner + */ + get count() { + return count; + }, + /** + * @type {Object} + * @description The handlers assigned to this recurrence tracker + * @example + * myRecurrence.handle( 4, () => console.log( "four" ) ); + * console.log( myRecurrence.handlers ); // {"4": () => console.log( "four" )} + * @memberof module:recurrence~RecurInstance + * @readonly + * @inner + */ + get handlers() { + return handlers; + }, + /** + * @type {Boolean} + * @description Delete any internal reference to the instance. + * <br /> + * Keep in mind that this will only attempt to remove the <strong>internal</strong> reference. + * <br /> + * If your code maintains a reference to the instance, the regular garbage collector will not free the memory. + * @memberof module:recurrence~RecurInstance + * @inner + */ + free() { + return delete instances[id]; + }, + /** + * @description Register a handler to be called when this occurrence is seen <code>onCount</code> number of times. + * @param {Number} onCount - The number of times the occurrence has been seen to respond to + * @param {Function} behavior - A callback function to run when the occurrence has been seen <code>onCount</code> times + * @memberof module:recurrence~RecurInstance + * @inner + */ + handle(onCount, behavior) { + if (onCount && behavior) { + handlers[onCount] = behavior; + } + }, + /** + * @description Remove the behavior callback handler that would be run when the occurrence is seen <code>onCount</code> times + * @param {Number} onCount - The count identifier for which to eject the callback handler + * @memberof module:recurrence~RecurInstance + * @inner + */ + eject(onCount) { + if (onCount) { + delete handlers[onCount]; + } + }, + /** + * @description Register that this occurrence has been seen and trigger any appropriate handlers + * @memberof module:recurrence~RecurInstance + * @inner + */ + occur() { + count += 1; + + if (typeof handlers[count] === 'function') { + handlers[count](count); + } + }, + /** + * @description Reset this recurrence instance without destroying it entirely + * @param {Object} [options] + * @param {Boolean} [options.currentCount = true] - Whether to reset the count + * @param {Boolean} [options.handlersList = false] - Whether to reset the list of attached handlers back to an empty state + * @memberof module:recurrence~RecurInstance + * @inner + */ + reset({ currentCount = true, handlersList = false } = {}) { + if (currentCount) { + count = 0; + } + + if (handlersList) { + handlers = {}; + } + }, + }; + + instances[id] = instance; + + return instance; +} + +/** + * Retrieve a stored {@link module:recurrence~RecurInstance|RecurInstance} by {@link module:uuids~UUIDv4|UUID} + * @param {module:uuids~UUIDv4} id - The {@link module:uuids~UUIDv4|UUID} of a previously created {@link module:recurrence~RecurInstance|RecurInstance} + * @returns {(module:recurrence~RecurInstance|undefined)} The {@link module:recurrence~RecurInstance|RecurInstance}, or undefined if the UUID doesn't refer to a known Instance + */ +export function recall(id) { + return instances[id]; +} + +/** + * Release the memory space for a given {@link module:recurrence~RecurInstance|RecurInstance} by {@link module:uuids~UUIDv4|UUID} + * @param {module:uuids~UUIDv4} id - The {@link module:uuids~UUIDv4|UUID} of a previously created {@link module:recurrence~RecurInstance|RecurInstance} + * @returns {Boolean} Whether the reference to the stored {@link module:recurrence~RecurInstance|RecurInstance} was released + */ +export function free(id) { + return recall(id)?.free() || false; +} diff --git a/app/assets/javascripts/pipeline_editor/components/drawer/pipeline_editor_drawer.vue b/app/assets/javascripts/pipeline_editor/components/drawer/pipeline_editor_drawer.vue new file mode 100644 index 00000000000..ef5be8abf9a --- /dev/null +++ b/app/assets/javascripts/pipeline_editor/components/drawer/pipeline_editor_drawer.vue @@ -0,0 +1,76 @@ +<script> +import { GlButton, GlIcon } from '@gitlab/ui'; +import { __ } from '~/locale'; + +export default { + width: { + expanded: '482px', + collapsed: '58px', + }, + i18n: { + toggleTxt: __('Collapse'), + }, + components: { + GlButton, + GlIcon, + }, + data() { + return { + isExpanded: false, + topPosition: 0, + }; + }, + computed: { + buttonIconName() { + return this.isExpanded ? 'chevron-double-lg-right' : 'chevron-double-lg-left'; + }, + buttonClass() { + return this.isExpanded ? 'gl-justify-content-end!' : ''; + }, + rootStyle() { + const { expanded, collapsed } = this.$options.width; + const top = this.topPosition; + const style = { top: `${top}px` }; + + return this.isExpanded ? { ...style, width: expanded } : { ...style, width: collapsed }; + }, + }, + mounted() { + this.setTopPosition(); + }, + methods: { + setTopPosition() { + const navbarEl = document.querySelector('.js-navbar'); + + if (navbarEl) { + this.topPosition = navbarEl.getBoundingClientRect().bottom; + } + }, + toggleDrawer() { + this.isExpanded = !this.isExpanded; + }, + }, +}; +</script> +<template> + <aside + aria-live="polite" + class="gl-fixed gl-right-0 gl-h-full gl-bg-gray-10 gl-transition-medium gl-border-l-solid gl-border-1 gl-border-gray-100" + :style="rootStyle" + > + <gl-button + category="tertiary" + class="gl-w-full gl-h-9 gl-rounded-0! gl-border-none! gl-border-b-solid! gl-border-1! gl-border-gray-100 gl-text-decoration-none! gl-outline-0! gl-display-flex" + :class="buttonClass" + :title="__('Toggle sidebar')" + data-testid="toggleBtn" + @click="toggleDrawer" + > + <span v-if="isExpanded" class="gl-text-gray-500 gl-mr-3" data-testid="collapse-text">{{ + __('Collapse') + }}</span> + <gl-icon data-testid="toggle-icon" :name="buttonIconName" /> + </gl-button> + <div v-if="isExpanded" class="gl-p-5" data-testid="drawer-content"></div> + </aside> +</template> diff --git a/app/assets/javascripts/pipeline_editor/pipeline_editor_home.vue b/app/assets/javascripts/pipeline_editor/pipeline_editor_home.vue index adba55f9f4b..dfe9c82b912 100644 --- a/app/assets/javascripts/pipeline_editor/pipeline_editor_home.vue +++ b/app/assets/javascripts/pipeline_editor/pipeline_editor_home.vue @@ -1,5 +1,7 @@ <script> +import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import CommitSection from './components/commit/commit_section.vue'; +import PipelineEditorDrawer from './components/drawer/pipeline_editor_drawer.vue'; import PipelineEditorFileNav from './components/file_nav/pipeline_editor_file_nav.vue'; import PipelineEditorHeader from './components/header/pipeline_editor_header.vue'; import PipelineEditorTabs from './components/pipeline_editor_tabs.vue'; @@ -8,10 +10,12 @@ import { TABS_WITH_COMMIT_FORM, CREATE_TAB } from './constants'; export default { components: { CommitSection, + PipelineEditorDrawer, PipelineEditorFileNav, PipelineEditorHeader, PipelineEditorTabs, }, + mixins: [glFeatureFlagMixin()], props: { ciConfigData: { type: Object, @@ -35,6 +39,9 @@ export default { showCommitForm() { return TABS_WITH_COMMIT_FORM.includes(this.currentTab); }, + showPipelineDrawer() { + return this.glFeatures.pipelineEditorDrawer; + }, }, methods: { setCurrentTab(tabName) { @@ -45,7 +52,7 @@ export default { </script> <template> - <div> + <div class="gl-pr-9 gl-transition-medium gl-w-full"> <pipeline-editor-file-nav v-on="$listeners" /> <pipeline-editor-header :ci-config-data="ciConfigData" @@ -58,5 +65,6 @@ export default { @set-current-tab="setCurrentTab" /> <commit-section v-if="showCommitForm" :ci-file-content="ciFileContent" v-on="$listeners" /> + <pipeline-editor-drawer v-if="showPipelineDrawer" /> </div> </template> |