diff options
Diffstat (limited to 'app/assets/javascripts/ide')
37 files changed, 298 insertions, 291 deletions
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/form.vue b/app/assets/javascripts/ide/components/commit_sidebar/form.vue index f36fe87ccfa..9d2deb1d4d0 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/form.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/form.vue @@ -1,8 +1,7 @@ <script> import { mapState, mapActions, mapGetters } from 'vuex'; -import { GlModal, GlSafeHtmlDirective } from '@gitlab/ui'; +import { GlModal, GlSafeHtmlDirective, GlButton } from '@gitlab/ui'; import { n__, __ } from '~/locale'; -import LoadingButton from '~/vue_shared/components/loading_button.vue'; import CommitMessageField from './message_field.vue'; import Actions from './actions.vue'; import SuccessMessage from './success_message.vue'; @@ -12,10 +11,10 @@ import { createUnexpectedCommitError } from '../../lib/errors'; export default { components: { Actions, - LoadingButton, CommitMessageField, SuccessMessage, GlModal, + GlButton, }, directives: { SafeHtml: GlSafeHtmlDirective, @@ -156,12 +155,16 @@ export default { /> <div class="clearfix gl-mt-5"> <actions /> - <loading-button + <gl-button :loading="submitCommitLoading" - :label="commitButtonText" - container-class="btn btn-success btn-sm float-left qa-commit-button" + class="float-left qa-commit-button" + size="small" + category="primary" + variant="success" @click="commit" - /> + > + {{ __('Commit') }} + </gl-button> <button v-if="!discardDraftButtonDisabled" type="button" @@ -170,14 +173,17 @@ export default { > {{ __('Discard draft') }} </button> - <button + <gl-button v-else type="button" - class="btn btn-default btn-sm float-right" + class="float-right" + category="secondary" + variant="default" + size="small" @click="toggleIsCompact" > {{ __('Collapse') }} - </button> + </gl-button> </div> <gl-modal ref="commitErrorModal" diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list.vue b/app/assets/javascripts/ide/components/commit_sidebar/list.vue index 609ce287d3f..729ff7c74ec 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/list.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/list.vue @@ -1,8 +1,7 @@ <script> import { mapActions } from 'vuex'; -import { GlModal, GlIcon } from '@gitlab/ui'; +import { GlModal, GlIcon, GlTooltipDirective } from '@gitlab/ui'; import { __, sprintf } from '~/locale'; -import tooltip from '~/vue_shared/directives/tooltip'; import ListItem from './list_item.vue'; export default { @@ -12,17 +11,13 @@ export default { GlModal, }, directives: { - tooltip, + GlTooltip: GlTooltipDirective, }, props: { fileList: { type: Array, required: true, }, - iconName: { - type: String, - required: true, - }, stagedList: { type: Boolean, required: false, @@ -73,12 +68,11 @@ export default { <div class="ide-commit-list-container"> <header class="multi-file-commit-panel-header d-flex mb-0"> <div class="d-flex align-items-center flex-fill"> - <gl-icon v-once :name="iconName" :size="18" class="gl-mr-3" /> <strong> {{ titleText }} </strong> <div class="d-flex ml-auto"> <button v-if="!stagedList" - v-tooltip + v-gl-tooltip :title="__('Discard all changes')" :aria-label="__('Discard all changes')" :disabled="!filesLength" diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list_collapsed.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_collapsed.vue deleted file mode 100644 index 4821b8389ff..00000000000 --- a/app/assets/javascripts/ide/components/commit_sidebar/list_collapsed.vue +++ /dev/null @@ -1,103 +0,0 @@ -<script> -import { GlIcon } from '@gitlab/ui'; -import tooltip from '~/vue_shared/directives/tooltip'; -import { sprintf, n__, __ } from '~/locale'; - -export default { - components: { - GlIcon, - }, - directives: { - tooltip, - }, - props: { - files: { - type: Array, - required: true, - }, - iconName: { - type: String, - required: true, - }, - title: { - type: String, - required: true, - }, - }, - computed: { - addedFilesLength() { - return this.files.filter(f => f.tempFile).length; - }, - modifiedFilesLength() { - return this.files.filter(f => !f.tempFile).length; - }, - addedFilesIconClass() { - return this.addedFilesLength ? 'multi-file-addition' : ''; - }, - modifiedFilesClass() { - return this.modifiedFilesLength ? 'multi-file-modified' : ''; - }, - additionsTooltip() { - return sprintf( - n__('1 %{type} addition', '%{count} %{type} additions', this.addedFilesLength), - { - type: this.title.toLowerCase(), - count: this.addedFilesLength, - }, - ); - }, - modifiedTooltip() { - return sprintf( - n__('1 %{type} modification', '%{count} %{type} modifications', this.modifiedFilesLength), - { - type: this.title.toLowerCase(), - count: this.modifiedFilesLength, - }, - ); - }, - titleTooltip() { - return sprintf(__('%{title} changes'), { title: this.title }); - }, - additionIconName() { - return this.title.toLowerCase() === 'staged' ? 'file-addition-solid' : 'file-addition'; - }, - modifiedIconName() { - return this.title.toLowerCase() === 'staged' ? 'file-modified-solid' : 'file-modified'; - }, - }, -}; -</script> - -<template> - <div class="multi-file-commit-list-collapsed text-center"> - <div - v-tooltip - :title="titleTooltip" - data-container="body" - data-placement="left" - class="gl-mb-5" - > - <gl-icon v-once :name="iconName" :size="18" /> - </div> - <div - v-tooltip - :title="additionsTooltip" - data-container="body" - data-placement="left" - class="gl-mb-3" - > - <gl-icon :name="additionIconName" :size="18" :class="addedFilesIconClass" /> - </div> - {{ addedFilesLength }} - <div - v-tooltip - :title="modifiedTooltip" - data-container="body" - data-placement="left" - class="gl-mt-3 gl-mb-3" - > - <gl-icon :name="modifiedIconName" :size="18" :class="modifiedFilesClass" /> - </div> - {{ modifiedFilesLength }} - </div> -</template> diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue index a0d6cf3c42d..123e0aba959 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue @@ -1,7 +1,6 @@ <script> import { mapActions } from 'vuex'; -import { GlIcon } from '@gitlab/ui'; -import tooltip from '~/vue_shared/directives/tooltip'; +import { GlIcon, GlTooltipDirective } from '@gitlab/ui'; import FileIcon from '~/vue_shared/components/file_icon.vue'; import { viewerTypes } from '../../constants'; import getCommitIconMap from '../../commit_icon'; @@ -12,7 +11,7 @@ export default { FileIcon, }, directives: { - tooltip, + GlTooltip: GlTooltipDirective, }, props: { file: { @@ -77,7 +76,7 @@ export default { <template> <div class="multi-file-commit-list-item position-relative"> <div - v-tooltip + v-gl-tooltip :title="tooltipTitle" :class="{ 'is-active': isActive, diff --git a/app/assets/javascripts/ide/components/file_row_extra.vue b/app/assets/javascripts/ide/components/file_row_extra.vue index 48ab58e1cb7..fb0d00dc6a1 100644 --- a/app/assets/javascripts/ide/components/file_row_extra.vue +++ b/app/assets/javascripts/ide/components/file_row_extra.vue @@ -1,8 +1,7 @@ <script> import { mapGetters } from 'vuex'; -import { GlIcon } from '@gitlab/ui'; +import { GlIcon, GlTooltipDirective } from '@gitlab/ui'; import { n__ } from '~/locale'; -import tooltip from '~/vue_shared/directives/tooltip'; import ChangedFileIcon from '~/vue_shared/components/changed_file_icon.vue'; import NewDropdown from './new_dropdown/index.vue'; import MrFileIcon from './mr_file_icon.vue'; @@ -10,7 +9,7 @@ import MrFileIcon from './mr_file_icon.vue'; export default { name: 'FileRowExtra', directives: { - tooltip, + GlTooltip: GlTooltipDirective, }, components: { GlIcon, @@ -70,7 +69,7 @@ export default { <span v-if="showTreeChangesCount" class="ide-tree-changes"> {{ changesCount }} <gl-icon - v-tooltip + v-gl-tooltip.left.viewport :title="folderChangesTooltip" :size="12" data-container="body" diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue index 8f23856fd6c..e1d2895831a 100644 --- a/app/assets/javascripts/ide/components/ide.vue +++ b/app/assets/javascripts/ide/components/ide.vue @@ -1,6 +1,5 @@ <script> import { mapActions, mapGetters, mapState } from 'vuex'; -import { GlButton, GlLoadingIcon } from '@gitlab/ui'; import { __ } from '~/locale'; import { WEBIDE_MARK_APP_START, @@ -10,19 +9,12 @@ import { WEBIDE_MEASURE_TREE_FROM_REQUEST, WEBIDE_MEASURE_FILE_FROM_REQUEST, WEBIDE_MEASURE_FILE_AFTER_INTERACTION, -} from '~/performance_constants'; -import { performanceMarkAndMeasure } from '~/performance_utils'; +} from '~/performance/constants'; +import { performanceMarkAndMeasure } from '~/performance/utils'; import { modalTypes } from '../constants'; import eventHub from '../eventhub'; -import FindFile from '~/vue_shared/components/file_finder/index.vue'; -import NewModal from './new_dropdown/modal.vue'; import IdeSidebar from './ide_side_bar.vue'; -import RepoTabs from './repo_tabs.vue'; -import IdeStatusBar from './ide_status_bar.vue'; import RepoEditor from './repo_editor.vue'; -import RightPane from './panes/right.vue'; -import ErrorMessage from './error_message.vue'; -import CommitEditorHeader from './commit_sidebar/editor_header.vue'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { measurePerformance } from '../utils'; @@ -43,19 +35,24 @@ eventHub.$on(WEBIDE_MEASURE_FILE_AFTER_INTERACTION, () => export default { components: { - NewModal, IdeSidebar, - RepoTabs, - IdeStatusBar, RepoEditor, - FindFile, - ErrorMessage, - CommitEditorHeader, - GlButton, - GlLoadingIcon, - RightPane, + 'error-message': () => import('./error_message.vue'), + 'gl-button': () => import('@gitlab/ui/src/components/base/button/button.vue'), + 'gl-loading-icon': () => import('@gitlab/ui/src/components/base/loading_icon/loading_icon.vue'), + 'commit-editor-header': () => import('./commit_sidebar/editor_header.vue'), + 'repo-tabs': () => import('./repo_tabs.vue'), + 'ide-status-bar': () => import('./ide_status_bar.vue'), + 'find-file': () => import('~/vue_shared/components/file_finder/index.vue'), + 'right-pane': () => import('./panes/right.vue'), + 'new-modal': () => import('./new_dropdown/modal.vue'), }, mixins: [glFeatureFlagsMixin()], + data() { + return { + loadDeferred: false, + }; + }, computed: { ...mapState([ 'openFiles', @@ -107,6 +104,9 @@ export default { createNewFile() { this.$refs.newModal.open(modalTypes.blob); }, + loadDeferredComponents() { + this.loadDeferred = true; + }, }, }; </script> @@ -118,19 +118,23 @@ export default { > <error-message v-if="errorMessage" :message="errorMessage" /> <div class="ide-view flex-grow d-flex"> - <find-file - v-show="fileFindVisible" - :files="allBlobs" - :visible="fileFindVisible" - :loading="loading" - @toggle="toggleFileFinder" - @click="openFile" - /> - <ide-sidebar /> + <template v-if="loadDeferred"> + <find-file + v-show="fileFindVisible" + :files="allBlobs" + :visible="fileFindVisible" + :loading="loading" + @toggle="toggleFileFinder" + @click="openFile" + /> + </template> + <ide-sidebar @tree-ready="loadDeferredComponents" /> <div class="multi-file-edit-pane"> <template v-if="activeFile"> - <commit-editor-header v-if="isCommitModeActive" :active-file="activeFile" /> - <repo-tabs v-else :active-file="activeFile" :files="openFiles" :viewer="viewer" /> + <template v-if="loadDeferred"> + <commit-editor-header v-if="isCommitModeActive" :active-file="activeFile" /> + <repo-tabs v-else :active-file="activeFile" :files="openFiles" :viewer="viewer" /> + </template> <repo-editor :file="activeFile" class="multi-file-edit-pane-content" /> </template> <template v-else> @@ -177,9 +181,13 @@ export default { </div> </template> </div> - <right-pane v-if="currentProjectId" /> + <template v-if="loadDeferred"> + <right-pane v-if="currentProjectId" /> + </template> </div> - <ide-status-bar /> - <new-modal ref="newModal" /> + <template v-if="loadDeferred"> + <ide-status-bar /> + <new-modal ref="newModal" /> + </template> </article> </template> diff --git a/app/assets/javascripts/ide/components/ide_side_bar.vue b/app/assets/javascripts/ide/components/ide_side_bar.vue index 53dfc133fc8..99215d6c3f1 100644 --- a/app/assets/javascripts/ide/components/ide_side_bar.vue +++ b/app/assets/javascripts/ide/components/ide_side_bar.vue @@ -4,21 +4,19 @@ import { GlDeprecatedSkeletonLoading as GlSkeletonLoading } from '@gitlab/ui'; import IdeTree from './ide_tree.vue'; import ResizablePanel from './resizable_panel.vue'; import ActivityBar from './activity_bar.vue'; -import RepoCommitSection from './repo_commit_section.vue'; import CommitForm from './commit_sidebar/form.vue'; -import IdeReview from './ide_review.vue'; import IdeProjectHeader from './ide_project_header.vue'; -import { SIDEBAR_INIT_WIDTH } from '../constants'; +import { SIDEBAR_INIT_WIDTH, leftSidebarViews } from '../constants'; export default { components: { GlSkeletonLoading, ResizablePanel, ActivityBar, - RepoCommitSection, IdeTree, + [leftSidebarViews.review.name]: () => import('./ide_review.vue'), + [leftSidebarViews.commit.name]: () => import('./repo_commit_section.vue'), CommitForm, - IdeReview, IdeProjectHeader, }, computed: { @@ -49,7 +47,7 @@ export default { <div class="multi-file-commit-panel-inner" data-testid="ide-side-bar-inner"> <div class="multi-file-commit-panel-inner-content"> <keep-alive> - <component :is="currentActivityView" /> + <component :is="currentActivityView" @tree-ready="$emit('tree-ready')" /> </keep-alive> </div> <commit-form /> diff --git a/app/assets/javascripts/ide/components/ide_status_list.vue b/app/assets/javascripts/ide/components/ide_status_list.vue index caa122f6ed2..aa61c0d9b5e 100644 --- a/app/assets/javascripts/ide/components/ide_status_list.vue +++ b/app/assets/javascripts/ide/components/ide_status_list.vue @@ -14,6 +14,7 @@ export default { }, computed: { ...mapGetters(['activeFile']), + ...mapGetters('editor', ['activeFileEditor']), activeFileEOL() { return getFileEOL(this.activeFile.content); }, @@ -33,8 +34,10 @@ export default { </gl-link> </div> <div>{{ activeFileEOL }}</div> - <div v-if="activeFileIsText">{{ activeFile.editorRow }}:{{ activeFile.editorColumn }}</div> - <div>{{ activeFile.fileLanguage }}</div> + <div v-if="activeFileIsText"> + {{ activeFileEditor.editorRow }}:{{ activeFileEditor.editorColumn }} + </div> + <div>{{ activeFileEditor.fileLanguage }}</div> </template> <terminal-sync-status-safe /> </div> diff --git a/app/assets/javascripts/ide/components/ide_tree.vue b/app/assets/javascripts/ide/components/ide_tree.vue index 56fcb6c2600..e563de6659a 100644 --- a/app/assets/javascripts/ide/components/ide_tree.vue +++ b/app/assets/javascripts/ide/components/ide_tree.vue @@ -51,7 +51,7 @@ export default { </script> <template> - <ide-tree-list> + <ide-tree-list @tree-ready="$emit('tree-ready')"> <template #header> {{ __('Edit') }} <div class="ide-tree-actions ml-auto d-flex" data-testid="ide-root-actions"> diff --git a/app/assets/javascripts/ide/components/ide_tree_list.vue b/app/assets/javascripts/ide/components/ide_tree_list.vue index dd226f07fb0..e7e94f5b5da 100644 --- a/app/assets/javascripts/ide/components/ide_tree_list.vue +++ b/app/assets/javascripts/ide/components/ide_tree_list.vue @@ -6,8 +6,8 @@ import { WEBIDE_MARK_TREE_START, WEBIDE_MEASURE_TREE_FROM_REQUEST, WEBIDE_MARK_FILE_CLICKED, -} from '~/performance_constants'; -import { performanceMarkAndMeasure } from '~/performance_utils'; +} from '~/performance/constants'; +import { performanceMarkAndMeasure } from '~/performance/utils'; import eventHub from '../eventhub'; import IdeFileRow from './ide_file_row.vue'; import NavDropdown from './nav_dropdown.vue'; @@ -32,6 +32,13 @@ export default { return !this.currentTree || this.currentTree.loading; }, }, + watch: { + showLoading(newVal) { + if (!newVal) { + this.$emit('tree-ready'); + } + }, + }, beforeCreate() { performanceMarkAndMeasure({ mark: WEBIDE_MARK_TREE_START }); }, diff --git a/app/assets/javascripts/ide/components/jobs/detail.vue b/app/assets/javascripts/ide/components/jobs/detail.vue index a5ae8bbfe9a..d65304034c2 100644 --- a/app/assets/javascripts/ide/components/jobs/detail.vue +++ b/app/assets/javascripts/ide/components/jobs/detail.vue @@ -2,7 +2,7 @@ /* eslint-disable vue/no-v-html */ import { mapActions, mapState } from 'vuex'; import { throttle } from 'lodash'; -import { GlIcon, GlTooltipDirective } from '@gitlab/ui'; +import { GlTooltipDirective, GlButton, GlIcon } from '@gitlab/ui'; import { __ } from '../../../locale'; import ScrollButton from './detail/scroll_button.vue'; import JobDescription from './detail/description.vue'; @@ -17,6 +17,7 @@ export default { GlTooltip: GlTooltipDirective, }, components: { + GlButton, GlIcon, ScrollButton, JobDescription, @@ -75,9 +76,9 @@ export default { <template> <div class="ide-pipeline build-page d-flex flex-column flex-fill"> <header class="ide-job-header d-flex align-items-center"> - <button class="btn btn-default btn-sm d-flex" @click="setDetailJob(null)"> - <gl-icon name="chevron-left" /> {{ __('View jobs') }} - </button> + <gl-button category="secondary" icon="chevron-left" size="small" @click="setDetailJob(null)"> + {{ __('View jobs') }} + </gl-button> </header> <div class="top-bar d-flex border-left-0 mr-3"> <job-description :job="detailJob" /> diff --git a/app/assets/javascripts/ide/components/jobs/item.vue b/app/assets/javascripts/ide/components/jobs/item.vue index db3630bc1d1..f84315b63d2 100644 --- a/app/assets/javascripts/ide/components/jobs/item.vue +++ b/app/assets/javascripts/ide/components/jobs/item.vue @@ -1,9 +1,11 @@ <script> +import { GlButton } from '@gitlab/ui'; import JobDescription from './detail/description.vue'; export default { components: { JobDescription, + GlButton, }, props: { job: { @@ -28,9 +30,9 @@ export default { <div class="ide-job-item"> <job-description :job="job" class="gl-mr-3" /> <div class="ml-auto align-self-center"> - <button v-if="job.started" type="button" class="btn btn-default btn-sm" @click="clickViewLog"> + <gl-button v-if="job.started" category="secondary" size="small" @click="clickViewLog"> {{ __('View log') }} - </button> + </gl-button> </div> </div> </template> diff --git a/app/assets/javascripts/ide/components/mr_file_icon.vue b/app/assets/javascripts/ide/components/mr_file_icon.vue index c8629a869e0..af297753c28 100644 --- a/app/assets/javascripts/ide/components/mr_file_icon.vue +++ b/app/assets/javascripts/ide/components/mr_file_icon.vue @@ -1,20 +1,19 @@ <script> -import { GlIcon } from '@gitlab/ui'; -import tooltip from '~/vue_shared/directives/tooltip'; +import { GlIcon, GlTooltipDirective } from '@gitlab/ui'; export default { components: { GlIcon, }, directives: { - tooltip, + GlTooltip: GlTooltipDirective, }, }; </script> <template> <gl-icon - v-tooltip + v-gl-tooltip :title="__('Part of merge request changes')" :size="12" name="git-merge" diff --git a/app/assets/javascripts/ide/components/new_dropdown/button.vue b/app/assets/javascripts/ide/components/new_dropdown/button.vue index 8ae8f97f237..ce80fbee2e0 100644 --- a/app/assets/javascripts/ide/components/new_dropdown/button.vue +++ b/app/assets/javascripts/ide/components/new_dropdown/button.vue @@ -1,10 +1,9 @@ <script> -import { GlIcon } from '@gitlab/ui'; -import tooltip from '~/vue_shared/directives/tooltip'; +import { GlIcon, GlTooltipDirective } from '@gitlab/ui'; export default { directives: { - tooltip, + GlTooltip: GlTooltipDirective, }, components: { GlIcon, @@ -45,7 +44,7 @@ export default { <template> <button - v-tooltip + v-gl-tooltip :aria-label="label" :title="tooltipTitle" type="button" diff --git a/app/assets/javascripts/ide/components/panes/collapsible_sidebar.vue b/app/assets/javascripts/ide/components/panes/collapsible_sidebar.vue index f1b882d8f29..87019c3b2a5 100644 --- a/app/assets/javascripts/ide/components/panes/collapsible_sidebar.vue +++ b/app/assets/javascripts/ide/components/panes/collapsible_sidebar.vue @@ -1,13 +1,9 @@ <script> import { mapActions, mapState } from 'vuex'; -import tooltip from '~/vue_shared/directives/tooltip'; import IdeSidebarNav from '../ide_sidebar_nav.vue'; export default { name: 'CollapsibleSidebar', - directives: { - tooltip, - }, components: { IdeSidebarNav, }, diff --git a/app/assets/javascripts/ide/components/pipelines/list.vue b/app/assets/javascripts/ide/components/pipelines/list.vue index 91bd64a2c9c..6f15773c9ab 100644 --- a/app/assets/javascripts/ide/components/pipelines/list.vue +++ b/app/assets/javascripts/ide/components/pipelines/list.vue @@ -1,11 +1,16 @@ <script> import { mapActions, mapGetters, mapState } from 'vuex'; import { escape } from 'lodash'; -import { GlLoadingIcon, GlIcon, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui'; +import { + GlLoadingIcon, + GlIcon, + GlSafeHtmlDirective as SafeHtml, + GlTabs, + GlTab, + GlBadge, +} from '@gitlab/ui'; import { sprintf, __ } from '../../../locale'; import CiIcon from '../../../vue_shared/components/ci_icon.vue'; -import Tabs from '../../../vue_shared/components/tabs/tabs'; -import Tab from '../../../vue_shared/components/tabs/tab.vue'; import EmptyState from '../../../pipelines/components/pipelines_list/empty_state.vue'; import JobsList from '../jobs/list.vue'; @@ -15,11 +20,12 @@ export default { components: { GlIcon, CiIcon, - Tabs, - Tab, JobsList, EmptyState, GlLoadingIcon, + GlTabs, + GlTab, + GlBadge, }, directives: { SafeHtml, @@ -88,22 +94,26 @@ export default { <p class="gl-mb-0 break-word">{{ latestPipeline.yamlError }}</p> <p v-safe-html="ciLintText" class="gl-mb-0"></p> </div> - <tabs v-else class="ide-pipeline-list"> - <tab :active="!pipelineFailed"> + <gl-tabs v-else> + <gl-tab :active="!pipelineFailed"> <template #title> {{ __('Jobs') }} - <span v-if="jobsCount" class="badge badge-pill"> {{ jobsCount }} </span> + <gl-badge v-if="jobsCount" size="sm" class="gl-tab-counter-badge">{{ + jobsCount + }}</gl-badge> </template> <jobs-list :loading="isLoadingJobs" :stages="stages" /> - </tab> - <tab :active="pipelineFailed"> + </gl-tab> + <gl-tab :active="pipelineFailed"> <template #title> {{ __('Failed Jobs') }} - <span v-if="failedJobsCount" class="badge badge-pill"> {{ failedJobsCount }} </span> + <gl-badge v-if="failedJobsCount" size="sm" class="gl-tab-counter-badge">{{ + failedJobsCount + }}</gl-badge> </template> <jobs-list :loading="isLoadingJobs" :stages="failedStages" /> - </tab> - </tabs> + </gl-tab> + </gl-tabs> </template> </div> </template> diff --git a/app/assets/javascripts/ide/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue index 92b99b5c731..dfd25feed08 100644 --- a/app/assets/javascripts/ide/components/repo_commit_section.vue +++ b/app/assets/javascripts/ide/components/repo_commit_section.vue @@ -1,6 +1,5 @@ <script> import { mapState, mapActions, mapGetters } from 'vuex'; -import tooltip from '~/vue_shared/directives/tooltip'; import CommitFilesList from './commit_sidebar/list.vue'; import EmptyState from './commit_sidebar/empty_state.vue'; import { stageKeys } from '../constants'; @@ -10,9 +9,6 @@ export default { CommitFilesList, EmptyState, }, - directives: { - tooltip, - }, computed: { ...mapState(['changedFiles', 'stagedFiles', 'lastCommitMsg']), ...mapState('commit', ['commitMessage', 'submitCommitLoading']), @@ -68,7 +64,6 @@ export default { :active-file-key="activeFileKey" :empty-state-text="__('There are no changes')" class="is-first" - icon-name="unstaged" /> </template> <empty-state v-else /> diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue index 56bbb6349cd..c8a825065f1 100644 --- a/app/assets/javascripts/ide/components/repo_editor.vue +++ b/app/assets/javascripts/ide/components/repo_editor.vue @@ -9,8 +9,8 @@ import { WEBIDE_MARK_FILE_START, WEBIDE_MEASURE_FILE_AFTER_INTERACTION, WEBIDE_MEASURE_FILE_FROM_REQUEST, -} from '~/performance_constants'; -import { performanceMarkAndMeasure } from '~/performance_utils'; +} from '~/performance/constants'; +import { performanceMarkAndMeasure } from '~/performance/utils'; import eventHub from '../eventhub'; import { leftSidebarViews, @@ -22,6 +22,7 @@ import Editor from '../lib/editor'; import FileTemplatesBar from './file_templates/bar.vue'; import { __ } from '~/locale'; import { extractMarkdownImagesFromEntries } from '../stores/utils'; +import { getFileEditorOrDefault } from '../stores/modules/editor/utils'; import { getPathParent, readFileAsDataURL, registerSchema, isTextFile } from '../utils'; import { getRulesWithTraversal } from '../lib/editorconfig/parser'; import mapRulesToMonaco from '../lib/editorconfig/rules_mapper'; @@ -49,6 +50,7 @@ export default { ...mapState('rightPane', { rightPaneIsOpen: 'isOpen', }), + ...mapState('editor', ['fileEditors']), ...mapState([ 'viewer', 'panelResizing', @@ -67,6 +69,9 @@ export default { 'getJsonSchemaForPath', ]), ...mapGetters('fileTemplates', ['showFileTemplatesBar']), + fileEditor() { + return getFileEditorOrDefault(this.fileEditors, this.file.path); + }, shouldHideEditor() { return this.file && !this.file.loading && !isTextFile(this.file); }, @@ -80,10 +85,10 @@ export default { return this.shouldHideEditor && this.file.mrChange && this.viewer === viewerTypes.mr; }, isEditorViewMode() { - return this.file.viewMode === FILE_VIEW_MODE_EDITOR; + return this.fileEditor.viewMode === FILE_VIEW_MODE_EDITOR; }, isPreviewViewMode() { - return this.file.viewMode === FILE_VIEW_MODE_PREVIEW; + return this.fileEditor.viewMode === FILE_VIEW_MODE_PREVIEW; }, editTabCSS() { return { @@ -125,8 +130,7 @@ export default { this.initEditor(); if (this.currentActivityView !== leftSidebarViews.edit.name) { - this.setFileViewMode({ - file: this.file, + this.updateEditor({ viewMode: FILE_VIEW_MODE_EDITOR, }); } @@ -134,8 +138,7 @@ export default { }, currentActivityView() { if (this.currentActivityView !== leftSidebarViews.edit.name) { - this.setFileViewMode({ - file: this.file, + this.updateEditor({ viewMode: FILE_VIEW_MODE_EDITOR, }); } @@ -195,13 +198,11 @@ export default { 'getFileData', 'getRawFileData', 'changeFileContent', - 'setFileLanguage', - 'setEditorPosition', - 'setFileViewMode', 'removePendingTab', 'triggerFilesChange', 'addTempImage', ]), + ...mapActions('editor', ['updateFileEditor']), initEditor() { if (this.shouldHideEditor && (this.file.content || this.file.raw)) { return; @@ -284,19 +285,19 @@ export default { // Handle Cursor Position this.editor.onPositionChange((instance, e) => { - this.setEditorPosition({ + this.updateEditor({ editorRow: e.position.lineNumber, editorColumn: e.position.column, }); }); this.editor.setPosition({ - lineNumber: this.file.editorRow, - column: this.file.editorColumn, + lineNumber: this.fileEditor.editorRow, + column: this.fileEditor.editorColumn, }); // Handle File Language - this.setFileLanguage({ + this.updateEditor({ fileLanguage: this.model.language, }); @@ -354,6 +355,16 @@ export default { const schema = this.getJsonSchemaForPath(this.file.path); registerSchema(schema); }, + updateEditor(data) { + // Looks like our model wrapper `.dispose` causes the monaco editor to emit some position changes after + // when disposing. We want to ignore these by only capturing editor changes that happen to the currently active + // file. + if (!this.file.active) { + return; + } + + this.updateFileEditor({ path: this.file.path, data }); + }, }, viewerTypes, FILE_VIEW_MODE_EDITOR, @@ -369,7 +380,7 @@ export default { <a href="javascript:void(0);" role="button" - @click.prevent="setFileViewMode({ file, viewMode: $options.FILE_VIEW_MODE_EDITOR })" + @click.prevent="updateEditor({ viewMode: $options.FILE_VIEW_MODE_EDITOR })" > {{ __('Edit') }} </a> @@ -378,7 +389,7 @@ export default { <a href="javascript:void(0);" role="button" - @click.prevent="setFileViewMode({ file, viewMode: $options.FILE_VIEW_MODE_PREVIEW })" + @click.prevent="updateEditor({ viewMode: $options.FILE_VIEW_MODE_PREVIEW })" >{{ previewMode.previewTitle }}</a > </li> diff --git a/app/assets/javascripts/ide/components/repo_file_status_icon.vue b/app/assets/javascripts/ide/components/repo_file_status_icon.vue index 1402f7aaf39..72c56daf69c 100644 --- a/app/assets/javascripts/ide/components/repo_file_status_icon.vue +++ b/app/assets/javascripts/ide/components/repo_file_status_icon.vue @@ -1,7 +1,6 @@ <script> -import { GlIcon } from '@gitlab/ui'; +import { GlIcon, GlTooltipDirective } from '@gitlab/ui'; import { __, sprintf } from '~/locale'; -import tooltip from '~/vue_shared/directives/tooltip'; import '~/lib/utils/datetime_utility'; export default { @@ -9,7 +8,7 @@ export default { GlIcon, }, directives: { - tooltip, + GlTooltip: GlTooltipDirective, }, props: { file: { @@ -28,7 +27,7 @@ export default { </script> <template> - <span v-if="file.file_lock" v-tooltip :title="lockTooltip" data-container="body"> + <span v-if="file.file_lock" v-gl-tooltip :title="lockTooltip" data-container="body"> <gl-icon name="lock" class="file-status-icon" /> </span> </template> diff --git a/app/assets/javascripts/ide/components/repo_tab.vue b/app/assets/javascripts/ide/components/repo_tab.vue index 60a80a31a8b..e3c41eee15e 100644 --- a/app/assets/javascripts/ide/components/repo_tab.vue +++ b/app/assets/javascripts/ide/components/repo_tab.vue @@ -29,9 +29,9 @@ export default { ...mapGetters(['getUrlForPath']), closeLabel() { if (this.fileHasChanged) { - return sprintf(__(`%{tabname} changed`), { tabname: this.tab.name }); + return sprintf(__('%{tabname} changed'), { tabname: this.tab.name }); } - return sprintf(__(`Close %{tabname}`, { tabname: this.tab.name })); + return sprintf(__('Close %{tabname}'), { tabname: this.tab.name }); }, showChangedIcon() { if (this.tab.pending) return true; diff --git a/app/assets/javascripts/ide/components/terminal/empty_state.vue b/app/assets/javascripts/ide/components/terminal/empty_state.vue index 3668dd24e81..f4dd83b16c7 100644 --- a/app/assets/javascripts/ide/components/terminal/empty_state.vue +++ b/app/assets/javascripts/ide/components/terminal/empty_state.vue @@ -1,10 +1,14 @@ <script> -/* eslint-disable vue/no-v-html */ -import { GlLoadingIcon } from '@gitlab/ui'; +import { GlLoadingIcon, GlButton, GlAlert, GlSafeHtmlDirective } from '@gitlab/ui'; export default { components: { GlLoadingIcon, + GlButton, + GlAlert, + }, + directives: { + SafeHtml: GlSafeHtmlDirective, }, props: { isLoading: { @@ -41,24 +45,26 @@ export default { }; </script> <template> - <div class="text-center p-3"> + <div class="gl-text-center gl-p-5"> <div v-if="illustrationPath" class="svg-content svg-130"><img :src="illustrationPath" /></div> <h4>{{ __('Web Terminal') }}</h4> <gl-loading-icon v-if="isLoading" size="lg" class="gl-mt-3" /> <template v-else> <p>{{ __('Run tests against your code live using the Web Terminal') }}</p> <p> - <button + <gl-button :disabled="!isValid" - class="btn btn-info" - type="button" + category="primary" + variant="info" data-qa-selector="start_web_terminal_button" @click="onStart" > {{ __('Start Web Terminal') }} - </button> + </gl-button> </p> - <div v-if="!isValid && message" class="bs-callout text-left" v-html="message"></div> + <gl-alert v-if="!isValid && message" variant="tip" :dismissible="false"> + <span v-safe-html="message"></span> + </gl-alert> <p v-else> <a v-if="helpPath" diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js index b8d59f8bd36..1496170447d 100644 --- a/app/assets/javascripts/ide/stores/actions.js +++ b/app/assets/javascripts/ide/stores/actions.js @@ -5,7 +5,7 @@ import { visitUrl } from '~/lib/utils/url_utility'; import { deprecatedCreateFlash as flash } from '~/flash'; import * as types from './mutation_types'; import { decorateFiles } from '../lib/files'; -import { stageKeys } from '../constants'; +import { stageKeys, commitActionTypes } from '../constants'; import service from '../services'; import eventHub from '../eventhub'; @@ -242,7 +242,7 @@ export const renameEntry = ({ dispatch, commit, state, getters }, { path, name, } } - dispatch('triggerFilesChange'); + dispatch('triggerFilesChange', { type: commitActionTypes.move, path, newPath }); }; export const getBranchData = ({ commit, state }, { projectId, branchId, force = false } = {}) => diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js index a0df85540f9..4b9b958ddd6 100644 --- a/app/assets/javascripts/ide/stores/actions/file.js +++ b/app/assets/javascripts/ide/stores/actions/file.js @@ -164,26 +164,6 @@ export const changeFileContent = ({ commit, state, getters }, { path, content }) } }; -export const setFileLanguage = ({ getters, commit }, { fileLanguage }) => { - if (getters.activeFile) { - commit(types.SET_FILE_LANGUAGE, { file: getters.activeFile, fileLanguage }); - } -}; - -export const setEditorPosition = ({ getters, commit }, { editorRow, editorColumn }) => { - if (getters.activeFile) { - commit(types.SET_FILE_POSITION, { - file: getters.activeFile, - editorRow, - editorColumn, - }); - } -}; - -export const setFileViewMode = ({ commit }, { file, viewMode }) => { - commit(types.SET_FILE_VIEWMODE, { file, viewMode }); -}; - export const restoreOriginalFile = ({ dispatch, state, commit }, path) => { const file = state.entries[path]; const isDestructiveDiscard = file.tempFile || file.prevPath; @@ -289,7 +269,7 @@ export const removePendingTab = ({ commit }, file) => { eventHub.$emit(`editor.update.model.dispose.${file.key}`); }; -export const triggerFilesChange = () => { +export const triggerFilesChange = (ctx, payload = {}) => { // Used in EE for file mirroring - eventHub.$emit('ide.files.change'); + eventHub.$emit('ide.files.change', payload); }; diff --git a/app/assets/javascripts/ide/stores/index.js b/app/assets/javascripts/ide/stores/index.js index 324c5b0c6e4..d543209716a 100644 --- a/app/assets/javascripts/ide/stores/index.js +++ b/app/assets/javascripts/ide/stores/index.js @@ -12,6 +12,8 @@ import fileTemplates from './modules/file_templates'; import paneModule from './modules/pane'; import clientsideModule from './modules/clientside'; import routerModule from './modules/router'; +import editorModule from './modules/editor'; +import { setupFileEditorsSync } from './modules/editor/setup'; Vue.use(Vuex); @@ -29,7 +31,14 @@ export const createStoreOptions = () => ({ rightPane: paneModule(), clientside: clientsideModule(), router: routerModule, + editor: editorModule, }, }); -export const createStore = () => new Vuex.Store(createStoreOptions()); +export const createStore = () => { + const store = new Vuex.Store(createStoreOptions()); + + setupFileEditorsSync(store); + + return store; +}; diff --git a/app/assets/javascripts/ide/stores/modules/commit/getters.js b/app/assets/javascripts/ide/stores/modules/commit/getters.js index 37f887bcf0a..416ca88d6c9 100644 --- a/app/assets/javascripts/ide/stores/modules/commit/getters.js +++ b/app/assets/javascripts/ide/stores/modules/commit/getters.js @@ -14,6 +14,8 @@ const createTranslatedTextForFiles = (files, text) => { export const discardDraftButtonDisabled = state => state.commitMessage === '' || state.submitCommitLoading; +// Note: If changing the structure of the placeholder branch name, please also +// update #patch_branch_name in app/helpers/tree_helper.rb export const placeholderBranchName = (state, _, rootState) => `${gon.current_username}-${rootState.currentBranchId}-patch-${`${new Date().getTime()}`.substr( -BRANCH_SUFFIX_COUNT, diff --git a/app/assets/javascripts/ide/stores/modules/editor/actions.js b/app/assets/javascripts/ide/stores/modules/editor/actions.js new file mode 100644 index 00000000000..cc23a655235 --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/editor/actions.js @@ -0,0 +1,19 @@ +import * as types from './mutation_types'; + +/** + * Action to update the current file editor info at the given `path` with the given `data` + * + * @param {} vuex + * @param {{ path: String, data: any }} payload + */ +export const updateFileEditor = ({ commit }, payload) => { + commit(types.UPDATE_FILE_EDITOR, payload); +}; + +export const removeFileEditor = ({ commit }, path) => { + commit(types.REMOVE_FILE_EDITOR, path); +}; + +export const renameFileEditor = ({ commit }, payload) => { + commit(types.RENAME_FILE_EDITOR, payload); +}; diff --git a/app/assets/javascripts/ide/stores/modules/editor/getters.js b/app/assets/javascripts/ide/stores/modules/editor/getters.js new file mode 100644 index 00000000000..dabaafa453a --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/editor/getters.js @@ -0,0 +1,13 @@ +import { getFileEditorOrDefault } from './utils'; + +export const activeFileEditor = (state, getters, rootState, rootGetters) => { + const { activeFile } = rootGetters; + + if (!activeFile) { + return null; + } + + const { path } = rootGetters.activeFile; + + return getFileEditorOrDefault(state.fileEditors, path); +}; diff --git a/app/assets/javascripts/ide/stores/modules/editor/index.js b/app/assets/javascripts/ide/stores/modules/editor/index.js new file mode 100644 index 00000000000..8a7437b427d --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/editor/index.js @@ -0,0 +1,12 @@ +import * as actions from './actions'; +import * as getters from './getters'; +import state from './state'; +import mutations from './mutations'; + +export default { + namespaced: true, + actions, + state, + mutations, + getters, +}; diff --git a/app/assets/javascripts/ide/stores/modules/editor/mutation_types.js b/app/assets/javascripts/ide/stores/modules/editor/mutation_types.js new file mode 100644 index 00000000000..89b7e9cbc76 --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/editor/mutation_types.js @@ -0,0 +1,3 @@ +export const UPDATE_FILE_EDITOR = 'UPDATE_FILE_EDITOR'; +export const REMOVE_FILE_EDITOR = 'REMOVE_FILE_EDITOR'; +export const RENAME_FILE_EDITOR = 'RENAME_FILE_EDITOR'; diff --git a/app/assets/javascripts/ide/stores/modules/editor/mutations.js b/app/assets/javascripts/ide/stores/modules/editor/mutations.js new file mode 100644 index 00000000000..f332fe9dce9 --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/editor/mutations.js @@ -0,0 +1,25 @@ +import Vue from 'vue'; +import * as types from './mutation_types'; +import { getFileEditorOrDefault } from './utils'; + +export default { + [types.UPDATE_FILE_EDITOR](state, { path, data }) { + const editor = getFileEditorOrDefault(state.fileEditors, path); + + Vue.set(state.fileEditors, path, Object.assign(editor, data)); + }, + [types.REMOVE_FILE_EDITOR](state, path) { + Vue.delete(state.fileEditors, path); + }, + [types.RENAME_FILE_EDITOR](state, { path, newPath }) { + const existing = state.fileEditors[path]; + + // Gracefully do nothing if fileEditor isn't found. + if (!existing) { + return; + } + + Vue.delete(state.fileEditors, path); + Vue.set(state.fileEditors, newPath, existing); + }, +}; diff --git a/app/assets/javascripts/ide/stores/modules/editor/setup.js b/app/assets/javascripts/ide/stores/modules/editor/setup.js new file mode 100644 index 00000000000..c5a613c6baa --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/editor/setup.js @@ -0,0 +1,19 @@ +import eventHub from '~/ide/eventhub'; +import { commitActionTypes } from '~/ide/constants'; + +const removeUnusedFileEditors = store => { + Object.keys(store.state.editor.fileEditors) + .filter(path => !store.state.entries[path]) + .forEach(path => store.dispatch('editor/removeFileEditor', path)); +}; + +export const setupFileEditorsSync = store => { + eventHub.$on('ide.files.change', ({ type, ...payload } = {}) => { + if (type === commitActionTypes.move) { + store.dispatch('editor/renameFileEditor', payload); + } else { + // The files have changed, but the specific change is not known. + removeUnusedFileEditors(store); + } + }); +}; diff --git a/app/assets/javascripts/ide/stores/modules/editor/state.js b/app/assets/javascripts/ide/stores/modules/editor/state.js new file mode 100644 index 00000000000..484aeec5cc3 --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/editor/state.js @@ -0,0 +1,8 @@ +export default () => ({ + // Object which represents a dictionary of filePath to editor specific properties, including: + // - fileLanguage + // - editorRow + // - editorCol + // - viewMode + fileEditors: {}, +}); diff --git a/app/assets/javascripts/ide/stores/modules/editor/utils.js b/app/assets/javascripts/ide/stores/modules/editor/utils.js new file mode 100644 index 00000000000..bef21d04b2b --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/editor/utils.js @@ -0,0 +1,11 @@ +import { FILE_VIEW_MODE_EDITOR } from '../../../constants'; + +export const createDefaultFileEditor = () => ({ + editorRow: 1, + editorColumn: 1, + fileLanguage: '', + viewMode: FILE_VIEW_MODE_EDITOR, +}); + +export const getFileEditorOrDefault = (fileEditors, path) => + fileEditors[path] || createDefaultFileEditor(); diff --git a/app/assets/javascripts/ide/stores/mutation_types.js b/app/assets/javascripts/ide/stores/mutation_types.js index ae119c2b1fd..22ff29e8866 100644 --- a/app/assets/javascripts/ide/stores/mutation_types.js +++ b/app/assets/javascripts/ide/stores/mutation_types.js @@ -36,9 +36,6 @@ export const SET_FILE_ACTIVE = 'SET_FILE_ACTIVE'; export const SET_FILE_RAW_DATA = 'SET_FILE_RAW_DATA'; export const SET_FILE_BASE_RAW_DATA = 'SET_FILE_BASE_RAW_DATA'; export const UPDATE_FILE_CONTENT = 'UPDATE_FILE_CONTENT'; -export const SET_FILE_LANGUAGE = 'SET_FILE_LANGUAGE'; -export const SET_FILE_POSITION = 'SET_FILE_POSITION'; -export const SET_FILE_VIEWMODE = 'SET_FILE_VIEWMODE'; export const DISCARD_FILE_CHANGES = 'DISCARD_FILE_CHANGES'; export const ADD_FILE_TO_CHANGED = 'ADD_FILE_TO_CHANGED'; export const REMOVE_FILE_FROM_CHANGED = 'REMOVE_FILE_FROM_CHANGED'; diff --git a/app/assets/javascripts/ide/stores/mutations/file.js b/app/assets/javascripts/ide/stores/mutations/file.js index a981f86fa40..61a55d45128 100644 --- a/app/assets/javascripts/ide/stores/mutations/file.js +++ b/app/assets/javascripts/ide/stores/mutations/file.js @@ -95,17 +95,6 @@ export default { changed, }); }, - [types.SET_FILE_LANGUAGE](state, { file, fileLanguage }) { - Object.assign(state.entries[file.path], { - fileLanguage, - }); - }, - [types.SET_FILE_POSITION](state, { file, editorRow, editorColumn }) { - Object.assign(state.entries[file.path], { - editorRow, - editorColumn, - }); - }, [types.SET_FILE_MERGE_REQUEST_CHANGE](state, { file, mrChange }) { let diffMode = diffModes.replaced; if (mrChange.new_file) { @@ -122,11 +111,6 @@ export default { }, }); }, - [types.SET_FILE_VIEWMODE](state, { file, viewMode }) { - Object.assign(state.entries[file.path], { - viewMode, - }); - }, [types.DISCARD_FILE_CHANGES](state, path) { const stagedFile = state.stagedFiles.find(f => f.path === path); const entry = state.entries[path]; diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js index b7ced3a271a..96f3caf1e98 100644 --- a/app/assets/javascripts/ide/stores/utils.js +++ b/app/assets/javascripts/ide/stores/utils.js @@ -1,4 +1,4 @@ -import { commitActionTypes, FILE_VIEW_MODE_EDITOR } from '../constants'; +import { commitActionTypes } from '../constants'; import { relativePathToAbsolute, isAbsolute, @@ -25,10 +25,6 @@ export const dataStructure = () => ({ rawPath: '', raw: '', content: '', - editorRow: 1, - editorColumn: 1, - fileLanguage: '', - viewMode: FILE_VIEW_MODE_EDITOR, size: 0, parentPath: null, lastOpenedAt: 0, diff --git a/app/assets/javascripts/ide/utils.js b/app/assets/javascripts/ide/utils.js index 4cf4f5e1d81..1ca1b971de1 100644 --- a/app/assets/javascripts/ide/utils.js +++ b/app/assets/javascripts/ide/utils.js @@ -1,7 +1,7 @@ import { languages } from 'monaco-editor'; import { flatten, isString } from 'lodash'; import { SIDE_LEFT, SIDE_RIGHT } from './constants'; -import { performanceMarkAndMeasure } from '~/performance_utils'; +import { performanceMarkAndMeasure } from '~/performance/utils'; const toLowerCase = x => x.toLowerCase(); |