summaryrefslogtreecommitdiff
path: root/app/assets
diff options
context:
space:
mode:
authorPhil Hughes <me@iamphill.com>2018-03-22 16:45:31 +0000
committerPhil Hughes <me@iamphill.com>2018-03-28 14:08:28 +0100
commit002cc923898335cbd3a1068d914fb09696e7f8e5 (patch)
treea02d30e2fdaa551a3538a68ecbdc66462d13b8f1 /app/assets
parentb3fb82a9d28571d90e45220c62dd70d7004a42bd (diff)
downloadgitlab-ce-002cc923898335cbd3a1068d914fb09696e7f8e5.tar.gz
Added pending tabs to IDE
Pending tabs are normal tabs that are opened from the right sidebar. They are opened in diff mode and when changed to edit mode they get closed & the actual file gets opened.
Diffstat (limited to 'app/assets')
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/list_item.vue56
-rw-r--r--app/assets/javascripts/ide/components/ide.vue86
-rw-r--r--app/assets/javascripts/ide/components/repo_tab.vue103
-rw-r--r--app/assets/javascripts/ide/components/repo_tabs.vue67
-rw-r--r--app/assets/javascripts/ide/stores/actions.js10
-rw-r--r--app/assets/javascripts/ide/stores/actions/file.js56
-rw-r--r--app/assets/javascripts/ide/stores/getters.js8
-rw-r--r--app/assets/javascripts/ide/stores/mutation_types.js3
-rw-r--r--app/assets/javascripts/ide/stores/mutations/file.js14
-rw-r--r--app/assets/javascripts/ide/stores/state.js1
-rw-r--r--app/assets/stylesheets/pages/repo.scss8
11 files changed, 209 insertions, 203 deletions
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 18934af004a..e443bd4e3fa 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
@@ -1,38 +1,36 @@
<script>
- import { mapActions } from 'vuex';
- import icon from '~/vue_shared/components/icon.vue';
- import router from '../../ide_router';
+import { mapActions } from 'vuex';
+import icon from '~/vue_shared/components/icon.vue';
+import router from '../../ide_router';
- export default {
- components: {
- icon,
+export default {
+ components: {
+ icon,
+ },
+ props: {
+ file: {
+ type: Object,
+ required: true,
},
- props: {
- file: {
- type: Object,
- required: true,
- },
+ },
+ computed: {
+ iconName() {
+ return this.file.tempFile ? 'file-addition' : 'file-modified';
},
- computed: {
- iconName() {
- return this.file.tempFile ? 'file-addition' : 'file-modified';
- },
- iconClass() {
- return `multi-file-${this.file.tempFile ? 'addition' : 'modified'} append-right-8`;
- },
+ iconClass() {
+ return `multi-file-${this.file.tempFile ? 'addition' : 'modified'} append-right-8`;
},
- methods: {
- ...mapActions([
- 'discardFileChanges',
- 'updateViewer',
- ]),
- openFileInEditor(file) {
- this.updateViewer('diff');
-
- router.push(`/project${file.url}`);
- },
+ },
+ methods: {
+ ...mapActions(['discardFileChanges', 'updateViewer', 'openPendingTab']),
+ openFileInEditor(file) {
+ return this.updateViewer('diff').then(() => {
+ this.openPendingTab(file);
+ router.push(`/project/${file.projectId}/tree/master/`);
+ });
},
- };
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue
index 015e750525a..7048f5fab43 100644
--- a/app/assets/javascripts/ide/components/ide.vue
+++ b/app/assets/javascripts/ide/components/ide.vue
@@ -1,51 +1,51 @@
<script>
- import { mapState, mapGetters } from 'vuex';
- import ideSidebar from './ide_side_bar.vue';
- import ideContextbar from './ide_context_bar.vue';
- import repoTabs from './repo_tabs.vue';
- import repoFileButtons from './repo_file_buttons.vue';
- import ideStatusBar from './ide_status_bar.vue';
- import repoEditor from './repo_editor.vue';
+import { mapState, mapGetters } from 'vuex';
+import ideSidebar from './ide_side_bar.vue';
+import ideContextbar from './ide_context_bar.vue';
+import repoTabs from './repo_tabs.vue';
+import repoFileButtons from './repo_file_buttons.vue';
+import ideStatusBar from './ide_status_bar.vue';
+import repoEditor from './repo_editor.vue';
- export default {
- components: {
- ideSidebar,
- ideContextbar,
- repoTabs,
- repoFileButtons,
- ideStatusBar,
- repoEditor,
+export default {
+ components: {
+ ideSidebar,
+ ideContextbar,
+ repoTabs,
+ repoFileButtons,
+ ideStatusBar,
+ repoEditor,
+ },
+ props: {
+ emptyStateSvgPath: {
+ type: String,
+ required: true,
},
- props: {
- emptyStateSvgPath: {
- type: String,
- required: true,
- },
- noChangesStateSvgPath: {
- type: String,
- required: true,
- },
- committedStateSvgPath: {
- type: String,
- required: true,
- },
+ noChangesStateSvgPath: {
+ type: String,
+ required: true,
},
- computed: {
- ...mapState(['changedFiles', 'openFiles', 'viewer']),
- ...mapGetters(['activeFile', 'hasChanges']),
+ committedStateSvgPath: {
+ type: String,
+ required: true,
},
- mounted() {
- const returnValue = 'Are you sure you want to lose unsaved changes?';
- window.onbeforeunload = e => {
- if (!this.changedFiles.length) return undefined;
+ },
+ computed: {
+ ...mapState(['changedFiles', 'openFiles', 'viewer']),
+ ...mapGetters(['activeFile', 'hasChanges', 'tabs']),
+ },
+ mounted() {
+ const returnValue = 'Are you sure you want to lose unsaved changes?';
+ window.onbeforeunload = e => {
+ if (!this.changedFiles.length) return undefined;
- Object.assign(e, {
- returnValue,
- });
- return returnValue;
- };
- },
- };
+ Object.assign(e, {
+ returnValue,
+ });
+ return returnValue;
+ };
+ },
+};
</script>
<template>
@@ -60,7 +60,7 @@
v-if="activeFile"
>
<repo-tabs
- :files="openFiles"
+ :files="tabs"
:viewer="viewer"
:has-changes="hasChanges"
/>
diff --git a/app/assets/javascripts/ide/components/repo_tab.vue b/app/assets/javascripts/ide/components/repo_tab.vue
index c337bc813e6..b36fb6b4830 100644
--- a/app/assets/javascripts/ide/components/repo_tab.vue
+++ b/app/assets/javascripts/ide/components/repo_tab.vue
@@ -1,60 +1,58 @@
<script>
- import { mapActions } from 'vuex';
+import { mapActions } from 'vuex';
- import fileIcon from '~/vue_shared/components/file_icon.vue';
- import icon from '~/vue_shared/components/icon.vue';
- import fileStatusIcon from './repo_file_status_icon.vue';
- import changedFileIcon from './changed_file_icon.vue';
+import fileIcon from '~/vue_shared/components/file_icon.vue';
+import icon from '~/vue_shared/components/icon.vue';
+import fileStatusIcon from './repo_file_status_icon.vue';
+import changedFileIcon from './changed_file_icon.vue';
- export default {
- components: {
- fileStatusIcon,
- fileIcon,
- icon,
- changedFileIcon,
+export default {
+ components: {
+ fileStatusIcon,
+ fileIcon,
+ icon,
+ changedFileIcon,
+ },
+ props: {
+ tab: {
+ type: Object,
+ required: true,
},
- props: {
- tab: {
- type: Object,
- required: true,
- },
+ },
+ data() {
+ return {
+ tabMouseOver: false,
+ };
+ },
+ computed: {
+ closeLabel() {
+ if (this.tab.changed || this.tab.tempFile) {
+ return `${this.tab.name} changed`;
+ }
+ return `Close ${this.tab.name}`;
},
- data() {
- return {
- tabMouseOver: false,
- };
- },
- computed: {
- closeLabel() {
- if (this.tab.changed || this.tab.tempFile) {
- return `${this.tab.name} changed`;
- }
- return `Close ${this.tab.name}`;
- },
- showChangedIcon() {
- return this.tab.changed ? !this.tabMouseOver : false;
- },
+ showChangedIcon() {
+ return this.tab.changed ? !this.tabMouseOver : false;
},
+ },
- methods: {
- ...mapActions([
- 'closeFile',
- ]),
- clickFile(tab) {
- this.$router.push(`/project${tab.url}`);
- },
- mouseOverTab() {
- if (this.tab.changed) {
- this.tabMouseOver = true;
- }
- },
- mouseOutTab() {
- if (this.tab.changed) {
- this.tabMouseOver = false;
- }
- },
+ methods: {
+ ...mapActions(['closeFile']),
+ clickFile(tab) {
+ this.$router.push(`/project${tab.url}`);
+ },
+ mouseOverTab() {
+ if (this.tab.changed) {
+ this.tabMouseOver = true;
+ }
+ },
+ mouseOutTab() {
+ if (this.tab.changed) {
+ this.tabMouseOver = false;
+ }
},
- };
+ },
+};
</script>
<template>
@@ -66,7 +64,7 @@
<button
type="button"
class="multi-file-tab-close"
- @click.stop.prevent="closeFile(tab.path)"
+ @click.stop.prevent="closeFile(tab)"
:aria-label="closeLabel"
>
<icon
@@ -82,7 +80,10 @@
<div
class="multi-file-tab"
- :class="{active : tab.active }"
+ :class="{
+ active: tab.active,
+ pending: tab.pending
+ }"
:title="tab.url"
>
<file-icon
diff --git a/app/assets/javascripts/ide/components/repo_tabs.vue b/app/assets/javascripts/ide/components/repo_tabs.vue
index 8ea64ddf84a..dcb2ff80ce1 100644
--- a/app/assets/javascripts/ide/components/repo_tabs.vue
+++ b/app/assets/javascripts/ide/components/repo_tabs.vue
@@ -1,42 +1,41 @@
<script>
- import { mapActions } from 'vuex';
- import RepoTab from './repo_tab.vue';
- import EditorMode from './editor_mode_dropdown.vue';
+import { mapActions } from 'vuex';
+import RepoTab from './repo_tab.vue';
+import EditorMode from './editor_mode_dropdown.vue';
- export default {
- components: {
- RepoTab,
- EditorMode,
+export default {
+ components: {
+ RepoTab,
+ EditorMode,
+ },
+ props: {
+ files: {
+ type: Array,
+ required: true,
},
- props: {
- files: {
- type: Array,
- required: true,
- },
- viewer: {
- type: String,
- required: true,
- },
- hasChanges: {
- type: Boolean,
- required: true,
- },
+ viewer: {
+ type: String,
+ required: true,
},
- data() {
- return {
- showShadow: false,
- };
+ hasChanges: {
+ type: Boolean,
+ required: true,
},
- updated() {
- if (!this.$refs.tabsScroller) return;
+ },
+ data() {
+ return {
+ showShadow: false,
+ };
+ },
+ updated() {
+ if (!this.$refs.tabsScroller) return;
- this.showShadow =
- this.$refs.tabsScroller.scrollWidth > this.$refs.tabsScroller.offsetWidth;
- },
- methods: {
- ...mapActions(['updateViewer']),
- },
- };
+ this.showShadow = this.$refs.tabsScroller.scrollWidth > this.$refs.tabsScroller.offsetWidth;
+ },
+ methods: {
+ ...mapActions(['updateViewer']),
+ },
+};
</script>
<template>
@@ -47,7 +46,7 @@
>
<repo-tab
v-for="tab in files"
- :key="tab.key"
+ :key="`${tab.key}${tab.pending ? '-pending' : ''}`"
:tab="tab"
/>
</ul>
diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js
index 7e920aa9f30..1300ea8e520 100644
--- a/app/assets/javascripts/ide/stores/actions.js
+++ b/app/assets/javascripts/ide/stores/actions.js
@@ -6,8 +6,7 @@ import FilesDecoratorWorker from './workers/files_decorator_worker';
export const redirectToUrl = (_, url) => visitUrl(url);
-export const setInitialData = ({ commit }, data) =>
- commit(types.SET_INITIAL_DATA, data);
+export const setInitialData = ({ commit }, data) => commit(types.SET_INITIAL_DATA, data);
export const discardAllChanges = ({ state, commit, dispatch }) => {
state.changedFiles.forEach(file => {
@@ -43,14 +42,11 @@ export const createTempEntry = (
) =>
new Promise(resolve => {
const worker = new FilesDecoratorWorker();
- const fullName =
- name.slice(-1) !== '/' && type === 'tree' ? `${name}/` : name;
+ const fullName = name.slice(-1) !== '/' && type === 'tree' ? `${name}/` : name;
if (state.entries[name]) {
flash(
- `The name "${name
- .split('/')
- .pop()}" is already taken in this directory.`,
+ `The name "${name.split('/').pop()}" is already taken in this directory.`,
'alert',
document,
null,
diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js
index ddc4b757bf9..5a39bfb02c4 100644
--- a/app/assets/javascripts/ide/stores/actions/file.js
+++ b/app/assets/javascripts/ide/stores/actions/file.js
@@ -6,21 +6,26 @@ import * as types from '../mutation_types';
import router from '../../ide_router';
import { setPageTitle } from '../utils';
-export const closeFile = ({ commit, state, getters, dispatch }, path) => {
- const indexOfClosedFile = state.openFiles.findIndex(f => f.path === path);
- const file = state.entries[path];
- const fileWasActive = file.active;
+export const closeFile = ({ commit, state, getters, dispatch }, file) => {
+ const path = file.path;
+
+ if (file.pending) {
+ commit(types.REMOVE_PENDING_TAB, file);
+ } else {
+ const indexOfClosedFile = state.openFiles.findIndex(f => f.path === path);
+ const fileWasActive = file.active;
- commit(types.TOGGLE_FILE_OPEN, path);
- commit(types.SET_FILE_ACTIVE, { path, active: false });
+ commit(types.TOGGLE_FILE_OPEN, path);
+ commit(types.SET_FILE_ACTIVE, { path, active: false });
- if (state.openFiles.length > 0 && fileWasActive) {
- const nextIndexToOpen = indexOfClosedFile === 0 ? 0 : indexOfClosedFile - 1;
- const nextFileToOpen = state.entries[state.openFiles[nextIndexToOpen].path];
+ if (state.openFiles.length > 0 && fileWasActive) {
+ const nextIndexToOpen = indexOfClosedFile === 0 ? 0 : indexOfClosedFile - 1;
+ const nextFileToOpen = state.entries[state.openFiles[nextIndexToOpen].path];
- router.push(`/project${nextFileToOpen.url}`);
- } else if (!state.openFiles.length) {
- router.push(`/project/${file.projectId}/tree/${file.branchId}/`);
+ router.push(`/project${nextFileToOpen.url}`);
+ } else if (!state.openFiles.length) {
+ router.push(`/project/${file.projectId}/tree/${file.branchId}/`);
+ }
}
eventHub.$emit(`editor.update.model.dispose.${file.path}`);
@@ -66,14 +71,7 @@ export const getFileData = ({ state, commit, dispatch }, file) => {
})
.catch(() => {
commit(types.TOGGLE_LOADING, { entry: file });
- flash(
- 'Error loading file data. Please try again.',
- 'alert',
- document,
- null,
- false,
- true,
- );
+ flash('Error loading file data. Please try again.', 'alert', document, null, false, true);
});
};
@@ -84,14 +82,7 @@ export const getRawFileData = ({ commit, dispatch }, file) =>
commit(types.SET_FILE_RAW_DATA, { file, raw });
})
.catch(() =>
- flash(
- 'Error loading file content. Please try again.',
- 'alert',
- document,
- null,
- false,
- true,
- ),
+ flash('Error loading file content. Please try again.', 'alert', document, null, false, true),
);
export const changeFileContent = ({ state, commit }, { path, content }) => {
@@ -119,10 +110,7 @@ export const setFileEOL = ({ getters, commit }, { eol }) => {
}
};
-export const setEditorPosition = (
- { getters, commit },
- { editorRow, editorColumn },
-) => {
+export const setEditorPosition = ({ getters, commit }, { editorRow, editorColumn }) => {
if (getters.activeFile) {
commit(types.SET_FILE_POSITION, {
file: getters.activeFile,
@@ -144,3 +132,7 @@ export const discardFileChanges = ({ state, commit }, path) => {
eventHub.$emit(`editor.update.model.content.${file.path}`, file.raw);
};
+
+export const openPendingTab = ({ commit }, file) => {
+ commit(types.ADD_PENDING_TAB, file);
+};
diff --git a/app/assets/javascripts/ide/stores/getters.js b/app/assets/javascripts/ide/stores/getters.js
index eba325a31df..28006d3ddf8 100644
--- a/app/assets/javascripts/ide/stores/getters.js
+++ b/app/assets/javascripts/ide/stores/getters.js
@@ -1,10 +1,10 @@
-export const activeFile = state =>
- state.openFiles.find(file => file.active) || null;
+export const tabs = state => state.openFiles.concat(state.pendingTabs);
+
+export const activeFile = state => tabs(state).find(file => file.active) || null;
export const addedFiles = state => state.changedFiles.filter(f => f.tempFile);
-export const modifiedFiles = state =>
- state.changedFiles.filter(f => !f.tempFile);
+export const modifiedFiles = state => state.changedFiles.filter(f => !f.tempFile);
export const projectsWithTrees = state =>
Object.keys(state.projects).map(projectId => {
diff --git a/app/assets/javascripts/ide/stores/mutation_types.js b/app/assets/javascripts/ide/stores/mutation_types.js
index e28f190897c..fa2fbaf8683 100644
--- a/app/assets/javascripts/ide/stores/mutation_types.js
+++ b/app/assets/javascripts/ide/stores/mutation_types.js
@@ -41,3 +41,6 @@ export const SET_ENTRIES = 'SET_ENTRIES';
export const CREATE_TMP_ENTRY = 'CREATE_TMP_ENTRY';
export const UPDATE_VIEWER = 'UPDATE_VIEWER';
export const UPDATE_DELAY_VIEWER_CHANGE = 'UPDATE_DELAY_VIEWER_CHANGE';
+
+export const ADD_PENDING_TAB = 'ADD_PENDING_TAB';
+export const REMOVE_PENDING_TAB = 'REMOVE_PENDING_TAB';
diff --git a/app/assets/javascripts/ide/stores/mutations/file.js b/app/assets/javascripts/ide/stores/mutations/file.js
index 2500f13db7c..915126f10eb 100644
--- a/app/assets/javascripts/ide/stores/mutations/file.js
+++ b/app/assets/javascripts/ide/stores/mutations/file.js
@@ -80,4 +80,18 @@ export default {
changed,
});
},
+ [types.ADD_PENDING_TAB](state, file) {
+ Object.assign(state, {
+ pendingTabs: state.pendingTabs.concat({
+ ...file,
+ active: true,
+ pending: true,
+ }),
+ });
+ },
+ [types.REMOVE_PENDING_TAB](state, file) {
+ Object.assign(state, {
+ pendingTabs: state.pendingTabs.filter(f => f.path !== file.path),
+ });
+ },
};
diff --git a/app/assets/javascripts/ide/stores/state.js b/app/assets/javascripts/ide/stores/state.js
index 6110f54951c..52a2400ec42 100644
--- a/app/assets/javascripts/ide/stores/state.js
+++ b/app/assets/javascripts/ide/stores/state.js
@@ -16,4 +16,5 @@ export default () => ({
entries: {},
viewer: 'editor',
delayViewerUpdated: false,
+ pendingTabs: [],
});
diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss
index 65046f6665e..94aa5130f28 100644
--- a/app/assets/stylesheets/pages/repo.scss
+++ b/app/assets/stylesheets/pages/repo.scss
@@ -177,6 +177,10 @@
background-color: $white-light;
border-bottom-color: $white-light;
}
+
+ &.pending {
+ font-style: italic;
+ }
}
.multi-file-tab-close {
@@ -720,9 +724,7 @@
}
.ide-view {
- height: calc(
- 100vh - #{$header-height + $performance-bar-height + $flash-height}
- );
+ height: calc(100vh - #{$header-height + $performance-bar-height + $flash-height});
}
}
}