Add left links sidebar to IDE
[ci skip]
This commit is contained in:
parent
d8dd75ca77
commit
bdc437fc76
12 changed files with 233 additions and 155 deletions
109
app/assets/javascripts/ide/components/activity_bar.vue
Normal file
109
app/assets/javascripts/ide/components/activity_bar.vue
Normal file
|
@ -0,0 +1,109 @@
|
|||
<script>
|
||||
import { mapActions, mapGetters, mapState } from 'vuex';
|
||||
import Icon from '~/vue_shared/components/icon.vue';
|
||||
import ExternalLinks from './ide_external_links.vue';
|
||||
import { ActivityBarViews } from '../stores/state';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Icon,
|
||||
ExternalLinks,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['currentProject']),
|
||||
...mapState(['currentActivityView']),
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['updateActivityBarView']),
|
||||
},
|
||||
ActivityBarViews,
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav class="ide-activity-bar">
|
||||
<ul class="list-unstyled">
|
||||
<li>
|
||||
<external-links
|
||||
class="ide-activity-bar-link"
|
||||
:project-url="currentProject.web_url"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
class="ide-sidebar-link ide-activity-bar-link"
|
||||
:class="{
|
||||
active: currentActivityView === $options.ActivityBarViews.edit
|
||||
}"
|
||||
@click.prevent="updateActivityBarView($options.ActivityBarViews.edit)"
|
||||
>
|
||||
<icon
|
||||
:size="16"
|
||||
name="code"
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
class="ide-sidebar-link ide-activity-bar-link"
|
||||
:class="{
|
||||
active: currentActivityView === $options.ActivityBarViews.commit
|
||||
}"
|
||||
@click.prevent="updateActivityBarView($options.ActivityBarViews.commit)"
|
||||
>
|
||||
<icon
|
||||
:size="16"
|
||||
name="commit"
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.ide-activity-bar {
|
||||
position: relative;
|
||||
flex: 0 0 60px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.ide-activity-bar-link {
|
||||
position: relative;
|
||||
height: 55px;
|
||||
margin: 2.5px 0;
|
||||
color: #707070;
|
||||
border-top: 1px solid transparent;
|
||||
border-bottom: 1px solid transparent;
|
||||
}
|
||||
|
||||
.ide-activity-bar-link svg {
|
||||
margin: 0 auto;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.ide-activity-bar-link.active {
|
||||
color: #4b4ba3;
|
||||
background-color: #fff;
|
||||
border-top: 1px solid #eaeaea;
|
||||
border-bottom: 1px solid #eaeaea;
|
||||
box-shadow: inset 3px 0 #4b4ba3;
|
||||
}
|
||||
|
||||
a.ide-sidebar-link.ide-activity-bar-link.active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: -1px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 1px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.ide-activity-bar-link:hover {
|
||||
color: #4b4ba3;
|
||||
background-color: #fff;
|
||||
}
|
||||
</style>
|
|
@ -100,9 +100,9 @@ export default {
|
|||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<ide-contextbar
|
||||
<!-- <ide-contextbar
|
||||
:no-changes-state-svg-path="noChangesStateSvgPath"
|
||||
:committed-state-svg-path="committedStateSvgPath"
|
||||
/>
|
||||
/> -->
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -20,24 +20,15 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<nav
|
||||
class="ide-external-links"
|
||||
<a
|
||||
:href="goBackUrl"
|
||||
class="ide-sidebar-link"
|
||||
:aria-label="s__('IDE|Go back')"
|
||||
v-once
|
||||
>
|
||||
<p>
|
||||
<a
|
||||
:href="goBackUrl"
|
||||
class="ide-sidebar-link"
|
||||
>
|
||||
<icon
|
||||
:size="16"
|
||||
class="append-right-8"
|
||||
name="go-back"
|
||||
/>
|
||||
<span class="ide-external-links-text">
|
||||
{{ s__('Go back') }}
|
||||
</span>
|
||||
</a>
|
||||
</p>
|
||||
</nav>
|
||||
<icon
|
||||
:size="16"
|
||||
name="go-back"
|
||||
/>
|
||||
</a>
|
||||
</template>
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
<script>
|
||||
import ProjectAvatarImage from '~/vue_shared/components/project_avatar/image.vue';
|
||||
import Identicon from '../../vue_shared/components/identicon.vue';
|
||||
import BranchesTree from './ide_project_branches_tree.vue';
|
||||
import ExternalLinks from './ide_external_links.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BranchesTree,
|
||||
ExternalLinks,
|
||||
ProjectAvatarImage,
|
||||
Identicon,
|
||||
},
|
||||
props: {
|
||||
project: {
|
||||
|
@ -22,37 +16,6 @@ export default {
|
|||
|
||||
<template>
|
||||
<div class="projects-sidebar">
|
||||
<div class="context-header">
|
||||
<a
|
||||
:title="project.name"
|
||||
:href="project.web_url"
|
||||
>
|
||||
<div
|
||||
v-if="project.avatar_url"
|
||||
class="avatar-container s40 project-avatar"
|
||||
>
|
||||
<project-avatar-image
|
||||
class="avatar-container project-avatar"
|
||||
:link-href="project.path"
|
||||
:img-src="project.avatar_url"
|
||||
:img-alt="project.name"
|
||||
:img-size="40"
|
||||
/>
|
||||
</div>
|
||||
<identicon
|
||||
v-else
|
||||
size-class="s40"
|
||||
:entity-id="project.id"
|
||||
:entity-name="project.name"
|
||||
/>
|
||||
<div class="sidebar-context-title">
|
||||
{{ project.name }}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<external-links
|
||||
:project-url="project.web_url"
|
||||
/>
|
||||
<div class="multi-file-commit-panel-inner-scroll">
|
||||
<branches-tree
|
||||
v-for="branch in project.branches"
|
||||
|
|
|
@ -1,36 +1,41 @@
|
|||
<script>
|
||||
import { mapState, mapGetters } from 'vuex';
|
||||
import icon from '~/vue_shared/components/icon.vue';
|
||||
import panelResizer from '~/vue_shared/components/panel_resizer.vue';
|
||||
import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
|
||||
import projectTree from './ide_project_tree.vue';
|
||||
import ResizablePanel from './resizable_panel.vue';
|
||||
import { mapState, mapGetters } from 'vuex';
|
||||
import ProjectAvatarImage from '~/vue_shared/components/project_avatar/image.vue';
|
||||
import icon from '~/vue_shared/components/icon.vue';
|
||||
import panelResizer from '~/vue_shared/components/panel_resizer.vue';
|
||||
import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
|
||||
import Identicon from '../../vue_shared/components/identicon.vue';
|
||||
import projectTree from './ide_project_tree.vue';
|
||||
import ResizablePanel from './resizable_panel.vue';
|
||||
import ActivityBar from './activity_bar.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
projectTree,
|
||||
icon,
|
||||
panelResizer,
|
||||
skeletonLoadingContainer,
|
||||
ResizablePanel,
|
||||
},
|
||||
computed: {
|
||||
...mapState([
|
||||
'loading',
|
||||
]),
|
||||
...mapGetters([
|
||||
'projectsWithTrees',
|
||||
]),
|
||||
},
|
||||
};
|
||||
export default {
|
||||
components: {
|
||||
projectTree,
|
||||
icon,
|
||||
panelResizer,
|
||||
skeletonLoadingContainer,
|
||||
ResizablePanel,
|
||||
ActivityBar,
|
||||
ProjectAvatarImage,
|
||||
Identicon,
|
||||
},
|
||||
computed: {
|
||||
...mapState(['loading']),
|
||||
...mapGetters(['currentProjectWithTree', 'activityBarComponent']),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<resizable-panel
|
||||
:collapsible="false"
|
||||
:initial-width="290"
|
||||
:initial-width="340"
|
||||
side="left"
|
||||
>
|
||||
<activity-bar
|
||||
v-if="!loading"
|
||||
/>
|
||||
<div class="multi-file-commit-panel-inner">
|
||||
<template v-if="loading">
|
||||
<div
|
||||
|
@ -41,11 +46,40 @@
|
|||
<skeleton-loading-container />
|
||||
</div>
|
||||
</template>
|
||||
<project-tree
|
||||
v-for="project in projectsWithTrees"
|
||||
:key="project.id"
|
||||
:project="project"
|
||||
/>
|
||||
<template v-else>
|
||||
<div class="context-header">
|
||||
<a
|
||||
:title="currentProjectWithTree.name"
|
||||
:href="currentProjectWithTree.web_url"
|
||||
>
|
||||
<div
|
||||
v-if="currentProjectWithTree.avatar_url"
|
||||
class="avatar-container s40 project-avatar"
|
||||
>
|
||||
<project-avatar-image
|
||||
class="avatar-container project-avatar"
|
||||
:link-href="currentProjectWithTree.path"
|
||||
:img-src="currentProjectWithTree.avatar_url"
|
||||
:img-alt="currentProjectWithTree.name"
|
||||
:img-size="40"
|
||||
/>
|
||||
</div>
|
||||
<identicon
|
||||
v-else
|
||||
size-class="s40"
|
||||
:entity-id="currentProjectWithTree.id"
|
||||
:entity-name="currentProjectWithTree.name"
|
||||
/>
|
||||
<div class="sidebar-context-title">
|
||||
{{ currentProjectWithTree.name }}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<component
|
||||
:is="activityBarComponent"
|
||||
:project="currentProjectWithTree"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</resizable-panel>
|
||||
</template>
|
||||
|
|
|
@ -112,6 +112,10 @@ export const updateDelayViewerUpdated = ({ commit }, delay) => {
|
|||
commit(types.UPDATE_DELAY_VIEWER_CHANGE, delay);
|
||||
};
|
||||
|
||||
export const updateActivityBarView = ({ commit }, view) => {
|
||||
commit(types.UPDATE_ACTIVITY_BAR_VIEW, view);
|
||||
};
|
||||
|
||||
export * from './actions/tree';
|
||||
export * from './actions/file';
|
||||
export * from './actions/project';
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { ActivityBarViews } from './state';
|
||||
|
||||
export const activeFile = state => state.openFiles.find(file => file.active) || null;
|
||||
|
||||
export const addedFiles = state => state.changedFiles.filter(f => f.tempFile);
|
||||
|
@ -21,6 +23,18 @@ export const projectsWithTrees = state =>
|
|||
};
|
||||
});
|
||||
|
||||
export const currentProjectWithTree = state => ({
|
||||
...state.projects[state.currentProjectId],
|
||||
branches: Object.keys(state.projects[state.currentProjectId].branches).map(branchId => {
|
||||
const branch = state.projects[state.currentProjectId].branches[branchId];
|
||||
|
||||
return {
|
||||
...branch,
|
||||
tree: state.trees[branch.treeId],
|
||||
};
|
||||
}),
|
||||
});
|
||||
|
||||
export const currentMergeRequest = state => {
|
||||
if (state.projects[state.currentProjectId]) {
|
||||
return state.projects[state.currentProjectId].mergeRequests[state.currentMergeRequestId];
|
||||
|
@ -28,6 +42,8 @@ export const currentMergeRequest = state => {
|
|||
return null;
|
||||
};
|
||||
|
||||
export const currentProject = state => state.projects[state.currentProjectId];
|
||||
|
||||
// eslint-disable-next-line no-confusing-arrow
|
||||
export const currentIcon = state =>
|
||||
state.rightPanelCollapsed ? 'angle-double-left' : 'angle-double-right';
|
||||
|
@ -35,3 +51,12 @@ export const currentIcon = state =>
|
|||
export const hasChanges = state => !!state.changedFiles.length;
|
||||
|
||||
export const hasMergeRequest = state => !!state.currentMergeRequestId;
|
||||
|
||||
export const activityBarComponent = state => {
|
||||
switch (state.currentActivityView) {
|
||||
case ActivityBarViews.edit:
|
||||
return 'project-tree';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -53,3 +53,5 @@ 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';
|
||||
|
||||
export const UPDATE_ACTIVITY_BAR_VIEW = 'UPDATE_ACTIVITY_BAR_VIEW';
|
||||
|
|
|
@ -95,6 +95,11 @@ export default {
|
|||
delayViewerUpdated,
|
||||
});
|
||||
},
|
||||
[types.UPDATE_ACTIVITY_BAR_VIEW](state, currentActivityView) {
|
||||
Object.assign(state, {
|
||||
currentActivityView,
|
||||
});
|
||||
},
|
||||
...projectMutations,
|
||||
...mergeRequestMutation,
|
||||
...fileMutations,
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
export const ActivityBarViews = {
|
||||
edit: 0,
|
||||
commit: 1,
|
||||
};
|
||||
|
||||
export default () => ({
|
||||
currentProjectId: '',
|
||||
currentBranchId: '',
|
||||
|
@ -17,4 +22,5 @@ export default () => ({
|
|||
entries: {},
|
||||
viewer: 'editor',
|
||||
delayViewerUpdated: false,
|
||||
currentActivityView: ActivityBarViews.edit,
|
||||
});
|
||||
|
|
|
@ -177,25 +177,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Web IDE
|
||||
.ide-sidebar-link {
|
||||
color: $color-200;
|
||||
background-color: $color-700;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: $color-500;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: $color-800;
|
||||
}
|
||||
}
|
||||
|
||||
.branch-container {
|
||||
border-left-color: $color-700;
|
||||
}
|
||||
|
||||
.branch-header-title {
|
||||
color: $color-700;
|
||||
}
|
||||
|
@ -343,9 +324,5 @@ body {
|
|||
.sidebar-top-level-items > li.active .badge {
|
||||
color: $theme-gray-900;
|
||||
}
|
||||
|
||||
.ide-sidebar-link {
|
||||
color: $white-light;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -420,27 +420,27 @@
|
|||
.multi-file-commit-panel {
|
||||
display: flex;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
width: 340px;
|
||||
padding: 0;
|
||||
background-color: $gray-light;
|
||||
padding-right: 3px;
|
||||
|
||||
.context-header {
|
||||
width: auto;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.projects-sidebar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
.context-header {
|
||||
width: auto;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.multi-file-commit-panel-inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.multi-file-commit-panel-inner-scroll {
|
||||
|
@ -448,29 +448,8 @@
|
|||
flex: 1;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
&.is-collapsed {
|
||||
width: 60px;
|
||||
|
||||
.multi-file-commit-list {
|
||||
padding-top: $gl-padding;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.multi-file-context-bar-icon {
|
||||
align-items: center;
|
||||
|
||||
svg {
|
||||
float: none;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.branch-container {
|
||||
border-left: 4px solid;
|
||||
margin-bottom: $gl-bar-padding;
|
||||
background-color: #fff;
|
||||
border-left: 1px solid #eaeaea;
|
||||
}
|
||||
|
||||
.branch-header {
|
||||
|
@ -491,17 +470,6 @@
|
|||
.branch-header-btns {
|
||||
padding: $gl-vert-padding $gl-padding;
|
||||
}
|
||||
|
||||
.left-collapse-btn {
|
||||
display: none;
|
||||
background: $gray-light;
|
||||
text-align: left;
|
||||
border-top: 1px solid $white-dark;
|
||||
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.multi-file-context-bar-icon {
|
||||
|
@ -822,12 +790,6 @@
|
|||
margin-left: 25px;
|
||||
}
|
||||
|
||||
.ide-external-links {
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ide-sidebar-link {
|
||||
padding: $gl-padding-8 $gl-padding;
|
||||
display: flex;
|
||||
|
|
Loading…
Reference in a new issue