diff options
author | Fatih Acet <acetfatih@gmail.com> | 2016-10-26 12:31:09 +0000 |
---|---|---|
committer | Fatih Acet <acetfatih@gmail.com> | 2016-10-26 12:31:09 +0000 |
commit | a66a3c7449f6d91f7f3bb1a86ac0e17623ba1fd6 (patch) | |
tree | 6fe52690e988165e2463357267fadcfa14b06e56 /app/assets/javascripts/boards | |
parent | b0e6fc12653cfc726e8d5710e850e8609a44dcf3 (diff) | |
parent | a2eff1a8e55b4bf513d3d752fcd6477595813dc2 (diff) | |
download | gitlab-ce-a66a3c7449f6d91f7f3bb1a86ac0e17623ba1fd6.tar.gz |
Merge branch 'issue-board-sidebar' into 'master'
Issue board sidebar
## What does this MR do?
Adds a sidebar when clicking an issue in the issue boards lists. This allows user to easily update other parts of the issue details without having to visit the issue itself. Same functionality as on issue page.
When creating a new issue the sidebar automatically opens.
## Screenshots (if relevant)
![Screen_Shot_2016-10-07_at_13.10.16](/uploads/ad08785f407d8ac3fe9cb078868a7839/Screen_Shot_2016-10-07_at_13.10.16.png)
## What are the relevant issue numbers?
Closes #21219
See merge request !6690
Diffstat (limited to 'app/assets/javascripts/boards')
11 files changed, 161 insertions, 6 deletions
diff --git a/app/assets/javascripts/boards/boards_bundle.js.es6 b/app/assets/javascripts/boards/boards_bundle.js.es6 index d4f8f4b9420..56f91503017 100644 --- a/app/assets/javascripts/boards/boards_bundle.js.es6 +++ b/app/assets/javascripts/boards/boards_bundle.js.es6 @@ -5,7 +5,9 @@ //= require_tree ./stores //= require_tree ./services //= require_tree ./mixins +//= require_tree ./filters //= require ./components/board +//= require ./components/board_sidebar //= require ./components/new_list_dropdown //= require ./vue_resource_interceptor @@ -22,7 +24,8 @@ $(() => { gl.IssueBoardsApp = new Vue({ el: $boardApp, components: { - 'board': gl.issueBoards.Board + 'board': gl.issueBoards.Board, + 'board-sidebar': gl.issueBoards.BoardSidebar }, data: { state: Store.state, @@ -30,9 +33,15 @@ $(() => { endpoint: $boardApp.dataset.endpoint, boardId: $boardApp.dataset.boardId, disabled: $boardApp.dataset.disabled === 'true', - issueLinkBase: $boardApp.dataset.issueLinkBase + issueLinkBase: $boardApp.dataset.issueLinkBase, + detailIssue: Store.detail }, init: Store.create.bind(Store), + computed: { + detailIssueVisible () { + return Object.keys(this.detailIssue.issue).length; + } + }, created () { gl.boardService = new BoardService(this.endpoint, this.boardId); }, diff --git a/app/assets/javascripts/boards/components/board.js.es6 b/app/assets/javascripts/boards/components/board.js.es6 index cacb36a897f..500213a3a43 100644 --- a/app/assets/javascripts/boards/components/board.js.es6 +++ b/app/assets/javascripts/boards/components/board.js.es6 @@ -21,6 +21,7 @@ }, data () { return { + detailIssue: Store.detail, filters: Store.state.filters, showIssueForm: false }; @@ -32,6 +33,26 @@ this.list.getIssues(true); }, deep: true + }, + detailIssue: { + handler () { + if (!Object.keys(this.detailIssue.issue).length) return; + + const issue = this.list.findIssue(this.detailIssue.issue.id); + + if (issue) { + const boardsList = document.querySelectorAll('.boards-list')[0]; + const right = (this.$el.offsetLeft + this.$el.offsetWidth) - boardsList.offsetWidth; + const left = boardsList.scrollLeft - this.$el.offsetLeft; + + if (right - boardsList.scrollLeft > 0) { + boardsList.scrollLeft = right; + } else if (left > 0) { + boardsList.scrollLeft = this.$el.offsetLeft; + } + } + }, + deep: true } }, methods: { diff --git a/app/assets/javascripts/boards/components/board_card.js.es6 b/app/assets/javascripts/boards/components/board_card.js.es6 index 4a7cfeaeab2..f743d72f936 100644 --- a/app/assets/javascripts/boards/components/board_card.js.es6 +++ b/app/assets/javascripts/boards/components/board_card.js.es6 @@ -12,6 +12,17 @@ disabled: Boolean, index: Number }, + data () { + return { + showDetail: false, + detailIssue: Store.detail + }; + }, + computed: { + issueDetailVisible () { + return this.detailIssue.issue && this.detailIssue.issue.id === this.issue.id; + } + }, methods: { filterByLabel (label, e) { let labelToggleText = label.title; @@ -37,6 +48,29 @@ $('.labels-filter .dropdown-toggle-text').text(labelToggleText); Store.updateFiltersUrl(); + }, + mouseDown () { + this.showDetail = true; + }, + mouseMove () { + if (this.showDetail) { + this.showDetail = false; + } + }, + showIssue (e) { + const targetTagName = e.target.tagName.toLowerCase(); + + if (targetTagName === 'a' || targetTagName === 'button') return; + + if (this.showDetail) { + this.showDetail = false; + + if (Store.detail.issue && Store.detail.issue.id === this.issue.id) { + Store.detail.issue = {}; + } else { + Store.detail.issue = this.issue; + } + } } } }); diff --git a/app/assets/javascripts/boards/components/board_new_issue.js.es6 b/app/assets/javascripts/boards/components/board_new_issue.js.es6 index a4fad422eca..b31adfdb8da 100644 --- a/app/assets/javascripts/boards/components/board_new_issue.js.es6 +++ b/app/assets/javascripts/boards/components/board_new_issue.js.es6 @@ -1,4 +1,6 @@ (() => { + const Store = gl.issueBoards.BoardsStore; + window.gl = window.gl || {}; gl.issueBoards.BoardNewIssue = Vue.extend({ @@ -27,13 +29,16 @@ const labels = this.list.label ? [this.list.label] : []; const issue = new ListIssue({ title: this.title, - labels + labels, + subscribed: true }); this.list.newIssue(issue) .then((data) => { // Need this because our jQuery very kindly disables buttons on ALL form submissions $(this.$els.submitButton).enable(); + + Store.detail.issue = issue; }) .catch(() => { // Need this because our jQuery very kindly disables buttons on ALL form submissions diff --git a/app/assets/javascripts/boards/components/board_sidebar.js.es6 b/app/assets/javascripts/boards/components/board_sidebar.js.es6 new file mode 100644 index 00000000000..e83e69247d9 --- /dev/null +++ b/app/assets/javascripts/boards/components/board_sidebar.js.es6 @@ -0,0 +1,52 @@ +(() => { + const Store = gl.issueBoards.BoardsStore; + + window.gl = window.gl || {}; + window.gl.issueBoards = window.gl.issueBoards || {}; + + gl.issueBoards.BoardSidebar = Vue.extend({ + props: { + currentUser: Object + }, + data() { + return { + detail: Store.detail, + issue: {} + }; + }, + computed: { + showSidebar () { + return Object.keys(this.issue).length; + } + }, + watch: { + detail: { + handler () { + this.issue = this.detail.issue; + }, + deep: true + }, + issue () { + if (this.showSidebar) { + this.$nextTick(() => { + $('.right-sidebar').getNiceScroll(0).doScrollTop(0, 0); + $('.right-sidebar').getNiceScroll().resize(); + }); + } + } + }, + methods: { + closeSidebar () { + this.detail.issue = {}; + } + }, + ready () { + new IssuableContext(this.currentUser); + new MilestoneSelect(); + new gl.DueDateSelectors(); + new LabelsSelect(); + new Sidebar(); + new Subscription('.subscription'); + } + }); +})(); diff --git a/app/assets/javascripts/boards/filters/due_date_filters.js.es6 b/app/assets/javascripts/boards/filters/due_date_filters.js.es6 new file mode 100644 index 00000000000..50ef1911022 --- /dev/null +++ b/app/assets/javascripts/boards/filters/due_date_filters.js.es6 @@ -0,0 +1,4 @@ +Vue.filter('due-date', (value) => { + const date = new Date(value); + return $.datepicker.formatDate('M d, yy', date); +}); diff --git a/app/assets/javascripts/boards/mixins/sortable_default_options.js.es6 b/app/assets/javascripts/boards/mixins/sortable_default_options.js.es6 index f629d45c587..bd9ba7d5118 100644 --- a/app/assets/javascripts/boards/mixins/sortable_default_options.js.es6 +++ b/app/assets/javascripts/boards/mixins/sortable_default_options.js.es6 @@ -22,7 +22,7 @@ fallbackOnBody: true, ghostClass: 'is-ghost', filter: '.has-tooltip, .btn', - delay: gl.issueBoards.touchEnabled ? 100 : 0, + delay: gl.issueBoards.touchEnabled ? 100 : 50, scrollSensitivity: gl.issueBoards.touchEnabled ? 60 : 100, scrollSpeed: 20, onStart: gl.issueBoards.onStart, diff --git a/app/assets/javascripts/boards/models/issue.js.es6 b/app/assets/javascripts/boards/models/issue.js.es6 index eb082103de9..03fd6c05d87 100644 --- a/app/assets/javascripts/boards/models/issue.js.es6 +++ b/app/assets/javascripts/boards/models/issue.js.es6 @@ -3,12 +3,18 @@ class ListIssue { this.id = obj.iid; this.title = obj.title; this.confidential = obj.confidential; + this.dueDate = obj.due_date; + this.subscribed = obj.subscribed; this.labels = []; if (obj.assignee) { this.assignee = new ListUser(obj.assignee); } + if (obj.milestone) { + this.milestone = new ListMilestone(obj.milestone); + } + obj.labels.forEach((label) => { this.labels.push(new ListLabel(label)); }); @@ -41,4 +47,21 @@ class ListIssue { getLists () { return gl.issueBoards.BoardsStore.state.lists.filter( list => list.findIssue(this.id) ); } + + update (url) { + const data = { + issue: { + milestone_id: this.milestone ? this.milestone.id : null, + due_date: this.dueDate, + assignee_id: this.assignee ? this.assignee.id : null, + label_ids: this.labels.map( (label) => label.id ) + } + }; + + if (!data.issue.label_ids.length) { + data.issue.label_ids = ['']; + } + + return Vue.http.patch(url, data); + } } diff --git a/app/assets/javascripts/boards/models/milestone.js.es6 b/app/assets/javascripts/boards/models/milestone.js.es6 new file mode 100644 index 00000000000..577adf11265 --- /dev/null +++ b/app/assets/javascripts/boards/models/milestone.js.es6 @@ -0,0 +1,6 @@ +class ListMilestone { + constructor (obj) { + this.id = obj.id; + this.title = obj.title; + } +} diff --git a/app/assets/javascripts/boards/services/board_service.js.es6 b/app/assets/javascripts/boards/services/board_service.js.es6 index b9c91cbf31e..b76063911bb 100644 --- a/app/assets/javascripts/boards/services/board_service.js.es6 +++ b/app/assets/javascripts/boards/services/board_service.js.es6 @@ -1,7 +1,5 @@ class BoardService { constructor (root, boardId) { - Vue.http.options.root = root; - this.lists = Vue.resource(`${root}/${boardId}/lists{/id}`, {}, { generate: { method: 'POST', diff --git a/app/assets/javascripts/boards/stores/boards_store.js.es6 b/app/assets/javascripts/boards/stores/boards_store.js.es6 index a12d3a59265..c30f66e3a5a 100644 --- a/app/assets/javascripts/boards/stores/boards_store.js.es6 +++ b/app/assets/javascripts/boards/stores/boards_store.js.es6 @@ -5,6 +5,9 @@ gl.issueBoards.BoardsStore = { disabled: false, state: {}, + detail: { + issue: {} + }, moving: { issue: {}, list: {} |