diff options
6 files changed, 141 insertions, 24 deletions
diff --git a/app/assets/javascripts/boards/components/modal/footer.js.es6 b/app/assets/javascripts/boards/components/modal/footer.js.es6 index 9cb48448a87..dec0196a32c 100644 --- a/app/assets/javascripts/boards/components/modal/footer.js.es6 +++ b/app/assets/javascripts/boards/components/modal/footer.js.es6 @@ -7,19 +7,40 @@ gl.issueBoards.ModalFooter = Vue.extend({ data() { - return Store.modal; + return Object.assign({}, Store.modal, { + disabled: false, + }); + }, + computed: { + submitDisabled() { + if (this.disabled) return true; + + return !Store.modalSelectedCount(); + }, + submitText() { + const count = Store.modalSelectedCount(); + + return `Add ${count} issue${count > 1 || !count ? 's' : ''}`; + }, }, methods: { hideModal() { this.showAddIssuesModal = false; }, + addIssues() { + const issueIds = this.issues.filter(issue => issue.selected).map(issue => issue.id); + + this.disabled = true; + }, }, template: ` <footer class="form-actions add-issues-footer"> <button class="btn btn-success pull-left" - type="button"> - Add issues + type="button" + :disabled="submitDisabled" + @click="addIssues"> + {{ submitText }} </button> <button class="btn btn-default pull-right" diff --git a/app/assets/javascripts/boards/components/modal/list.js.es6 b/app/assets/javascripts/boards/components/modal/list.js.es6 index e06fa58b3b6..e700161f642 100644 --- a/app/assets/javascripts/boards/components/modal/list.js.es6 +++ b/app/assets/javascripts/boards/components/modal/list.js.es6 @@ -1,6 +1,8 @@ /* global Vue */ /* global ListIssue */ +/* global Masonry */ (() => { + let listMasonry; const Store = gl.issueBoards.BoardsStore; window.gl = window.gl || {}; @@ -22,9 +24,14 @@ loading() { return this.issues.length === 0; }, + selectedCount() { + return Store.modalSelectedCount(); + }, }, methods: { - toggleIssue(issue) { + toggleIssue(issueObj) { + const issue = issueObj; + issue.selected = !issue.selected; }, showIssue(issue) { @@ -41,7 +48,7 @@ if (listMasonry) { listMasonry.destroy(); } - } + }, }, mounted() { gl.boardService.getBacklog() @@ -66,9 +73,11 @@ }, template: ` <section class="add-issues-list"> - <i - class="fa fa-spinner fa-spin" - v-if="loading"></i> + <div + class="add-issues-list-loading" + v-if="loading"> + <i class="fa fa-spinner fa-spin"></i> + </div> <div class="add-issues-list-columns list-unstyled" ref="list" @@ -85,9 +94,19 @@ :issue="issue" :issue-link-base="'/'"> </issue-card-inner> + <span + v-if="issue.selected" + class="issue-card-selected"> + <i class="fa fa-check"></i> + </span> </div> </div> </div> + <p + class="all-issues-selected-empty" + v-if="activeTab == 'selected' && selectedCount == 0"> + You don't have any issues selected, <a href="#" @click="activeTab = 'all'">select some</a>. + </p> </section> `, }); diff --git a/app/assets/javascripts/boards/components/modal/search.js.es6 b/app/assets/javascripts/boards/components/modal/search.js.es6 index 714c9240d4d..59aeb17baa5 100644 --- a/app/assets/javascripts/boards/components/modal/search.js.es6 +++ b/app/assets/javascripts/boards/components/modal/search.js.es6 @@ -1,14 +1,48 @@ /* global Vue */ (() => { + const Store = gl.issueBoards.BoardsStore; + window.gl = window.gl || {}; window.gl.issueBoards = window.gl.issueBoards || {}; gl.issueBoards.ModalSearch = Vue.extend({ + data() { + return Store.modal; + }, + computed: { + selectAllText() { + if (Store.modalSelectedCount() !== this.issues.length || this.issues.length === 0) { + return 'Select all'; + } + + return 'Un-select all'; + }, + }, + methods: { + toggleAll() { + const select = Store.modalSelectedCount() !== this.issues.length; + + this.issues.forEach((issue) => { + const issueUpdate = issue; + issueUpdate.selected = select; + }); + }, + }, template: ` - <input - placeholder="Search issues..." - class="form-control" - type="search" /> + <div + class="add-issues-search" + v-if="activeTab == 'all'"> + <input + placeholder="Search issues..." + class="form-control" + type="search" /> + <button + type="button" + class="btn btn-success btn-inverted" + @click="toggleAll"> + {{ selectAllText }} + </button> + </div> `, }); })(); diff --git a/app/assets/javascripts/boards/components/modal/tabs.js.es6 b/app/assets/javascripts/boards/components/modal/tabs.js.es6 index 58fb75f839f..a1da7840036 100644 --- a/app/assets/javascripts/boards/components/modal/tabs.js.es6 +++ b/app/assets/javascripts/boards/components/modal/tabs.js.es6 @@ -16,17 +16,12 @@ }, computed: { selectedCount() { - let count = 0; - - this.issues.forEach((issue) => { - if (issue.selected) { - count += 1; - } - }); - - return count; + return Store.modalSelectedCount(); }, }, + destroyed() { + this.activeTab = 'all'; + }, template: ` <div class="top-area"> <ul class="nav-links issues-state-filters"> diff --git a/app/assets/javascripts/boards/stores/boards_store.js.es6 b/app/assets/javascripts/boards/stores/boards_store.js.es6 index 42216d429c6..4c5eb57f5c9 100644 --- a/app/assets/javascripts/boards/stores/boards_store.js.es6 +++ b/app/assets/javascripts/boards/stores/boards_store.js.es6 @@ -125,6 +125,17 @@ }, updateFiltersUrl () { history.pushState(null, null, `?${$.param(this.state.filters)}`); - } + }, + modalSelectedCount() { + let count = 0; + + this.modal.issues.forEach((issue) => { + if (issue.selected) { + count += 1; + } + }); + + return count; + }, }; })(); diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index f41d4cdd45d..73ed8dd8ab1 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -390,19 +390,34 @@ .top-area { margin-bottom: 10px; } +} - .form-control { - margin-bottom: 10px; +.add-issues-search { + display: flex; + margin-bottom: 10px; + + .btn { + margin-left: 10px; } } .add-issues-list { + display: flex; flex: 1; margin-left: -$gl-vert-padding; margin-right: -$gl-vert-padding; overflow-y: scroll; } +.add-issues-list-loading { + align-self: center; + width: 100%; + padding-left: $gl-vert-padding; + padding-right: $gl-vert-padding; + font-size: 35px; + text-align: center; +} + .add-issues-footer { margin-top: auto; margin-left: -15px; @@ -412,6 +427,9 @@ } .add-issues-list-columns { + width: 100%; + padding-top: 3px; + .card-parent { width: (100% / 3); padding: 0 $gl-vert-padding ($gl-vert-padding * 2); @@ -421,3 +439,22 @@ cursor: pointer; } } + +.all-issues-selected-empty { + align-self: center; + margin-bottom: 0; +} + +.issue-card-selected { + position: absolute; + right: -3px; + top: -3px; + width: 20px; + height: 20px; + background-color: $blue-dark; + color: $white-light; + font-size: 12px; + text-align: center; + line-height: 20px; + border-radius: 50%; +} |