diff options
author | Oswaldo Ferreira <oswaldo@gitlab.com> | 2017-10-30 09:10:09 +0000 |
---|---|---|
committer | Phil Hughes <me@iamphill.com> | 2017-10-30 09:10:09 +0000 |
commit | cd784a80d7c779edc468d6255ffdeb8ea692661a (patch) | |
tree | db32f323b5aa0af3fcba743e1b871991d41b97d2 /app/assets | |
parent | 192049a61039220bb62e88d1d173d0aeaa555297 (diff) | |
download | gitlab-ce-cd784a80d7c779edc468d6255ffdeb8ea692661a.tar.gz |
[CE backport] Saved configuration for issue board
Diffstat (limited to 'app/assets')
15 files changed, 174 insertions, 82 deletions
diff --git a/app/assets/javascripts/boards/filtered_search_boards.js b/app/assets/javascripts/boards/filtered_search_boards.js index 3f083655f95..184665f395c 100644 --- a/app/assets/javascripts/boards/filtered_search_boards.js +++ b/app/assets/javascripts/boards/filtered_search_boards.js @@ -11,7 +11,8 @@ export default class FilteredSearchBoards extends gl.FilteredSearchManager { // Issue boards is slightly different, we handle all the requests async // instead or reloading the page, we just re-fire the list ajax requests this.isHandledAsync = true; - this.cantEdit = cantEdit; + this.cantEdit = cantEdit.filter(i => typeof i === 'string'); + this.cantEditWithValue = cantEdit.filter(i => typeof i === 'object'); } updateObject(path) { @@ -42,7 +43,9 @@ export default class FilteredSearchBoards extends gl.FilteredSearchManager { this.filteredSearchInput.dispatchEvent(new Event('input')); } - canEdit(tokenName) { - return this.cantEdit.indexOf(tokenName) === -1; + canEdit(tokenName, tokenValue) { + if (this.cantEdit.includes(tokenName)) return false; + return this.cantEditWithValue.findIndex(token => token.name === tokenName && + token.value === tokenValue) === -1; } } diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js index ea82958e80d..798d7e0d147 100644 --- a/app/assets/javascripts/boards/stores/boards_store.js +++ b/app/assets/javascripts/boards/stores/boards_store.js @@ -14,16 +14,18 @@ gl.issueBoards.BoardsStore = { }, state: {}, detail: { - issue: {} + issue: {}, }, moving: { issue: {}, - list: {} + list: {}, }, create () { this.state.lists = []; this.filter.path = getUrlParamsArray().join('&'); - this.detail = { issue: {} }; + this.detail = { + issue: {}, + }; }, addList (listObj, defaultAvatar) { const list = new List(listObj, defaultAvatar); diff --git a/app/assets/javascripts/filtered_search/dropdown_utils.js b/app/assets/javascripts/filtered_search/dropdown_utils.js index 8d711e3213c..cf8a9b0402b 100644 --- a/app/assets/javascripts/filtered_search/dropdown_utils.js +++ b/app/assets/javascripts/filtered_search/dropdown_utils.js @@ -147,6 +147,16 @@ class DropdownUtils { return dataValue !== null; } + static getVisualTokenValues(visualToken) { + const tokenName = visualToken && visualToken.querySelector('.name').textContent.trim(); + let tokenValue = visualToken && visualToken.querySelector('.value') && visualToken.querySelector('.value').textContent.trim(); + if (tokenName === 'label' && tokenValue) { + // remove leading symbol and wrapping quotes + tokenValue = tokenValue.replace(/^~("|')?(.*)/, '$2').replace(/("|')$/, ''); + } + return { tokenName, tokenValue }; + } + // Determines the full search query (visual tokens + input) static getSearchQuery(untilInput = false) { const container = FilteredSearchContainer.container; diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js index 7b233842d5a..69c57f923b6 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_manager.js +++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js @@ -185,8 +185,8 @@ class FilteredSearchManager { if (e.keyCode === 8 || e.keyCode === 46) { const { lastVisualToken } = gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); - const sanitizedTokenName = lastVisualToken && lastVisualToken.querySelector('.name').textContent.trim(); - const canEdit = sanitizedTokenName && this.canEdit && this.canEdit(sanitizedTokenName); + const { tokenName, tokenValue } = gl.DropdownUtils.getVisualTokenValues(lastVisualToken); + const canEdit = tokenName && this.canEdit && this.canEdit(tokenName, tokenValue); if (this.filteredSearchInput.value === '' && lastVisualToken && canEdit) { this.filteredSearchInput.value = gl.FilteredSearchVisualTokens.getLastTokenPartial(); gl.FilteredSearchVisualTokens.removeLastTokenPartial(); @@ -336,8 +336,8 @@ class FilteredSearchManager { let canClearToken = t.classList.contains('js-visual-token'); if (canClearToken) { - const tokenKey = t.querySelector('.name').textContent.trim(); - canClearToken = this.canEdit && this.canEdit(tokenKey); + const { tokenName, tokenValue } = gl.DropdownUtils.getVisualTokenValues(t); + canClearToken = this.canEdit && this.canEdit(tokenName, tokenValue); } if (canClearToken) { @@ -469,7 +469,7 @@ class FilteredSearchManager { } hasFilteredSearch = true; - const canEdit = this.canEdit && this.canEdit(sanitizedKey); + const canEdit = this.canEdit && this.canEdit(sanitizedKey, sanitizedValue); gl.FilteredSearchVisualTokens.addFilterVisualToken( sanitizedKey, `${symbol}${quotationsToUse}${sanitizedValue}${quotationsToUse}`, diff --git a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js index d2f92929b8a..6139e81fe6d 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js +++ b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js @@ -38,21 +38,14 @@ class FilteredSearchVisualTokens { } static createVisualTokenElementHTML(canEdit = true) { - let removeTokenMarkup = ''; - if (canEdit) { - removeTokenMarkup = ` - <div class="remove-token" role="button"> - <i class="fa fa-close"></i> - </div> - `; - } - return ` - <div class="selectable" role="button"> + <div class="${canEdit ? 'selectable' : 'hidden'}" role="button"> <div class="name"></div> <div class="value-container"> <div class="value"></div> - ${removeTokenMarkup} + <div class="remove-token" role="button"> + <i class="fa fa-close"></i> + </div> </div> </div> `; diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js index 84602cf9207..1e52963b1dd 100644 --- a/app/assets/javascripts/labels_select.js +++ b/app/assets/javascripts/labels_select.js @@ -8,7 +8,7 @@ import CreateLabelDropdown from './create_label'; (function() { this.LabelsSelect = (function() { - function LabelsSelect(els) { + function LabelsSelect(els, options = {}) { var _this, $els; _this = this; @@ -58,6 +58,7 @@ import CreateLabelDropdown from './create_label'; labelHTMLTemplate = _.template('<% _.each(labels, function(label){ %> <a href="<%- ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name[]=<%- encodeURIComponent(label.title) %>"> <span class="label has-tooltip color-label" title="<%- label.description %>" style="background-color: <%- label.color %>; color: <%- label.text_color %>;"> <%- label.title %> </span> </a> <% }); %>'); labelNoneHTMLTemplate = '<span class="no-value">None</span>'; } + const handleClick = options.handleClick; $sidebarLabelTooltip.tooltip(); @@ -316,9 +317,9 @@ import CreateLabelDropdown from './create_label'; }, multiSelect: $dropdown.hasClass('js-multiselect'), vue: $dropdown.hasClass('js-issue-board-sidebar'), - clicked: function(options) { - const { $el, e, isMarking } = options; - const label = options.selectedObj; + clicked: function(clickEvent) { + const { $el, e, isMarking } = clickEvent; + const label = clickEvent.selectedObj; var isIssueIndex, isMRIndex, page, boardsModel; var fadeOutLoader = () => { @@ -391,6 +392,10 @@ import CreateLabelDropdown from './create_label'; .then(fadeOutLoader) .catch(fadeOutLoader); } + else if (handleClick) { + e.preventDefault(); + handleClick(label); + } else { if ($dropdown.hasClass('js-multiselect')) { diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js index e7d5325a509..74e5a4f1cea 100644 --- a/app/assets/javascripts/milestone_select.js +++ b/app/assets/javascripts/milestone_select.js @@ -5,7 +5,7 @@ import _ from 'underscore'; (function() { this.MilestoneSelect = (function() { - function MilestoneSelect(currentProject, els) { + function MilestoneSelect(currentProject, els, options = {}) { var _this, $els; if (currentProject != null) { _this = this; @@ -136,19 +136,26 @@ import _ from 'underscore'; }, opened: function(e) { const $el = $(e.currentTarget); - if ($dropdown.hasClass('js-issue-board-sidebar')) { + if ($dropdown.hasClass('js-issue-board-sidebar') || options.handleClick) { selectedMilestone = $dropdown[0].dataset.selected || selectedMilestoneDefault; } $('a.is-active', $el).removeClass('is-active'); $(`[data-milestone-id="${selectedMilestone}"] > a`, $el).addClass('is-active'); }, vue: $dropdown.hasClass('js-issue-board-sidebar'), - clicked: function(options) { - const { $el, e } = options; - let selected = options.selectedObj; + clicked: function(clickEvent) { + const { $el, e } = clickEvent; + let selected = clickEvent.selectedObj; var data, isIssueIndex, isMRIndex, isSelecting, page, boardsStore; if (!selected) return; + + if (options.handleClick) { + e.preventDefault(); + options.handleClick(selected); + return; + } + page = $('body').attr('data-page'); isIssueIndex = page === 'projects:issues:index'; isMRIndex = (page === page && page === 'projects:merge_requests:index'); diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js index a0883b32593..759cc9925f4 100644 --- a/app/assets/javascripts/users_select.js +++ b/app/assets/javascripts/users_select.js @@ -6,7 +6,7 @@ import _ from 'underscore'; // TODO: remove eventHub hack after code splitting refactor window.emitSidebarEvent = window.emitSidebarEvent || $.noop; -function UsersSelect(currentUser, els) { +function UsersSelect(currentUser, els, options = {}) { var $els; this.users = this.users.bind(this); this.user = this.user.bind(this); @@ -20,6 +20,8 @@ function UsersSelect(currentUser, els) { } } + const { handleClick } = options; + $els = $(els); if (!els) { @@ -442,6 +444,9 @@ function UsersSelect(currentUser, els) { } if ($el.closest('.add-issues-modal').length) { gl.issueBoards.ModalStore.store.filter[$dropdown.data('field-name')] = user.id; + } else if (handleClick) { + e.preventDefault(); + handleClick(user, isMarking); } else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) { return Issuable.filterResults($dropdown.closest('form')); } else if ($dropdown.hasClass('js-filter-submit')) { diff --git a/app/assets/javascripts/vue_shared/components/loading_icon.vue b/app/assets/javascripts/vue_shared/components/loading_icon.vue index 15581d5c2a0..494fe4468d9 100644 --- a/app/assets/javascripts/vue_shared/components/loading_icon.vue +++ b/app/assets/javascripts/vue_shared/components/loading_icon.vue @@ -18,6 +18,12 @@ required: false, default: false, }, + + class: { + type: String, + required: false, + default: '', + }, }, computed: { @@ -25,7 +31,7 @@ return this.inline ? 'span' : 'div'; }, cssClass() { - return `fa-${this.size}x`; + return `fa-${this.size}x ${this.class}`.trim(); }, }, }; diff --git a/app/assets/javascripts/vue_shared/components/popup_dialog.vue b/app/assets/javascripts/vue_shared/components/popup_dialog.vue index 9e8c10bdc1a..fc6421fecb9 100644 --- a/app/assets/javascripts/vue_shared/components/popup_dialog.vue +++ b/app/assets/javascripts/vue_shared/components/popup_dialog.vue @@ -5,17 +5,27 @@ export default { props: { title: { type: String, - required: true, + required: false, }, text: { type: String, required: false, }, + hideFooter: { + type: Boolean, + required: false, + default: false, + }, kind: { type: String, required: false, default: 'primary', }, + modalDialogClass: { + type: String, + required: false, + default: '', + }, closeKind: { type: String, required: false, @@ -30,6 +40,11 @@ export default { type: String, required: true, }, + submitDisabled: { + type: Boolean, + required: false, + default: false, + }, }, computed: { @@ -57,43 +72,57 @@ export default { </script> <template> -<div - class="modal popup-dialog" - role="dialog" - tabindex="-1"> - <div class="modal-dialog" role="document"> - <div class="modal-content"> - <div class="modal-header"> - <button type="button" - class="close" - @click="close" - aria-label="Close"> - <span aria-hidden="true">×</span> - </button> - <h4 class="modal-title">{{this.title}}</h4> - </div> - <div class="modal-body"> - <slot name="body" :text="text"> - <p>{{text}}</p> - </slot> - </div> - <div class="modal-footer"> - <button - type="button" - class="btn" - :class="btnCancelKindClass" - @click="close"> - {{ closeButtonLabel }} - </button> - <button - type="button" - class="btn" - :class="btnKindClass" - @click="emitSubmit(true)"> - {{ primaryButtonLabel }} - </button> +<div class="modal-open"> + <div + class="modal popup-dialog" + role="dialog" + tabindex="-1" + > + <div + :class="modalDialogClass" + class="modal-dialog" + role="document" + > + <div class="modal-content"> + <div class="modal-header"> + <slot name="header"> + <h4 class="modal-title pull-left"> + {{this.title}} + </h4> + <button + type="button" + class="close pull-right" + @click="close" + aria-label="Close" + > + <span aria-hidden="true">×</span> + </button> + </slot> + </div> + <div class="modal-body"> + <slot name="body" :text="text"> + <p>{{this.text}}</p> + </slot> + </div> + <div class="modal-footer" v-if="!hideFooter"> + <button + type="button" + class="btn pull-left" + :class="btnCancelKindClass" + @click="close"> + {{ closeButtonLabel }} + </button> + <button + type="button" + class="btn pull-right" + :class="btnKindClass" + @click="emitSubmit(true)"> + {{ primaryButtonLabel }} + </button> + </div> </div> </div> </div> + <div class="modal-backdrop fade in" /> </div> </template> diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 96f9dda26c4..1cfd7ef01a8 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -4,6 +4,9 @@ .cred { color: $common-red; } .cgreen { color: $common-green; } .cdark { color: $common-gray-dark; } +.text-secondary { + color: $gl-text-color-secondary; +} /** COMMON CLASSES **/ .prepend-top-0 { margin-top: 0; } diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index a9d804e735d..63697fd38a7 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -37,6 +37,7 @@ .dropdown-menu-nav { @include set-visible; display: block; + min-height: 40px; @media (max-width: $screen-xs-max) { width: 100%; diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss index 1cebd02df48..d218fb6d702 100644 --- a/app/assets/stylesheets/framework/modal.scss +++ b/app/assets/stylesheets/framework/modal.scss @@ -42,3 +42,11 @@ body.modal-open { width: 98%; } } + +.modal.popup-dialog { + display: block; +} + +.modal-body { + background-color: $modal-body-bg; +} diff --git a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss index 3ea77eb7a43..a23131e0818 100644 --- a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss +++ b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss @@ -164,3 +164,36 @@ $pre-border-color: $border-color; $table-bg-accent: $gray-light; $zindex-popover: 900; + +//== Modals +// +//## + +//** Padding applied to the modal body +$modal-inner-padding: $gl-padding; + +//** Padding applied to the modal title +$modal-title-padding: $gl-padding; +//** Modal title line-height +// $modal-title-line-height: $line-height-base + +//** Background color of modal content area +$modal-content-bg: $gray-light; +$modal-body-bg: $white-light; +//** Modal content border color +// $modal-content-border-color: rgba(0,0,0,.2) +//** Modal content border color **for IE8** +// $modal-content-fallback-border-color: #999 + +//** Modal backdrop background color +// $modal-backdrop-bg: #000 +//** Modal backdrop opacity +// $modal-backdrop-opacity: .5 +//** Modal header border color +// $modal-header-border-color: #e5e5e5 +//** Modal footer border color +// $modal-footer-border-color: $modal-header-border-color + +// $modal-lg: 900px +// $modal-md: 600px +// $modal-sm: 300px diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss index 6a363b1710e..e8c7f8a8fc0 100644 --- a/app/assets/stylesheets/pages/repo.scss +++ b/app/assets/stylesheets/pages/repo.scss @@ -7,19 +7,6 @@ background: $black-transparent; } -.modal.popup-dialog { - display: block; - background-color: $black-transparent; - z-index: 2100; - - @media (min-width: $screen-md-min) { - .modal-dialog { - width: 600px; - margin: 30px auto; - } - } -} - .project-refs-form, .project-refs-target-form { display: inline-block; |