Commit 0b500111 authored by Jacob Schatz's avatar Jacob Schatz

Adds support for multiple file updating.

parent 631ee31f
...@@ -19,23 +19,17 @@ export default class RepoBinaryViewer { ...@@ -19,23 +19,17 @@ export default class RepoBinaryViewer {
}, },
methods: { methods: {
supportedNonBinaryFileType() { isMarkdown() {
switch(this.activeFile.extension) { return this.activeFile.extension === 'md';
case 'md': },
this.binaryTypes.markdown = true;
return true;
break;
default:
return false;
}
}
}, },
watch: { watch: {
blobRaw() { blobRaw() {
let supported = this.supportedNonBinaryFileType(); if(this.isMarkdown()) {
if(supported) {
this.binaryTypes.markdown = true; this.binaryTypes.markdown = true;
this.activeFile.raw = false;
// counts as binaryish so we use the binary viewer in this case.
this.binary = true; this.binary = true;
return; return;
} }
......
...@@ -2,8 +2,9 @@ import Tabs from './repo_tabs' ...@@ -2,8 +2,9 @@ import Tabs from './repo_tabs'
import Sidebar from './repo_sidebar' import Sidebar from './repo_sidebar'
import Editor from './repo_editor' import Editor from './repo_editor'
import FileButtons from './repo_file_buttons' import FileButtons from './repo_file_buttons'
import EditButton from './repo_edit_button'
import BinaryViewer from './repo_binary_viewer' import BinaryViewer from './repo_binary_viewer'
import ViewToggler from './repo_view_toggler' import CommitSection from './repo_commit_section'
import Service from './repo_service' import Service from './repo_service'
import Store from './repo_store' import Store from './repo_store'
import Helper from './repo_helper' import Helper from './repo_helper'
...@@ -17,7 +18,8 @@ export default class RepoBundle { ...@@ -17,7 +18,8 @@ export default class RepoBundle {
Store.sidebar = new Sidebar(url); Store.sidebar = new Sidebar(url);
Store.editor = new Editor(); Store.editor = new Editor();
Store.buttons = new FileButtons(); Store.buttons = new FileButtons();
// Store.toggler = new ViewToggler(); Store.editButton = new EditButton();
Store.commitSection = new CommitSection();
Store.binaryViewer = new BinaryViewer(); Store.binaryViewer = new BinaryViewer();
Helper.getContent(); Helper.getContent();
} }
......
import Helper from './repo_helper'
import Vue from 'vue'
import Store from './repo_store'
export default class RepoCommitSection {
constructor() {
this.initVue();
this.el = document.getElementById('commit-area');
}
initVue() {
this.vue = new Vue({
el: '#commit-area',
data: () => Store,
computed: {
changedFiles() {
const changedFileList = this.openedFiles
.filter((file) => {
return file.changed;
});
console.log('changedFileList',changedFileList);
return changedFileList;
},
}
});
}
}
\ No newline at end of file
import Service from './repo_service' import Service from './repo_service'
import Helper from './repo_helper'
import Vue from 'vue' import Vue from 'vue'
import Store from './repo_store' import Store from './repo_store'
export default class RepoViewToggler { export default class RepoEditButton {
constructor() { constructor() {
this.initVue(); this.initVue();
this.el = document.getElementById('editable-mode');
} }
initVue() { initVue() {
this.vue = new Vue({ this.vue = new Vue({
el: '#view-toggler', el: '#editable-mode',
data: () => Store, data: () => Store,
computed: {
buttonLabel() {
return this.editMode ? 'Read-only mode' : 'Edit mode';
},
buttonIcon() {
return this.editMode ? [] : ['fa', 'fa-pencil'];
}
},
methods: {
editClicked() {
this.editMode = !this.editMode;
}
}
}); });
} }
} }
\ No newline at end of file
...@@ -10,10 +10,8 @@ export default class RepoEditor { ...@@ -10,10 +10,8 @@ export default class RepoEditor {
} }
addMonacoEvents() { addMonacoEvents() {
this.vue.$watch('activeFile.lineNumber', () => { this.monacoEditor.onMouseUp(this.onMonacoEditorMouseUp.bind(this));
console.log('cahnged') this.monacoEditor.onKeyUp(this.onMonacoEditorKeysPressed.bind(this));
})
this.monacoEditor.onMouseUp(this.onMonacoEditorMouseUp);
} }
onMonacoEditorMouseUp(e) { onMonacoEditorMouseUp(e) {
...@@ -23,6 +21,10 @@ export default class RepoEditor { ...@@ -23,6 +21,10 @@ export default class RepoEditor {
} }
} }
onMonacoEditorKeysPressed(e) {
Helper.setActiveFileContents(this.monacoEditor.getValue());
}
initMonaco() { initMonaco() {
window.require.config({ paths: { vs: '/monaco-editor/min/vs' } }); window.require.config({ paths: { vs: '/monaco-editor/min/vs' } });
window.require(['vs/editor/editor.main'], () => { window.require(['vs/editor/editor.main'], () => {
...@@ -61,7 +63,7 @@ export default class RepoEditor { ...@@ -61,7 +63,7 @@ export default class RepoEditor {
methods: { methods: {
showHide() { showHide() {
if((!this.openedFiles.length) || this.binary) { if(!this.openedFiles.length || (this.binary && !this.activeFile.raw)) {
self.el.style.display = 'none'; self.el.style.display = 'none';
} else { } else {
self.el.style.display = 'inline-block'; self.el.style.display = 'inline-block';
...@@ -77,6 +79,26 @@ export default class RepoEditor { ...@@ -77,6 +79,26 @@ export default class RepoEditor {
}); });
}, },
editMode() {
if(this.editMode) {
document.querySelector('.panel-right').classList.add('edit-mode');
self.monacoEditor.updateOptions({
readOnly: false
});
} else {
document.querySelector('.panel-right').classList.remove('edit-mode');
self.monacoEditor.updateOptions({
readOnly: true
});
}
},
activeFileLabel() {
this.showHide();
},
isTree() { isTree() {
this.showHide(); this.showHide();
}, },
...@@ -93,12 +115,16 @@ export default class RepoEditor { ...@@ -93,12 +115,16 @@ export default class RepoEditor {
this.showHide(); this.showHide();
if(!this.isTree) { if(!this.isTree) {
// kill the current model;
self.monacoEditor.setModel(null);
// then create the new one
self.monacoEditor.setModel( self.monacoEditor.setModel(
monaco.editor.createModel( monaco.editor.createModel(
this.blobRaw, this.blobRaw,
this.activeFile.mime_type this.activeFile.mime_type
) )
); );
console.log(monaco.editor.getModels());
} }
} }
} }
......
import Vue from 'vue' import Vue from 'vue'
import Store from './repo_store' import Store from './repo_store'
import Helper from './repo_helper' import Helper from './repo_helper'
import RepoMiniMixin from './repo_mini_mixin'
export default class RepoSidebar { export default class RepoSidebar {
constructor(url) { constructor(url) {
...@@ -13,8 +14,9 @@ export default class RepoSidebar { ...@@ -13,8 +14,9 @@ export default class RepoSidebar {
this.vue = new Vue({ this.vue = new Vue({
el: '#repo-file-buttons', el: '#repo-file-buttons',
data: () => Store, data: () => Store,
mixins: [RepoMiniMixin],
template: ` template: `
<div id='repo-file-buttons' v-if='isMini'> <div id='repo-file-buttons' v-if='isMini' :style='{"border-bottom": editableBorder}'>
<a :href='rawFileURL' target='_blank' class='btn btn-default'>Download file</a> <a :href='rawFileURL' target='_blank' class='btn btn-default'>Download file</a>
<div class="btn-group" role="group" aria-label="File actions"> <div class="btn-group" role="group" aria-label="File actions">
<a :href='blameFileUrl' class='btn btn-default'>Blame</a> <a :href='blameFileUrl' class='btn btn-default'>Blame</a>
...@@ -22,13 +24,16 @@ export default class RepoSidebar { ...@@ -22,13 +24,16 @@ export default class RepoSidebar {
<a href='#' class='btn btn-default'>Permalink</a> <a href='#' class='btn btn-default'>Permalink</a>
<a href='#' class='btn btn-default'>Lock</a> <a href='#' class='btn btn-default'>Lock</a>
</div> </div>
<a href='#' v-if='canPreview' class='btn btn-default'>{{previewLabel}}</a> <a href='#' v-if='canPreview' @click.prevent='rawPreviewToggle' class='btn btn-default'>
{{activeFileLabel}}
</a>
<a href='#' class='btn btn-danger'>Delete</a> <a href='#' class='btn btn-danger'>Delete</a>
</div> </div>
`, `,
computed: { computed: {
previewLabel() {
return this.activeFile.raw ? 'Preview' : 'Raw' editableBorder() {
return this.editMode ? '1px solid #1F78D1' :'1px solid #f0f0f0';
}, },
canPreview() { canPreview() {
...@@ -49,10 +54,10 @@ export default class RepoSidebar { ...@@ -49,10 +54,10 @@ export default class RepoSidebar {
}, },
methods: { methods: {
setRawPreviewMode() { rawPreviewToggle() {
Helper.setCurrentFileRawOrPreview();
} }
} },
}); });
} }
} }
\ No newline at end of file
...@@ -70,14 +70,29 @@ let RepoHelper = { ...@@ -70,14 +70,29 @@ let RepoHelper = {
} }
}, },
setCurrentFileRawOrPreview() {
Store.activeFile.raw = !Store.activeFile.raw;
Store.activeFileLabel = Store.activeFile.raw ? 'Preview' : 'Raw';
},
setActiveFile(file) { setActiveFile(file) {
Store.openedFiles = Store.openedFiles.map((openedFile) => { // don't load the file that is already loaded
if(file.url === Store.activeFile.url) return;
Store.openedFiles = Store.openedFiles.map((openedFile, i) => {
openedFile.active = file.url === openedFile.url; openedFile.active = file.url === openedFile.url;
if(openedFile.active) { if(openedFile.active) {
Store.activeFile = openedFile; Store.activeFile = openedFile;
Store.activeFileIndex = i;
} }
return openedFile; return openedFile;
}); });
// reset the active file raw
Store.activeFile.raw = false;
// can't get vue to listen to raw for some reason so this for now.
Store.activeFileLabel = 'Raw';
if(file.binary) { if(file.binary) {
Store.blobRaw = file.base64; Store.blobRaw = file.base64;
} else { } else {
...@@ -101,6 +116,7 @@ let RepoHelper = { ...@@ -101,6 +116,7 @@ let RepoHelper = {
return openedFile.url === file.url return openedFile.url === file.url
}); });
if(!openedFilesAlreadyExists) { if(!openedFilesAlreadyExists) {
file.changed = false;
Store.openedFiles.push(file); Store.openedFiles.push(file);
} }
}, },
...@@ -132,6 +148,13 @@ let RepoHelper = { ...@@ -132,6 +148,13 @@ let RepoHelper = {
}); });
}, },
setActiveFileContents(contents) {
if(!Store.editMode) return;
Store.activeFile.newContent = contents;
Store.activeFile.changed = Store.activeFile.plain !== Store.activeFile.newContent;
Store.openedFiles[Store.activeFileIndex].changed = Store.activeFile.changed;
},
toggleFakeTab(loading, file) { toggleFakeTab(loading, file) {
if(loading) { if(loading) {
const randomURL = this.Time.now(); const randomURL = this.Time.now();
...@@ -163,6 +186,8 @@ let RepoHelper = { ...@@ -163,6 +186,8 @@ let RepoHelper = {
// may be tree or file. // may be tree or file.
getContent(file) { getContent(file) {
// don't load the same active file. That's silly.
// if(file && file.url === this.activeFile.url) return;
const loadingData = this.setLoading(true); const loadingData = this.setLoading(true);
Service.getContent() Service.getContent()
.then((response) => { .then((response) => {
...@@ -182,22 +207,17 @@ let RepoHelper = { ...@@ -182,22 +207,17 @@ let RepoHelper = {
data data
); );
data.binary = true; data.binary = true;
if(!file.url) {
file.url = location.pathname;
}
data.url = file.url;
this.addToOpenedFiles(data);
this.setActiveFile(data);
} else { } else {
Store.blobRaw = data.plain; Store.blobRaw = data.plain;
if(!file.url) {
file.url = location.pathname;
}
data.url = file.url;
data.binary = false; data.binary = false;
this.addToOpenedFiles(data);
this.setActiveFile(data);
} }
if(!file.url) {
file.url = location.pathname;
}
data.url = file.url;
data.newContent = '';
this.addToOpenedFiles(data);
this.setActiveFile(data);
// if the file tree is empty // if the file tree is empty
if(Store.files.length === 0) { if(Store.files.length === 0) {
......
...@@ -51,7 +51,7 @@ export default class RepoSidebar { ...@@ -51,7 +51,7 @@ export default class RepoSidebar {
} else { } else {
url = file.url; url = file.url;
Service.url = url; Service.url = url;
Helper.getContent(file); Helper.getContent(file);
} }
} else if(typeof file === 'string') { } else if(typeof file === 'string') {
// go back // go back
......
...@@ -2,6 +2,8 @@ let RepoStore = { ...@@ -2,6 +2,8 @@ let RepoStore = {
service: '', service: '',
editor: '', editor: '',
sidebar: '', sidebar: '',
editButton: '',
editMode: false,
isTree: false, isTree: false,
prevURL: '', prevURL: '',
trees: [], trees: [],
...@@ -19,9 +21,14 @@ let RepoStore = { ...@@ -19,9 +21,14 @@ let RepoStore = {
name: 'loading...', name: 'loading...',
plain: '', plain: '',
size: 0, size: 0,
url: '' url: '',
raw: false,
newContent: '',
changed: false
}, },
activeFileIndex: 0,
activeLine: 0, activeLine: 0,
activeFileLabel: 'Raw',
files: [], files: [],
binary: false, binary: false,
binaryMimeType: '', binaryMimeType: '',
......
...@@ -4,7 +4,7 @@ let RepoTab = { ...@@ -4,7 +4,7 @@ let RepoTab = {
template: ` template: `
<li> <li>
<a href='#' @click.prevent='xClicked(tab)' v-if='!tab.loading'> <a href='#' @click.prevent='xClicked(tab)' v-if='!tab.loading'>
<i class='fa fa-times' :class="{'fa-times':saved, 'dot-circle-o': !saved}"></i> <i class='fa' :class="changedClass"></i>
</a> </a>
<a href='#' v-if='!tab.loading' :title='tab.url' @click.prevent='tabClicked(tab)'>{{tab.name}}</a> <a href='#' v-if='!tab.loading' :title='tab.url' @click.prevent='tabClicked(tab)'>{{tab.name}}</a>
<i v-if='tab.loading' class='fa fa-spinner fa-spin'></i> <i v-if='tab.loading' class='fa fa-spinner fa-spin'></i>
...@@ -16,12 +16,23 @@ let RepoTab = { ...@@ -16,12 +16,23 @@ let RepoTab = {
saved: true, saved: true,
}, },
computed: {
changedClass() {
const tabChangedObj = {
'fa-times': !this.tab.changed,
'fa-circle': this.tab.changed
};
return tabChangedObj
}
},
methods: { methods: {
tabClicked(file) { tabClicked(file) {
RepoHelper.setActiveFile(file); RepoHelper.setActiveFile(file);
}, },
xClicked(file) { xClicked(file) {
if(file.changed) return;
RepoHelper.removeFromOpenedFiles(file); RepoHelper.removeFromOpenedFiles(file);
} }
} }
......
...@@ -31,7 +31,7 @@ header { ...@@ -31,7 +31,7 @@ header {
.monaco-editor.vs { .monaco-editor.vs {
.line-numbers{ .line-numbers{
cursor: pointer; cursor: pointer;
&:hover { &:hover {
text-decoration: underline; text-decoration: underline;
} }
...@@ -42,6 +42,15 @@ header { ...@@ -42,6 +42,15 @@ header {
} }
} }
&.edit-mode {
.monaco-editor.vs {
.cursor {
background: #000;
border-color: #000;
}
}
}
#tabs { #tabs {
height: 41px; height: 41px;
border-bottom: 1px solid $white-normal; border-bottom: 1px solid $white-normal;
...@@ -70,10 +79,11 @@ header { ...@@ -70,10 +79,11 @@ header {
color: black; color: black;
} }
i.fa.fa-times { i.fa.fa-times, i.fa.fa-circle {
float: right; float: right;
margin-top: 3px; margin-top: 3px;
margin-left: 15px; margin-left: 15px;
color: $gray-darkest;
} }
} }
} }
...@@ -101,6 +111,11 @@ header { ...@@ -101,6 +111,11 @@ header {
} }
} }
#commit-area {
background: $gray-light;
padding: 20px;
}
#view-toggler { #view-toggler {
height: 41px; height: 41px;
position: relative; position: relative;
......
...@@ -23,6 +23,47 @@ ...@@ -23,6 +23,47 @@
#binary-viewer{ "v-if" => "binary" } #binary-viewer{ "v-if" => "binary" }
%img{"v-if" => "binaryTypes.png", ":src" => "pngBlobWithDataURI"} %img{"v-if" => "binaryTypes.png", ":src" => "pngBlobWithDataURI"}
%div{"v-if" => "binaryTypes.markdown", "v-html" => "activeFile.html"} %div{"v-if" => "binaryTypes.markdown", "v-html" => "activeFile.html"}
#commit-area{"v-if" => "changedFiles.length"}
%form.form-horizontal
%fieldset
.form-group
%label.col-md-4.control-label Staged files ({{changedFiles.length}})
.col-md-4
%ul.list-unstyled
%li{"v-for" => "file in changedFiles"}
%span.help-block
{{file.url}}
/ Textarea
.form-group
%label.col-md-4.control-label{:for => "commit-message"} Commit message
.col-md-4
%textarea#commit-message.form-control{:name => "commit-message"} Updating README.md
/ Button Drop Down
.form-group
%label.col-md-4.control-label{:for => "target-branch"} Target branch
.col-md-4
.input-group
.input-group-btn
%button.btn.btn-default.dropdown-toggle{"data-toggle" => "dropdown", :type => "button"}
Action
=icon "caret-down"
%ul.dropdown-menu.pull-right
%li
%a{:href => "#"} Target branch
%li
%a{:href => "#"} Create my own branch
%input#target-branch.form-control{:name => "target-branch", :placeholder => "placeholder", :type => "text"}/
/ Multiple Checkboxes
.form-group
%label.col-md-4.control-label{:for => "checkboxes"}
.col-md-4
.checkbox
%label{:for => "checkboxes-0"}
%input#checkboxes-0{:name => "checkboxes", :type => "checkbox", :value => "1"}/
Start a new merge request with these changes
- if can_edit_tree? - if can_edit_tree?
= render 'projects/blob/upload', title: _('Upload New File'), placeholder: _('Upload New File'), button_title: _('Upload file'), form_path: namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post = render 'projects/blob/upload', title: _('Upload New File'), placeholder: _('Upload New File'), button_title: _('Upload file'), form_path: namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post
= render 'projects/blob/new_dir' = render 'projects/blob/new_dir'
......
.tree-controls .tree-controls
%a.btn.btn-default.editable-mode{"href"=>"#"} %a.btn.btn-default.btn-grouped#editable-mode{"href"=>"#", "@click.prevent" => "editClicked", "v-cloak" => 1}
Edit mode %i{":class" => "buttonIcon"}
%span {{buttonLabel}}
= render 'projects/find_file_link' = render 'projects/find_file_link'
= render 'projects/buttons/download', project: @project, ref: @ref = render 'projects/buttons/download', project: @project, ref: @ref
......
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