Commit caee9d58 authored by Phil Hughes's avatar Phil Hughes

Add new files & directories in the multi-file editor

Closes #38614
parent 5295f23b
<script>
import newModal from './modal.vue';
export default {
components: {
newModal,
},
data() {
return {
openModal: false,
modalType: '',
};
},
methods: {
createNewItem(type) {
this.modalType = type;
this.toggleModalOpen();
},
toggleModalOpen() {
this.openModal = !this.openModal;
},
},
};
</script>
<template>
<div class="breadcrumb repo-breadcrumb">
<div class="dropdown">
<button
type="button"
class="btn btn-default dropdown-toggle add-to-tree"
data-toggle="dropdown"
data-target=".add-to-tree-dropdown"
>
<i
class="fa fa-plus"
aria-hidden="true"
>
</i>
</button>
</div>
<div class="add-to-tree-dropdown">
<ul class="dropdown-menu">
<li>
<a
href="#"
role="button"
@click.prevent="createNewItem('blob')"
>
{{ __('New file') }}
</a>
</li>
<li>
<a
href="#"
role="button"
@click.prevent="createNewItem('tree')"
>
{{ __('New directory') }}
</a>
</li>
</ul>
</div>
<new-modal
v-if="openModal"
:type="modalType"
@toggle="toggleModalOpen"
/>
</div>
</template>
<script>
import { __ } from '../../../locale';
import popupDialog from '../../../vue_shared/components/popup_dialog.vue';
import RepoStore from '../../stores/repo_store';
import RepoHelper from '../../helpers/repo_helper';
export default {
props: {
type: {
type: String,
required: true,
},
},
data() {
return {
entryName: '',
};
},
components: {
popupDialog,
},
methods: {
createEntryInStore() {
if (this.entryName === '') return;
const fileName = this.type === 'tree' ? '.gitkeep' : this.entryName;
let tree = null;
if (this.type === 'tree') {
tree = RepoHelper.serializeTree({
name: this.entryName,
path: this.entryName,
tempFile: true,
});
RepoStore.files.push(tree);
RepoHelper.setDirectoryOpen(tree, tree.name);
}
const file = RepoHelper.serializeBlob({
name: fileName,
path: tree ? `${tree}/${fileName}` : fileName,
tempFile: true,
});
if (tree) {
RepoStore.addFilesToDirectory(tree, RepoStore.files, [file]);
} else {
RepoStore.addFilesToDirectory(tree, RepoStore.files, [...RepoStore.files, file]);
}
RepoHelper.setFile(file, file);
RepoStore.editMode = true;
RepoStore.toggleBlobView();
this.toggleModalOpen();
},
toggleModalOpen() {
this.$emit('toggle');
},
},
computed: {
modalTitle() {
if (this.type === 'tree') {
return __('Create new directory');
}
return __('Create new file');
},
buttonLabel() {
if (this.type === 'tree') {
return __('Create directory');
}
return __('Create file');
},
formLabelName() {
if (this.type === 'tree') {
return __('Directory name');
}
return __('File name');
},
},
};
</script>
<template>
<popup-dialog
:title="modalTitle"
:primary-button-label="buttonLabel"
kind="success"
@toggle="toggleModalOpen"
@submit="createEntryInStore"
>
<form
class="form-horizontal"
slot="body"
@submit.prevent="createEntryInStore"
>
<fieldset class="form-group append-bottom-0">
<label class="label-light col-sm-3">
{{ formLabelName }}
</label>
<div class="col-sm-9">
<input
type="text"
class="form-control"
v-model="entryName"
/>
</div>
</fieldset>
</form>
</popup-dialog>
</template>
......@@ -49,7 +49,7 @@ export default {
// see https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions
const commitMessage = this.commitMessage;
const actions = this.changedFiles.map(f => ({
action: 'update',
action: f.tempFile ? 'create' : 'update',
file_path: f.path,
content: f.newContent,
}));
......
......@@ -16,11 +16,22 @@ const RepoEditor = {
},
mounted() {
if (!this.activeFile.tempFile) {
Service.getRaw(this.activeFile.raw_path)
.then((rawResponse) => {
Store.blobRaw = rawResponse.data;
Store.activeFile.plain = rawResponse.data;
this.createMonacoInstance();
})
.catch(Helper.loadingError);
} else {
this.createMonacoInstance();
}
},
methods: {
createMonacoInstance() {
const monacoInstance = Helper.monaco.editor.create(this.$el, {
model: null,
readOnly: false,
......@@ -33,11 +44,7 @@ const RepoEditor = {
this.addMonacoEvents();
this.setupEditor();
})
.catch(Helper.loadingError);
},
methods: {
setupEditor() {
this.showHide();
......
......@@ -11,7 +11,12 @@ const RepoFileButtons = {
mixins: [RepoMixin],
computed: {
showButtons() {
return this.activeFile.raw_path ||
this.activeFile.blame_path ||
this.activeFile.commits_path ||
this.activeFile.permalink;
},
rawDownloadButtonLabel() {
return this.binary ? 'Download' : 'Raw';
},
......@@ -30,7 +35,10 @@ export default RepoFileButtons;
</script>
<template>
<div id="repo-file-buttons">
<div
v-if="showButtons"
id="repo-file-buttons"
>
<a
:href="activeFile.raw_path"
target="_blank"
......
......@@ -18,8 +18,8 @@ const RepoTab = {
},
changedClass() {
const tabChangedObj = {
'fa-times close-icon': !this.tab.changed,
'fa-circle unsaved-icon': this.tab.changed,
'fa-times close-icon': !this.tab.changed && !this.tab.tempFile,
'fa-circle unsaved-icon': this.tab.changed || this.tab.tempFile,
};
return tabChangedObj;
},
......
......@@ -157,7 +157,7 @@ const RepoHelper = {
},
serializeRepoEntity(type, entity, level = 0) {
const { id, url, name, icon, last_commit, tree_url } = entity;
const { id, url, name, icon, last_commit, tree_url, path, tempFile } = entity;
return {
id,
......@@ -165,7 +165,9 @@ const RepoHelper = {
name,
url,
tree_url,
path,
level,
tempFile,
icon: `fa-${icon}`,
files: [],
loading: false,
......
......@@ -5,6 +5,7 @@ import Service from './services/repo_service';
import Store from './stores/repo_store';
import Repo from './components/repo.vue';
import RepoEditButton from './components/repo_edit_button.vue';
import newDropdown from './components/new_dropdown/index.vue';
import Translate from '../vue_shared/translate';
function initDropdowns() {
......@@ -62,9 +63,22 @@ function initRepoEditButton(el) {
});
}
function initNewDropdown(el) {
return new Vue({
el,
components: {
newDropdown,
},
render(createElement) {
return createElement('new-dropdown');
},
});
}
function initRepoBundle() {
const repo = document.getElementById('repo');
const editButton = document.querySelector('.editable-mode');
const newDropdownHolder = document.querySelector('.js-new-dropdown');
setInitialStore(repo.dataset);
addEventsForNonVueEls();
initDropdowns();
......@@ -73,6 +87,7 @@ function initRepoBundle() {
initRepo(repo);
initRepoEditButton(editButton);
initNewDropdown(newDropdownHolder);
}
$(initRepoBundle);
......
......@@ -8,7 +8,7 @@ const RepoMixin = {
changedFiles() {
const changedFileList = this.openedFiles
.filter(file => file.changed);
.filter(file => file.changed || file.tempFile);
return changedFileList;
},
},
......
......@@ -75,7 +75,7 @@ const RepoStore = {
RepoStore.blobRaw = file.base64;
} else if (file.newContent || file.plain) {
RepoStore.blobRaw = file.newContent || file.plain;
} else {
} else if (!file.tempFile) {
Service.getRaw(file.raw_path)
.then((rawResponse) => {
RepoStore.blobRaw = rawResponse.data;
......@@ -120,6 +120,11 @@ const RepoStore = {
return openedFile.path !== file.path;
});
// remove the file from the sidebar if it is a tempFile
if (file.tempFile) {
RepoStore.files = RepoStore.files.filter(f => !(f.tempFile && f.path === file.path));
}
// now activate the right tab based on what you closed.
if (RepoStore.openedFiles.length === 0) {
RepoStore.activeFile = {};
......
......@@ -9,7 +9,7 @@ export default {
},
text: {
type: String,
required: true,
required: false,
},
kind: {
type: String,
......@@ -82,14 +82,15 @@ export default {
type="button"
class="btn"
:class="btnCancelKindClass"
@click="emitSubmit(false)">
{{closeButtonLabel}}
@click="close">
{{ closeButtonLabel }}
</button>
<button type="button"
<button
type="button"
class="btn"
:class="btnKindClass"
@click="emitSubmit(true)">
{{primaryButtonLabel}}
{{ primaryButtonLabel }}
</button>
</div>
</div>
......
......@@ -2,7 +2,9 @@
.tree-ref-holder
= render 'shared/ref_switcher', destination: 'tree', path: @path
- unless show_new_repo?
- if show_new_repo?
.js-new-dropdown
- else
= render 'projects/tree/old_tree_header'
.tree-controls
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment