Commit a76cd883 authored by Phil Hughes's avatar Phil Hughes

Added IDE commit panel

Closes #40041
parent 882dac68
<script>
import icon from '../../../vue_shared/components/icon.vue';
import listItem from './list_item.vue';
import listCollapsed from './list_collapsed.vue';
export default {
components: {
icon,
listItem,
listCollapsed,
},
props: {
title: {
type: String,
required: true,
},
fileList: {
type: Array,
required: true,
},
collapsed: {
type: Boolean,
required: true,
},
},
methods: {
toggleCollapsed() {
this.$emit('toggleCollapsed');
},
},
};
</script>
<template>
<div class="multi-file-commit-panel-section">
<header
class="multi-file-commit-panel-header"
:class="{
'is-collapsed': collapsed,
}"
>
<icon
name="list-bulleted"
:size="18"
css-classes="append-right-default"
/>
<template v-if="!collapsed">
{{ title }}
<button
type="button"
class="btn btn-transparent multi-file-commit-panel-collapse-btn"
@click="toggleCollapsed"
>
<i
aria-hidden="true"
class="fa fa-angle-double-right"
>
</i>
</button>
</template>
</header>
<div class="multi-file-commit-list">
<list-collapsed
v-if="collapsed"
:file-list="fileList"
/>
<template v-else>
<ul
v-if="fileList.length"
class="list-unstyled append-bottom-0"
>
<li
v-for="file in fileList"
:key="file.key"
>
<list-item
:file="file"
/>
</li>
</ul>
<div
v-else
class="help-block prepend-top-0"
>
No changes
</div>
</template>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import icon from '../../../vue_shared/components/icon.vue';
export default {
components: {
icon,
},
props: {
fileList: {
type: Array,
required: true,
},
},
computed: {
...mapGetters([
'addedFiles',
'modifiedFiles',
]),
},
};
</script>
<template>
<div
class="multi-file-commit-list-collapsed text-center"
>
<icon
name="file-addition"
:size="18"
css-classes="multi-file-addition append-bottom-10"
/>
{{ addedFiles.length }}
<icon
name="file-modified"
:size="18"
css-classes="multi-file-modified prepend-top-10 append-bottom-10"
/>
{{ modifiedFiles.length }}
</div>
</template>
<script>
import icon from '../../../vue_shared/components/icon.vue';
export default {
components: {
icon,
},
props: {
file: {
type: Object,
required: true,
},
},
computed: {
iconName() {
return this.file.tempFile ? 'file-addition' : 'file-modifed';
},
iconClass() {
return `multi-file-${this.file.tempFile ? 'addition' : 'modified'} append-right-8`;
},
},
};
</script>
<template>
<div class="multi-file-commit-list-item">
<icon
:name="iconName"
:size="16"
:css-classes="iconClass"
/>
<span class="multi-file-commit-list-path">
{{ file.path }}
</span>
</div>
</template>
...@@ -40,20 +40,24 @@ export default { ...@@ -40,20 +40,24 @@ export default {
</script> </script>
<template> <template>
<div class="repository-view"> <div
<div class="tree-content-holder" :class="{'tree-content-holder-mini' : isCollapsed}"> class="multi-file"
:class="{
'is-collapsed': isCollapsed
}"
>
<repo-sidebar/> <repo-sidebar/>
<div <div
v-if="isCollapsed" v-if="isCollapsed"
class="panel-right" class="multi-file-edit-pane"
> >
<repo-tabs/> <repo-tabs />
<component <component
class="multi-file-edit-pane-content"
:is="currentBlobView" :is="currentBlobView"
/> />
<repo-file-buttons/> <repo-file-buttons />
</div>
</div> </div>
<repo-commit-section v-if="changedFiles.length" /> <repo-commit-section />
</div> </div>
</template> </template>
<script> <script>
import { mapGetters, mapState, mapActions } from 'vuex'; import { mapGetters, mapState, mapActions } from 'vuex';
import tooltip from '../../vue_shared/directives/tooltip';
import icon from '../../vue_shared/components/icon.vue';
import PopupDialog from '../../vue_shared/components/popup_dialog.vue'; import PopupDialog from '../../vue_shared/components/popup_dialog.vue';
import { n__ } from '../../locale'; import commitFilesList from './commit_sidebar/list.vue';
export default { export default {
components: { components: {
PopupDialog, PopupDialog,
icon,
commitFilesList,
},
directives: {
tooltip,
}, },
data() { data() {
return { return {
...@@ -13,6 +20,7 @@ export default { ...@@ -13,6 +20,7 @@ export default {
submitCommitsLoading: false, submitCommitsLoading: false,
startNewMR: false, startNewMR: false,
commitMessage: '', commitMessage: '',
collapsed: true,
}; };
}, },
computed: { computed: {
...@@ -23,10 +31,10 @@ export default { ...@@ -23,10 +31,10 @@ export default {
'changedFiles', 'changedFiles',
]), ]),
commitButtonDisabled() { commitButtonDisabled() {
return !this.commitMessage || this.submitCommitsLoading; return !this.commitMessage || this.submitCommitsLoading || !this.changedFiles.length;
}, },
commitButtonText() { commitMessageCount() {
return n__('Commit %d file', 'Commit %d files', this.changedFiles.length); return this.commitMessage.length;
}, },
}, },
methods: { methods: {
...@@ -77,12 +85,20 @@ export default { ...@@ -77,12 +85,20 @@ export default {
this.submitCommitsLoading = false; this.submitCommitsLoading = false;
}); });
}, },
toggleCollapsed() {
this.collapsed = !this.collapsed;
},
}, },
}; };
</script> </script>
<template> <template>
<div id="commit-area"> <div
class="multi-file-commit-panel"
:class="{
'is-collapsed': collapsed,
}"
>
<popup-dialog <popup-dialog
v-if="showNewBranchDialog" v-if="showNewBranchDialog"
:primary-button-label="__('Create new branch')" :primary-button-label="__('Create new branch')"
...@@ -92,78 +108,71 @@ export default { ...@@ -92,78 +108,71 @@ export default {
@toggle="showNewBranchDialog = false" @toggle="showNewBranchDialog = false"
@submit="makeCommit(true)" @submit="makeCommit(true)"
/> />
<button
v-if="collapsed"
type="button"
class="btn btn-transparent multi-file-commit-panel-collapse-btn is-collapsed prepend-top-10 append-bottom-10"
@click="toggleCollapsed"
>
<i
aria-hidden="true"
class="fa fa-angle-double-left"
>
</i>
</button>
<commit-files-list
title="Staged"
:file-list="changedFiles"
:collapsed="collapsed"
@toggleCollapsed="toggleCollapsed"
/>
<form <form
class="form-horizontal" class="form-horizontal multi-file-commit-form"
@submit.prevent="tryCommit()"> @submit.prevent="tryCommit"
<fieldset> v-if="!collapsed"
<div class="form-group"> >
<label class="col-md-4 control-label staged-files"> <div class="multi-file-commit-fieldset">
Staged files ({{changedFiles.length}})
</label>
<div class="col-md-6">
<ul class="list-unstyled changed-files">
<li
v-for="(file, index) in changedFiles"
:key="index">
<span class="help-block">
{{ file.path }}
</span>
</li>
</ul>
</div>
</div>
<div class="form-group">
<label
class="col-md-4 control-label"
for="commit-message">
Commit message
</label>
<div class="col-md-6">
<textarea <textarea
id="commit-message" class="form-control multi-file-commit-message"
class="form-control"
name="commit-message" name="commit-message"
v-model="commitMessage"> v-model="commitMessage"
placeholder="Commit message"
>
</textarea> </textarea>
</div> </div>
</div> <div class="multi-file-commit-fieldset">
<div class="form-group target-branch">
<label <label
class="col-md-4 control-label" v-tooltip
for="target-branch"> title="Create a new merge request with these changes"
Target branch data-container="body"
data-placement="top"
>
<input
type="checkbox"
v-model="startNewMR"
/>
Merge Request
</label> </label>
<div class="col-md-6">
<span class="help-block">
{{currentBranch}}
</span>
</div>
</div>
<div class="col-md-offset-4 col-md-6">
<button <button
type="submit" type="submit"
:disabled="commitButtonDisabled" :disabled="commitButtonDisabled"
class="btn btn-success"> class="btn btn-default btn-sm append-right-10 prepend-left-10"
>
<i <i
v-if="submitCommitsLoading" v-if="submitCommitsLoading"
class="js-commit-loading-icon fa fa-spinner fa-spin" class="js-commit-loading-icon fa fa-spinner fa-spin"
aria-hidden="true" aria-hidden="true"
aria-label="loading"> aria-label="loading"
>
</i> </i>
<span class="commit-summary"> Commit
{{ commitButtonText }}
</span>
</button> </button>
</div> <div
<div class="col-md-offset-4 col-md-6"> class="multi-file-commit-message-count"
<div class="checkbox"> >
<label> {{ commitMessageCount }}
<input type="checkbox" v-model="startNewMR">
<span>Start a <strong>new merge request</strong> with these changes</span>
</label>
</div> </div>
</div> </div>
</fieldset>
</form> </form>
</div> </div>
</template> </template>
...@@ -85,12 +85,11 @@ ...@@ -85,12 +85,11 @@
</td> </td>
<template v-if="!isCollapsed && !isSubmodule"> <template v-if="!isCollapsed && !isSubmodule">
<td class="hidden-sm hidden-xs"> <td class="multi-file-table-col-commit-message hidden-sm hidden-xs">
<a <a
v-if="file.lastCommit.message" v-if="file.lastCommit.message"
@click.stop @click.stop
:href="file.lastCommit.url" :href="file.lastCommit.url"
class="commit-message"
> >
{{ file.lastCommit.message }} {{ file.lastCommit.message }}
</a> </a>
......
...@@ -22,12 +22,12 @@ export default { ...@@ -22,12 +22,12 @@ export default {
<template> <template>
<div <div
v-if="showButtons" v-if="showButtons"
class="repo-file-buttons" class="multi-file-editor-btn-group"
> >
<a <a
:href="activeFile.rawPath" :href="activeFile.rawPath"
target="_blank" target="_blank"
class="btn btn-default raw" class="btn btn-default btn-sm raw"
rel="noopener noreferrer"> rel="noopener noreferrer">
{{ rawDownloadButtonLabel }} {{ rawDownloadButtonLabel }}
</a> </a>
...@@ -38,17 +38,17 @@ export default { ...@@ -38,17 +38,17 @@ export default {
aria-label="File actions"> aria-label="File actions">
<a <a
:href="activeFile.blamePath" :href="activeFile.blamePath"
class="btn btn-default blame"> class="btn btn-default btn-sm blame">
Blame Blame
</a> </a>
<a <a
:href="activeFile.commitsPath" :href="activeFile.commitsPath"
class="btn btn-default history"> class="btn btn-default btn-sm history">
History History
</a> </a>
<a <a
:href="activeFile.permalink" :href="activeFile.permalink"
class="btn btn-default permalink"> class="btn btn-default btn-sm permalink">
Permalink Permalink
</a> </a>
</div> </div>
......
...@@ -32,10 +32,12 @@ export default { ...@@ -32,10 +32,12 @@ export default {
</script> </script>
<template> <template>
<div class="blob-viewer-container"> <div>
<div <div
v-if="!activeFile.renderError" v-if="!activeFile.renderError"
v-html="activeFile.html"> v-html="activeFile.html"
class="multi-file-preview-holder"
>
</div> </div>
<div <div
v-else-if="activeFile.tempFile" v-else-if="activeFile.tempFile"
......
...@@ -44,17 +44,13 @@ export default { ...@@ -44,17 +44,13 @@ export default {
</script> </script>
<template> <template>
<div id="sidebar" :class="{'sidebar-mini' : isCollapsed}"> <div class="ide-file-list">
<table class="table"> <table class="table">
<thead> <thead>
<tr> <tr>
<th <th
v-if="isCollapsed" v-if="isCollapsed"
class="repo-file-options title"
> >
<strong class="clgray">
{{ projectName }}
</strong>
</th> </th>
<template v-else> <template v-else>
<th class="name multi-file-table-col-name"> <th class="name multi-file-table-col-name">
...@@ -79,7 +75,7 @@ export default { ...@@ -79,7 +75,7 @@ export default {
:key="n" :key="n"
/> />
<repo-file <repo-file
v-for="(file, index) in treeList" v-for="file in treeList"
:key="file.key" :key="file.key"
:file="file" :file="file"
/> />
......
...@@ -36,27 +36,32 @@ export default { ...@@ -36,27 +36,32 @@ export default {
<template> <template>
<li <li
:class="{ active : tab.active }"
@click="setFileActive(tab)" @click="setFileActive(tab)"
> >
<button <button
type="button" type="button"
class="close-btn" class="multi-file-tab-close"
@click.stop.prevent="closeFile({ file: tab })" @click.stop.prevent="closeFile({ file: tab })"
:aria-label="closeLabel"> :aria-label="closeLabel"
:class="{
'modified': tab.changed,
}"
:disabled="tab.changed"
>
<i <i
class="fa" class="fa"
:class="changedClass" :class="changedClass"
aria-hidden="true"> aria-hidden="true"
>
</i> </i>
</button> </button>
<a <div
href="#" class="multi-file-tab"
class="repo-tab" :class="{active : tab.active }"
:title="tab.url" :title="tab.url"
@click.prevent.stop="setFileActive(tab)"> >
{{tab.name}} {{ tab.name }}
</a> </div>
</li> </li>
</template> </template>
...@@ -16,14 +16,12 @@ ...@@ -16,14 +16,12 @@
<template> <template>
<ul <ul
id="tabs" class="multi-file-tabs list-unstyled append-bottom-0"
class="list-unstyled"
> >
<repo-tab <repo-tab
v-for="tab in openFiles" v-for="tab in openFiles"
:key="tab.id" :key="tab.id"
:tab="tab" :tab="tab"
/> />
<li class="tabs-divider" />
</ul> </ul>
</template> </template>
...@@ -34,3 +34,7 @@ export const canEditFile = (state) => { ...@@ -34,3 +34,7 @@ export const canEditFile = (state) => {
openedFiles.length && openedFiles.length &&
(currentActiveFile && !currentActiveFile.renderError && !currentActiveFile.binary); (currentActiveFile && !currentActiveFile.renderError && !currentActiveFile.binary);
}; };
export const addedFiles = state => changedFiles(state).filter(f => f.tempFile);
export const modifiedFiles = state => changedFiles(state).filter(f => !f.tempFile);
...@@ -35,126 +35,82 @@ ...@@ -35,126 +35,82 @@
} }
} }
.repository-view { .multi-file {
border: 1px solid $border-color;
border-radius: $border-radius-default;
color: $almost-black;
.code.white pre .hll {
background-color: $well-light-border !important;
}
.tree-content-holder {
display: -webkit-flex;
display: flex; display: flex;
min-height: 300px; height: calc(100vh - 145px);
} border-top: 1px solid $white-dark;
border-bottom: 1px solid $white-dark;
.tree-content-holder-mini { &.is-collapsed {
height: 100vh; .ide-file-list {
max-width: 250px;
overflow: scroll;
} }
.panel-right {
display: -webkit-flex;
display: flex;
-webkit-flex-direction: column;
flex-direction: column;
width: 80%;
height: 100%;
.monaco-editor.vs {
.current-line {
border: 0;
background: $well-light-border;
} }
}
.line-numbers { .ide-file-list {
cursor: pointer; flex: 1;
&:hover { .file {
text-decoration: underline; cursor: pointer;
}
}
} }
.blob-no-preview { a {
.vertical-center { color: $gl-text-color;
justify-content: center;
width: 100%;
}
} }
}
&.blob-editor-container { .multi-file-table-col-name,
.multi-file-table-col-commit-message {
white-space: nowrap;
overflow: hidden; overflow: hidden;
} text-overflow: ellipsis;
max-width: 0;
.blob-viewer-container { }
-webkit-flex: 1;
flex: 1;
overflow: auto;
> div,
.file-content:not(.wiki) {
display: flex;
}
> div, .multi-file-table-col-name {
.file-content, width: 350px;
.blob-viewer, }
.line-number,
.blob-content,
.code {
min-height: 100%;
width: 100%;
}
.line-numbers { .multi-file-table-col-commit-message {
min-width: 44px; width: 450px;
} }
.blob-content { .multi-file-edit-pane {
display: flex;
flex-direction: column;
flex: 1; flex: 1;
overflow-x: auto; border-left: 1px solid $white-dark;
} overflow: hidden;
} }
#tabs { .multi-file-tabs {
position: relative;
flex-shrink: 0;
display: flex; display: flex;
width: 100%; overflow: scroll;
padding-left: 0; background-color: $white-normal;
margin-bottom: 0; box-shadow: inset 0 -1px $white-dark;
white-space: nowrap;
overflow-y: hidden;
overflow-x: auto;
li { > li {
position: relative; position: relative;
background: $gray-normal; }
padding: #{$gl-padding / 2} $gl-padding; }
.multi-file-tab {
padding: ($gl-padding / 2) ($gl-padding + 12) ($gl-padding / 2) $gl-padding;
background-color: $gray-normal;
border-right: 1px solid $white-dark; border-right: 1px solid $white-dark;
border-bottom: 1px solid $white-dark; border-bottom: 1px solid $white-dark;
cursor: pointer; cursor: pointer;
@include str-truncated(150px);
&.active { &.active {
background: $white-light; background-color: $white-light;
border-bottom: 0; border-bottom-color: $white-light;
}
a {
@include str-truncated(100px);
color: $gl-text-color;
vertical-align: middle;
text-decoration: none;
margin-right: 12px;
&:focus {
outline: none;
}
} }
}
.close-btn { .multi-file-tab-close {
position: absolute; position: absolute;
right: 8px; right: 8px;
top: 50%; top: 50%;
...@@ -162,143 +118,157 @@ ...@@ -162,143 +118,157 @@
background: none; background: none;
border: 0; border: 0;
font-size: $gl-font-size; font-size: $gl-font-size;
color: $gray-darkest;
transform: translateY(-50%); transform: translateY(-50%);
}
.close-icon:hover { &:not(.modified):hover,
&:not(.modified):focus {
color: $hint-color; color: $hint-color;
} }
.close-icon, &.modified {
.unsaved-icon { color: $indigo-700;
color: $gray-darkest;
}
.unsaved-icon {
color: $brand-success;
} }
}
&.tabs-divider { .multi-file-edit-pane-content {
width: 100%; flex: 1;
background-color: $white-light; height: 0;
border-right: 0; }
border-top-right-radius: 2px;
}
}
}
.repo-file-buttons { .multi-file-editor-btn-group {
background-color: $white-light; padding: $grid-size;
padding: 5px 10px; border-top: 1px solid $white-dark;
border-top: 1px solid $white-normal; }
}
#binary-viewer { // Not great, but this is to deal with our current output
height: 80vh; .multi-file-preview-holder {
overflow: auto; height: 100%;
margin: 0; overflow: scroll;
& {
.blob-viewer { .blob-viewer {
padding-top: 20px;
padding-left: 20px;
}
.binary-unknown {
text-align: center;
padding-top: 100px;
background: $gray-light;
height: 100%; height: 100%;
font-size: 17px;
span {
display: block;
}
}
}
} }
#commit-area { .file-content.code {
background: $gray-light; display: flex;
padding: 20px;
.help-block { i {
padding-top: 7px; margin-left: -10px;
margin-top: 0;
} }
} }
#view-toggler { .line-numbers {
height: 41px; min-width: 50px;
position: relative;
display: block;
border-bottom: 1px solid $white-normal;
background: $white-light;
margin-top: -5px;
} }
#binary-viewer { .file-content,
img { .line-numbers,
max-width: 100%; .blob-content,
.code {
min-height: 100%;
} }
} }
}
#sidebar { .multi-file-commit-panel {
flex: 1; display: flex;
flex-direction: column;
height: 100%; height: 100%;
width: 290px;
padding: $gl-padding;
background-color: $gray-light;
border-left: 1px solid $white-dark;
&.sidebar-mini { &.is-collapsed {
width: 20%; width: 60px;
border-right: 1px solid $white-normal; padding: 0;
overflow: auto;
}
.table {
margin-bottom: 0;
} }
}
tr { .multi-file-commit-panel-section {
.repo-file-options { display: flex;
padding: 2px 16px; flex-direction: column;
width: 100%; flex: 1;
} }
.title { .multi-file-commit-panel-header {
font-size: 10px; display: flex;
text-transform: uppercase; align-items: center;
white-space: nowrap; padding: 0 0 12px 0;
overflow: hidden; margin-bottom: 12px;
text-overflow: ellipsis; border-bottom: 1px solid $white-dark;
vertical-align: middle;
}
.file-icon { &.is-collapsed {
margin-right: 5px; border-bottom: 1px solid $white-dark;
}
td { svg {
white-space: nowrap; margin-left: auto;
margin-right: auto;
} }
} }
}
.file { .multi-file-commit-panel-collapse-btn {
cursor: pointer; padding-top: 0;
} padding-bottom: 0;
margin-left: auto;
font-size: 20px;
a { &.is-collapsed {
@include str-truncated(250px); margin-right: auto;
color: $almost-black;
} }
}
.multi-file-commit-list {
flex: 1;
overflow: scroll;
}
.multi-file-commit-list-item {
display: flex;
align-items: center;
}
.multi-file-addition {
fill: $green-500;
}
.multi-file-modified {
fill: $orange-500;
}
.multi-file-commit-list-collapsed {
display: flex;
flex-direction: column;
> svg {
margin-left: auto;
margin-right: auto;
} }
} }
.render-error { .multi-file-commit-list-path {
min-height: calc(100vh - 62px); @include str-truncated(100%);
}
p { .multi-file-commit-form {
width: 100%; padding-top: 12px;
border-top: 1px solid $white-dark;
}
.multi-file-commit-fieldset {
display: flex;
align-items: center;
padding-bottom: 12px;
.btn {
flex: 1;
} }
} }
.multi-file-table-col-name { .multi-file-commit-message.form-control {
width: 350px; height: 80px;
resize: none;
} }
...@@ -11,6 +11,6 @@ ...@@ -11,6 +11,6 @@
= webpack_bundle_tag 'common_vue' = webpack_bundle_tag 'common_vue'
= webpack_bundle_tag 'repo' = webpack_bundle_tag 'repo'
%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] } %div{ class: [(container_class unless show_new_repo?), ("limit-container-width" unless fluid_layout)] }
= render 'projects/last_push' = render 'projects/last_push'
= render 'projects/files', commit: @last_commit, project: @project, ref: @ref, content_url: project_tree_path(@project, @id) = render 'projects/files', commit: @last_commit, project: @project, ref: @ref, content_url: project_tree_path(@project, @id)
- @no_container = true;
#repo{ data: { root: @path.empty?.to_s, #repo{ data: { root: @path.empty?.to_s,
root_url: project_tree_path(project), root_url: project_tree_path(project),
url: content_url, url: content_url,
......
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