diff options
author | Tim Zallmann <tzallmann@gitlab.com> | 2018-01-04 09:31:06 +0000 |
---|---|---|
committer | Filipa Lacerda <filipa@gitlab.com> | 2018-01-04 09:31:06 +0000 |
commit | 7fbb5addaf7c15c971f584ebeecb2c93773093c0 (patch) | |
tree | 1fdbd80a4d6b3b660a329751c693ab3e449aca54 | |
parent | 0e7e9e32b30bd3c310a769eaef91843b609697df (diff) | |
download | gitlab-ce-7fbb5addaf7c15c971f584ebeecb2c93773093c0.tar.gz |
Resolve "Resizable file list and commit panel"
10 files changed, 262 insertions, 5 deletions
diff --git a/app/assets/javascripts/ide/components/ide_context_bar.vue b/app/assets/javascripts/ide/components/ide_context_bar.vue index 5a08718e386..78c01272af6 100644 --- a/app/assets/javascripts/ide/components/ide_context_bar.vue +++ b/app/assets/javascripts/ide/components/ide_context_bar.vue @@ -2,11 +2,18 @@ import { mapGetters, mapState, mapActions } from 'vuex'; import repoCommitSection from './repo_commit_section.vue'; import icon from '../../vue_shared/components/icon.vue'; +import panelResizer from '../../vue_shared/components/panel_resizer.vue'; export default { + data() { + return { + width: 290, + }; + }, components: { repoCommitSection, icon, + panelResizer, }, computed: { ...mapState([ @@ -18,10 +25,20 @@ export default { currentIcon() { return this.rightPanelCollapsed ? 'angle-double-left' : 'angle-double-right'; }, + maxSize() { + return window.innerWidth / 2; + }, + panelStyle() { + if (!this.rightPanelCollapsed) { + return { width: `${this.width}px` }; + } + return {}; + }, }, methods: { ...mapActions([ 'setPanelCollapsedStatus', + 'setResizingStatus', ]), toggleCollapsed() { this.setPanelCollapsedStatus({ @@ -29,6 +46,12 @@ export default { collapsed: !this.rightPanelCollapsed, }); }, + resizingStarted() { + this.setResizingStatus(true); + }, + resizingEnded() { + this.setResizingStatus(false); + }, }, }; </script> @@ -39,6 +62,7 @@ export default { :class="{ 'is-collapsed': rightPanelCollapsed, }" + :style="panelStyle" > <div class="multi-file-commit-panel-section"> @@ -71,5 +95,14 @@ export default { <repo-commit-section class=""/> </div> + <panel-resizer + :size.sync="width" + :enabled="!rightPanelCollapsed" + :start-size="290" + :min-size="200" + :max-size="maxSize" + @resize-start="resizingStarted" + @resize-end="resizingEnded" + side="left"/> </div> </template> diff --git a/app/assets/javascripts/ide/components/ide_side_bar.vue b/app/assets/javascripts/ide/components/ide_side_bar.vue index 535398d98c2..269f300a04d 100644 --- a/app/assets/javascripts/ide/components/ide_side_bar.vue +++ b/app/assets/javascripts/ide/components/ide_side_bar.vue @@ -2,11 +2,18 @@ import { mapState, mapActions } from 'vuex'; import projectTree from './ide_project_tree.vue'; import icon from '../../vue_shared/components/icon.vue'; +import panelResizer from '../../vue_shared/components/panel_resizer.vue'; export default { + data() { + return { + width: 290, + }; + }, components: { projectTree, icon, + panelResizer, }, computed: { ...mapState([ @@ -16,10 +23,20 @@ export default { currentIcon() { return this.leftPanelCollapsed ? 'angle-double-right' : 'angle-double-left'; }, + maxSize() { + return window.innerWidth / 2; + }, + panelStyle() { + if (!this.leftPanelCollapsed) { + return { width: `${this.width}px` }; + } + return {}; + }, }, methods: { ...mapActions([ 'setPanelCollapsedStatus', + 'setResizingStatus', ]), toggleCollapsed() { this.setPanelCollapsedStatus({ @@ -27,6 +44,12 @@ export default { collapsed: !this.leftPanelCollapsed, }); }, + resizingStarted() { + this.setResizingStatus(true); + }, + resizingEnded() { + this.setResizingStatus(false); + }, }, }; </script> @@ -37,6 +60,7 @@ export default { :class="{ 'is-collapsed': leftPanelCollapsed, }" + :style="panelStyle" > <div class="multi-file-commit-panel-inner"> <project-tree @@ -58,5 +82,14 @@ export default { class="collapse-text" >Collapse sidebar</span> </button> + <panel-resizer + :size.sync="width" + :enabled="!leftPanelCollapsed" + :start-size="290" + :min-size="200" + :max-size="maxSize" + @resize-start="resizingStarted" + @resize-end="resizingEnded" + side="right"/> </div> </template> diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue index 221be4b9074..343fd0a5300 100644 --- a/app/assets/javascripts/ide/components/repo_editor.vue +++ b/app/assets/javascripts/ide/components/repo_editor.vue @@ -90,6 +90,11 @@ export default { rightPanelCollapsed() { this.editor.updateDimensions(); }, + panelResizing(isResizing) { + if (isResizing === false) { + this.editor.updateDimensions(); + } + }, }, computed: { ...mapGetters([ @@ -99,6 +104,7 @@ export default { ...mapState([ 'leftPanelCollapsed', 'rightPanelCollapsed', + 'panelResizing', ]), shouldHideEditor() { return this.activeFile.binary && !this.activeFile.raw; diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js index c01046c8c76..335882bb6d7 100644 --- a/app/assets/javascripts/ide/stores/actions.js +++ b/app/assets/javascripts/ide/stores/actions.js @@ -63,6 +63,10 @@ export const setPanelCollapsedStatus = ({ commit }, { side, collapsed }) => { } }; +export const setResizingStatus = ({ commit }, resizing) => { + commit(types.SET_RESIZING_STATUS, resizing); +}; + export const checkCommitStatus = ({ state }) => service .getBranchData(state.currentProjectId, state.currentBranchId) diff --git a/app/assets/javascripts/ide/stores/mutation_types.js b/app/assets/javascripts/ide/stores/mutation_types.js index 4e3c10972ba..69b218a5e7d 100644 --- a/app/assets/javascripts/ide/stores/mutation_types.js +++ b/app/assets/javascripts/ide/stores/mutation_types.js @@ -5,6 +5,7 @@ export const SET_ROOT = 'SET_ROOT'; export const SET_LAST_COMMIT_DATA = 'SET_LAST_COMMIT_DATA'; export const SET_LEFT_PANEL_COLLAPSED = 'SET_LEFT_PANEL_COLLAPSED'; export const SET_RIGHT_PANEL_COLLAPSED = 'SET_RIGHT_PANEL_COLLAPSED'; +export const SET_RESIZING_STATUS = 'SET_RESIZING_STATUS'; // Project Mutation Types export const SET_PROJECT = 'SET_PROJECT'; diff --git a/app/assets/javascripts/ide/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js index 2fed9019cb6..03d81be10a1 100644 --- a/app/assets/javascripts/ide/stores/mutations.js +++ b/app/assets/javascripts/ide/stores/mutations.js @@ -49,6 +49,11 @@ export default { rightPanelCollapsed: collapsed, }); }, + [types.SET_RESIZING_STATUS](state, resizing) { + Object.assign(state, { + panelResizing: resizing, + }); + }, [types.SET_LAST_COMMIT_DATA](state, { entry, lastCommit }) { Object.assign(entry.lastCommit, { id: lastCommit.commit.id, diff --git a/app/assets/javascripts/ide/stores/state.js b/app/assets/javascripts/ide/stores/state.js index 539e382830f..61d12096946 100644 --- a/app/assets/javascripts/ide/stores/state.js +++ b/app/assets/javascripts/ide/stores/state.js @@ -19,4 +19,5 @@ export default () => ({ projects: {}, leftPanelCollapsed: false, rightPanelCollapsed: true, + panelResizing: false, }); diff --git a/app/assets/javascripts/vue_shared/components/panel_resizer.vue b/app/assets/javascripts/vue_shared/components/panel_resizer.vue new file mode 100644 index 00000000000..4371534d345 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/panel_resizer.vue @@ -0,0 +1,91 @@ +<script> +export default { + props: { + startSize: { + type: Number, + required: true, + }, + side: { + type: String, + required: true, + }, + minSize: { + type: Number, + required: false, + default: 0, + }, + maxSize: { + type: Number, + required: false, + default: Number.MAX_VALUE, + }, + enabled: { + type: Boolean, + required: false, + default: true, + }, + }, + data() { + return { + size: this.startSize, + }; + }, + computed: { + className() { + return `drag${this.side}`; + }, + cursorStyle() { + if (this.enabled) { + return { cursor: 'ew-resize' }; + } + return {}; + }, + }, + methods: { + resetSize(e) { + e.preventDefault(); + this.size = this.startSize; + this.$emit('update:size', this.size); + }, + startDrag(e) { + if (this.enabled) { + e.preventDefault(); + this.startPos = e.clientX; + this.currentStartSize = this.size; + document.addEventListener('mousemove', this.drag); + document.addEventListener('mouseup', this.endDrag, { once: true }); + this.$emit('resize-start', this.size); + } + }, + drag(e) { + e.preventDefault(); + let moved = e.clientX - this.startPos; + if (this.side === 'left') moved = -moved; + let newSize = this.currentStartSize + moved; + if (newSize < this.minSize) { + newSize = this.minSize; + } else if (newSize > this.maxSize) { + newSize = this.maxSize; + } + this.size = newSize; + + this.$emit('update:size', newSize); + }, + endDrag(e) { + e.preventDefault(); + document.removeEventListener('mousemove', this.drag); + this.$emit('resize-end', this.size); + }, + }, +}; +</script> + +<template> + <div + class="dragHandle" + :class="className" + :style="cursorStyle" + @mousedown="startDrag" + @dblclick="resetSize" + ></div> +</template> diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss index 51cc1729d9a..d01cbadebcc 100644 --- a/app/assets/stylesheets/pages/repo.scss +++ b/app/assets/stylesheets/pages/repo.scss @@ -36,10 +36,6 @@ } } -.with-performance-bar .ide-view { - height: calc(100vh - #{$header-height}); -} - .ide-file-list { flex: 1; @@ -242,12 +238,13 @@ table.table tr td.multi-file-table-name { .multi-file-commit-panel { display: flex; + position: relative; flex-direction: column; height: 100%; width: 290px; padding: 0; background-color: $gray-light; - border-left: 1px solid $white-dark; + padding-right: 3px; .projects-sidebar { display: flex; @@ -496,3 +493,30 @@ table.table tr td.multi-file-table-name { margin-top: $header-height; margin-bottom: 0; } + +.with-performance-bar { + .ide-flash-container.flash-container { + margin-top: $header-height + $performance-bar-height; + } + + .ide-view { + height: calc(100vh - #{$header-height + $performance-bar-height}); + } +} + + +.dragHandle { + position: absolute; + top: 0; + bottom: 0; + width: 3px; + background-color: $white-dark; + + &.dragright { + right: 0; + } + + &.dragleft { + left: 0; + } +} diff --git a/spec/javascripts/vue_shared/components/panel_resizer_spec.js b/spec/javascripts/vue_shared/components/panel_resizer_spec.js new file mode 100644 index 00000000000..70ce3dffaba --- /dev/null +++ b/spec/javascripts/vue_shared/components/panel_resizer_spec.js @@ -0,0 +1,59 @@ +import Vue from 'vue'; +import panelResizer from '~/vue_shared/components/panel_resizer.vue'; +import mountComponent from '../../helpers/vue_mount_component_helper'; + +describe('Panel Resizer component', () => { + let vm; + let PanelResizer; + + const triggerEvent = (eventName, el = vm.$el, clientX = 0) => { + const event = document.createEvent('MouseEvents'); + event.initMouseEvent(eventName, true, true, window, 1, clientX, 0, clientX, 0, false, false, + false, false, 0, null); + + el.dispatchEvent(event); + }; + + beforeEach(() => { + PanelResizer = Vue.extend(panelResizer); + }); + + afterEach(() => { + vm.$destroy(); + }); + + it('should render a div element with the correct classes and styles', () => { + vm = mountComponent(PanelResizer, { + startSize: 100, + side: 'left', + }); + + expect(vm.$el.tagName).toEqual('DIV'); + expect(vm.$el.getAttribute('class')).toBe('dragHandle dragleft'); + expect(vm.$el.getAttribute('style')).toBe('cursor: ew-resize;'); + }); + + it('should render a div element with the correct classes for a right side panel', () => { + vm = mountComponent(PanelResizer, { + startSize: 100, + side: 'right', + }); + + expect(vm.$el.tagName).toEqual('DIV'); + expect(vm.$el.getAttribute('class')).toBe('dragHandle dragright'); + }); + + it('drag the resizer', () => { + vm = mountComponent(PanelResizer, { + startSize: 100, + side: 'left', + }); + + spyOn(vm, '$emit'); + triggerEvent('mousedown', vm.$el); + triggerEvent('mousemove', document); + triggerEvent('mouseup', document); + expect(vm.$emit.calls.allArgs()).toEqual([['resize-start', 100], ['update:size', 100], ['resize-end', 100]]); + expect(vm.size).toBe(100); + }); +}); |