summaryrefslogtreecommitdiff
path: root/app/assets
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets')
-rw-r--r--app/assets/javascripts/boards/components/modal/filters.js57
-rw-r--r--app/assets/javascripts/boards/components/modal/filters/label.js54
-rw-r--r--app/assets/javascripts/boards/components/modal/filters/milestone.js56
-rw-r--r--app/assets/javascripts/boards/components/modal/filters/user.js96
-rw-r--r--app/assets/javascripts/boards/components/modal/header.js16
-rw-r--r--app/assets/javascripts/boards/components/modal/index.js14
-rw-r--r--app/assets/javascripts/boards/filtered_search_boards.js11
-rw-r--r--app/assets/javascripts/boards/models/list.js21
-rw-r--r--app/assets/javascripts/boards/stores/modal_store.js14
-rw-r--r--app/assets/javascripts/boards/utils/query_data.js21
-rw-r--r--app/assets/javascripts/filtered_search/container.js14
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_hint.js2
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_utils.js7
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js20
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_manager.js11
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js26
-rw-r--r--app/assets/javascripts/todos.js67
-rw-r--r--app/assets/stylesheets/framework/awards.scss16
-rw-r--r--app/assets/stylesheets/pages/boards.scss9
19 files changed, 165 insertions, 367 deletions
diff --git a/app/assets/javascripts/boards/components/modal/filters.js b/app/assets/javascripts/boards/components/modal/filters.js
index 6de06811d94..bd394a2318c 100644
--- a/app/assets/javascripts/boards/components/modal/filters.js
+++ b/app/assets/javascripts/boards/components/modal/filters.js
@@ -1,49 +1,24 @@
-/* global Vue */
-const userFilter = require('./filters/user');
-const milestoneFilter = require('./filters/milestone');
-const labelFilter = require('./filters/label');
+import FilteredSearchBoards from '../../filtered_search_boards';
+import FilteredSearchContainer from '../../../filtered_search/container';
-module.exports = Vue.extend({
+export default {
name: 'modal-filters',
props: {
- projectId: {
- type: Number,
- required: true,
- },
- milestonePath: {
- type: String,
- required: true,
- },
- labelPath: {
- type: String,
+ store: {
+ type: Object,
required: true,
},
},
- destroyed() {
- gl.issueBoards.ModalStore.setDefaultFilter();
+ mounted() {
+ FilteredSearchContainer.container = this.$el;
+
+ this.filteredSearch = new FilteredSearchBoards(this.store);
+ this.filteredSearch.removeTokens();
},
- components: {
- userFilter,
- milestoneFilter,
- labelFilter,
+ beforeDestroy() {
+ this.filteredSearch.cleanup();
+ FilteredSearchContainer.container = document;
+ this.store.path = '';
},
- template: `
- <div class="modal-filters">
- <user-filter
- dropdown-class-name="dropdown-menu-author"
- toggle-class-name="js-user-search js-author-search"
- toggle-label="Author"
- field-name="author_id"
- :project-id="projectId"></user-filter>
- <user-filter
- dropdown-class-name="dropdown-menu-author"
- toggle-class-name="js-assignee-search"
- toggle-label="Assignee"
- field-name="assignee_id"
- :null-user="true"
- :project-id="projectId"></user-filter>
- <milestone-filter :milestone-path="milestonePath"></milestone-filter>
- <label-filter :label-path="labelPath"></label-filter>
- </div>
- `,
-});
+ template: '#js-board-modal-filter',
+};
diff --git a/app/assets/javascripts/boards/components/modal/filters/label.js b/app/assets/javascripts/boards/components/modal/filters/label.js
deleted file mode 100644
index 4fc8f72a145..00000000000
--- a/app/assets/javascripts/boards/components/modal/filters/label.js
+++ /dev/null
@@ -1,54 +0,0 @@
-/* eslint-disable no-new */
-/* global Vue */
-/* global LabelsSelect */
-module.exports = Vue.extend({
- name: 'filter-label',
- props: {
- labelPath: {
- type: String,
- required: true,
- },
- },
- mounted() {
- new LabelsSelect(this.$refs.dropdown);
- },
- template: `
- <div class="dropdown">
- <button
- class="dropdown-menu-toggle js-label-select js-multiselect js-extra-options"
- type="button"
- data-toggle="dropdown"
- data-show-any="true"
- data-show-no="true"
- :data-labels="labelPath"
- ref="dropdown">
- <span class="dropdown-toggle-text">
- Label
- </span>
- <i class="fa fa-chevron-down"></i>
- </button>
- <div class="dropdown-menu dropdown-select dropdown-menu-paging dropdown-menu-labels dropdown-menu-selectable">
- <div class="dropdown-title">
- Filter by label
- <button
- class="dropdown-title-button dropdown-menu-close"
- aria-label="Close"
- type="button">
- <i class="fa fa-times dropdown-menu-close-icon"></i>
- </button>
- </div>
- <div class="dropdown-input">
- <input
- type="search"
- class="dropdown-input-field"
- placeholder="Search"
- autocomplete="off" />
- <i class="fa fa-search dropdown-input-search"></i>
- <i role="button" class="fa fa-times dropdown-input-clear js-dropdown-input-clear"></i>
- </div>
- <div class="dropdown-content"></div>
- <div class="dropdown-loading"><i class="fa fa-spinner fa-spin"></i></div>
- </div>
- </div>
- `,
-});
diff --git a/app/assets/javascripts/boards/components/modal/filters/milestone.js b/app/assets/javascripts/boards/components/modal/filters/milestone.js
deleted file mode 100644
index 436aa981c59..00000000000
--- a/app/assets/javascripts/boards/components/modal/filters/milestone.js
+++ /dev/null
@@ -1,56 +0,0 @@
-/* eslint-disable no-new */
-/* global Vue */
-/* global MilestoneSelect */
-module.exports = Vue.extend({
- name: 'filter-milestone',
- props: {
- milestonePath: {
- type: String,
- required: true,
- },
- },
- mounted() {
- new MilestoneSelect(null, this.$refs.dropdown);
- },
- template: `
- <div class="dropdown">
- <button
- class="dropdown-menu-toggle js-milestone-select"
- type="button"
- data-toggle="dropdown"
- data-show-any="true"
- data-show-upcoming="true"
- data-show-started="true"
- data-field-name="milestone_title"
- :data-milestones="milestonePath"
- ref="dropdown">
- <span class="dropdown-toggle-text">
- Milestone
- </span>
- <i class="fa fa-chevron-down"></i>
- </button>
- <div class="dropdown-menu dropdown-select dropdown-menu-selectable dropdown-menu-milestone">
- <div class="dropdown-title">
- <span>Filter by milestone</span>
- <button
- class="dropdown-title-button dropdown-menu-close"
- aria-label="Close"
- type="button">
- <i class="fa fa-times dropdown-menu-close-icon"></i>
- </button>
- </div>
- <div class="dropdown-input">
- <input
- type="search"
- class="dropdown-input-field"
- placeholder="Search milestones"
- autocomplete="off" />
- <i class="fa fa-search dropdown-input-search"></i>
- <i role="button" class="fa fa-times dropdown-input-clear js-dropdown-input-clear"></i>
- </div>
- <div class="dropdown-content"></div>
- <div class="dropdown-loading"><i class="fa fa-spinner fa-spin"></i></div>
- </div>
- </div>
- `,
-});
diff --git a/app/assets/javascripts/boards/components/modal/filters/user.js b/app/assets/javascripts/boards/components/modal/filters/user.js
deleted file mode 100644
index 8523028c29c..00000000000
--- a/app/assets/javascripts/boards/components/modal/filters/user.js
+++ /dev/null
@@ -1,96 +0,0 @@
-/* eslint-disable no-new */
-/* global Vue */
-/* global UsersSelect */
-module.exports = Vue.extend({
- name: 'filter-user',
- props: {
- toggleClassName: {
- type: String,
- required: true,
- },
- dropdownClassName: {
- type: String,
- required: false,
- default: '',
- },
- toggleLabel: {
- type: String,
- required: true,
- },
- fieldName: {
- type: String,
- required: true,
- },
- nullUser: {
- type: Boolean,
- required: false,
- default: false,
- },
- projectId: {
- type: Number,
- required: true,
- },
- },
- mounted() {
- new UsersSelect(null, this.$refs.dropdown);
- },
- computed: {
- currentUsername() {
- return gon.current_username;
- },
- dropdownTitle() {
- return `Filter by ${this.toggleLabel.toLowerCase()}`;
- },
- inputPlaceholder() {
- return `Search ${this.toggleLabel.toLowerCase()}`;
- },
- },
- template: `
- <div class="dropdown">
- <button
- class="dropdown-menu-toggle js-user-search"
- :class="toggleClassName"
- type="button"
- data-toggle="dropdown"
- data-current-user="true"
- :data-any-user="'Any ' + toggleLabel"
- :data-null-user="nullUser"
- :data-field-name="fieldName"
- :data-project-id="projectId"
- :data-first-user="currentUsername"
- ref="dropdown">
- <span class="dropdown-toggle-text">
- {{ toggleLabel }}
- </span>
- <i class="fa fa-chevron-down"></i>
- </button>
- <div
- class="dropdown-menu dropdown-select dropdown-menu-user dropdown-menu-selectable"
- :class="dropdownClassName">
- <div class="dropdown-title">
- {{ dropdownTitle }}
- <button
- class="dropdown-title-button dropdown-menu-close"
- aria-label="Close"
- type="button">
- <i class="fa fa-times dropdown-menu-close-icon"></i>
- </button>
- </div>
- <div class="dropdown-input">
- <input
- type="search"
- class="dropdown-input-field"
- autocomplete="off"
- :placeholder="inputPlaceholder" />
- <i class="fa fa-search dropdown-input-search"></i>
- <i
- role="button"
- class="fa fa-times dropdown-input-clear js-dropdown-input-clear">
- </i>
- </div>
- <div class="dropdown-content"></div>
- <div class="dropdown-loading"><i class="fa fa-spinner fa-spin"></i></div>
- </div>
- </div>
- `,
-});
diff --git a/app/assets/javascripts/boards/components/modal/header.js b/app/assets/javascripts/boards/components/modal/header.js
index 70c088f9054..116e29cd177 100644
--- a/app/assets/javascripts/boards/components/modal/header.js
+++ b/app/assets/javascripts/boards/components/modal/header.js
@@ -1,6 +1,7 @@
-/* global Vue */
+import Vue from 'vue';
+import modalFilters from './filters';
+
require('./tabs');
-const modalFilters = require('./filters');
(() => {
const ModalStore = gl.issueBoards.ModalStore;
@@ -66,16 +67,7 @@ const modalFilters = require('./filters');
<div
class="add-issues-search append-bottom-10"
v-if="showSearch">
- <modal-filters
- :project-id="projectId"
- :milestone-path="milestonePath"
- :label-path="labelPath">
- </modal-filters>
- <input
- placeholder="Search issues..."
- class="form-control"
- type="search"
- v-model="searchTerm" />
+ <modal-filters :store="filter" />
<button
type="button"
class="btn btn-success btn-inverted prepend-left-10"
diff --git a/app/assets/javascripts/boards/components/modal/index.js b/app/assets/javascripts/boards/components/modal/index.js
index f290cd13763..1b66c8b922d 100644
--- a/app/assets/javascripts/boards/components/modal/index.js
+++ b/app/assets/javascripts/boards/components/modal/index.js
@@ -1,5 +1,6 @@
/* global Vue */
/* global ListIssue */
+import queryData from '../../utils/query_data';
require('./header');
require('./list');
@@ -47,9 +48,6 @@ require('./empty_state');
page() {
this.loadIssues();
},
- searchTerm() {
- this.searchOperation();
- },
showAddIssuesModal() {
if (this.showAddIssuesModal && !this.issues.length) {
this.loading = true;
@@ -72,19 +70,13 @@ require('./empty_state');
},
},
methods: {
- searchOperation: _.debounce(function searchOperationDebounce() {
- this.loadIssues(true);
- }, 500),
loadIssues(clearIssues = false) {
if (!this.showAddIssuesModal) return false;
- const queryData = Object.assign({}, this.filter, {
- search: this.searchTerm,
+ return gl.boardService.getBacklog(queryData(this.filter.path, {
page: this.page,
per: this.perPage,
- });
-
- return gl.boardService.getBacklog(queryData).then((res) => {
+ })).then((res) => {
const data = res.json();
if (clearIssues) {
diff --git a/app/assets/javascripts/boards/filtered_search_boards.js b/app/assets/javascripts/boards/filtered_search_boards.js
index 47448b02bdd..101732309ea 100644
--- a/app/assets/javascripts/boards/filtered_search_boards.js
+++ b/app/assets/javascripts/boards/filtered_search_boards.js
@@ -1,3 +1,6 @@
+/* eslint-disable class-methods-use-this */
+import FilteredSearchContainer from '../filtered_search/container';
+
export default class FilteredSearchBoards extends gl.FilteredSearchManager {
constructor(store, updateUrl = false) {
super('boards');
@@ -18,13 +21,17 @@ export default class FilteredSearchBoards extends gl.FilteredSearchManager {
}
}
- updateTokens() {
- const tokens = document.querySelectorAll('.js-visual-token');
+ removeTokens() {
+ const tokens = FilteredSearchContainer.container.querySelectorAll('.js-visual-token');
// Remove all the tokens as they will be replaced by the search manager
[].forEach.call(tokens, (el) => {
el.parentNode.removeChild(el);
});
+ }
+
+ updateTokens() {
+ this.removeTokens();
this.loadSearchParamsFromURL();
diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js
index 3251ca76b26..f18ad2a0fac 100644
--- a/app/assets/javascripts/boards/models/list.js
+++ b/app/assets/javascripts/boards/models/list.js
@@ -1,6 +1,7 @@
/* eslint-disable space-before-function-paren, no-underscore-dangle, class-methods-use-this, consistent-return, no-shadow, no-param-reassign, max-len, no-unused-vars */
/* global ListIssue */
/* global ListLabel */
+import queryData from '../utils/query_data';
class List {
constructor (obj) {
@@ -64,25 +65,7 @@ class List {
}
getIssues (emptyIssues = true) {
- const data = gl.issueBoards.BoardsStore.filter.path.split('&').reduce((data, filterParam) => {
- if (filterParam === '') return data;
- const paramSplit = filterParam.split('=');
- const paramKeyNormalized = paramSplit[0].replace('[]', '');
- const isArray = paramSplit[0].indexOf('[]');
- const value = decodeURIComponent(paramSplit[1]).replace(/\+/g, ' ');
-
- if (isArray !== -1) {
- if (!data[paramKeyNormalized]) {
- data[paramKeyNormalized] = [];
- }
-
- data[paramKeyNormalized].push(value);
- } else {
- data[paramKeyNormalized] = value;
- }
-
- return data;
- }, { page: this.page });
+ const data = queryData(gl.issueBoards.BoardsStore.filter.path, { page: this.page });
if (this.label && data.label_name) {
data.label_name = data.label_name.filter(label => label !== this.label.title);
diff --git a/app/assets/javascripts/boards/stores/modal_store.js b/app/assets/javascripts/boards/stores/modal_store.js
index 15fc6c79e8d..7ee266a831f 100644
--- a/app/assets/javascripts/boards/stores/modal_store.js
+++ b/app/assets/javascripts/boards/stores/modal_store.js
@@ -17,17 +17,9 @@
loadingNewPage: false,
page: 1,
perPage: 50,
- };
-
- this.setDefaultFilter();
- }
-
- setDefaultFilter() {
- this.store.filter = {
- author_id: '',
- assignee_id: '',
- milestone_title: '',
- label_name: [],
+ filter: {
+ path: '',
+ },
};
}
diff --git a/app/assets/javascripts/boards/utils/query_data.js b/app/assets/javascripts/boards/utils/query_data.js
new file mode 100644
index 00000000000..2cd3c146f11
--- /dev/null
+++ b/app/assets/javascripts/boards/utils/query_data.js
@@ -0,0 +1,21 @@
+export default (path, extraData) => path.split('&').reduce((dataParam, filterParam) => {
+ if (filterParam === '') return dataParam;
+
+ const data = dataParam;
+ const paramSplit = filterParam.split('=');
+ const paramKeyNormalized = paramSplit[0].replace('[]', '');
+ const isArray = paramSplit[0].indexOf('[]');
+ const value = decodeURIComponent(paramSplit[1]).replace(/\+/g, ' ');
+
+ if (isArray !== -1) {
+ if (!data[paramKeyNormalized]) {
+ data[paramKeyNormalized] = [];
+ }
+
+ data[paramKeyNormalized].push(value);
+ } else {
+ data[paramKeyNormalized] = value;
+ }
+
+ return data;
+}, extraData);
diff --git a/app/assets/javascripts/filtered_search/container.js b/app/assets/javascripts/filtered_search/container.js
new file mode 100644
index 00000000000..2243c4dd2c5
--- /dev/null
+++ b/app/assets/javascripts/filtered_search/container.js
@@ -0,0 +1,14 @@
+/* eslint-disable class-methods-use-this */
+let container = document;
+
+class FilteredSearchContainerClass {
+ set container(containerParam) {
+ container = containerParam;
+ }
+
+ get container() {
+ return container;
+ }
+}
+
+export default new FilteredSearchContainerClass();
diff --git a/app/assets/javascripts/filtered_search/dropdown_hint.js b/app/assets/javascripts/filtered_search/dropdown_hint.js
index 28e5e3232cb..98dcb697af9 100644
--- a/app/assets/javascripts/filtered_search/dropdown_hint.js
+++ b/app/assets/javascripts/filtered_search/dropdown_hint.js
@@ -45,7 +45,7 @@ require('./filtered_search_dropdown');
gl.FilteredSearchVisualTokens.addSearchVisualToken(searchTerms.join(' '));
}
- gl.FilteredSearchDropdownManager.addWordToInput(token.replace(':', ''));
+ gl.FilteredSearchDropdownManager.addWordToInput(token.replace(':', ''), '', false, this.container);
}
this.dismissDropdown();
this.dispatchInputEvent();
diff --git a/app/assets/javascripts/filtered_search/dropdown_utils.js b/app/assets/javascripts/filtered_search/dropdown_utils.js
index 77bf191f343..432b0c0dfd2 100644
--- a/app/assets/javascripts/filtered_search/dropdown_utils.js
+++ b/app/assets/javascripts/filtered_search/dropdown_utils.js
@@ -1,3 +1,5 @@
+import FilteredSearchContainer from './container';
+
(() => {
class DropdownUtils {
static getEscapedText(text) {
@@ -85,7 +87,8 @@
// Determines the full search query (visual tokens + input)
static getSearchQuery(untilInput = false) {
- const tokens = [].slice.call(document.querySelectorAll('.tokens-container li'));
+ const container = FilteredSearchContainer.container;
+ const tokens = [].slice.call(container.querySelectorAll('.tokens-container li'));
const values = [];
if (untilInput) {
@@ -114,7 +117,7 @@
const { isLastVisualTokenValid } =
gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
- const input = document.querySelector('.filtered-search');
+ const input = FilteredSearchContainer.container.querySelector('.filtered-search');
const inputValue = input && input.value;
if (isLastVisualTokenValid) {
diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
index d37c812c1f7..5fbe0450bb8 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
@@ -1,12 +1,14 @@
/* global DropLab */
+import FilteredSearchContainer from './container';
(() => {
class FilteredSearchDropdownManager {
constructor(baseEndpoint = '', page) {
+ this.container = FilteredSearchContainer.container;
this.baseEndpoint = baseEndpoint.replace(/\/$/, '');
this.tokenizer = gl.FilteredSearchTokenizer;
this.filteredSearchTokenKeys = gl.FilteredSearchTokenKeys;
- this.filteredSearchInput = document.querySelector('.filtered-search');
+ this.filteredSearchInput = this.container.querySelector('.filtered-search');
this.page = page;
this.setupMapping();
@@ -31,35 +33,35 @@
author: {
reference: null,
gl: 'DropdownUser',
- element: document.querySelector('#js-dropdown-author'),
+ element: this.container.querySelector('#js-dropdown-author'),
},
assignee: {
reference: null,
gl: 'DropdownUser',
- element: document.querySelector('#js-dropdown-assignee'),
+ element: this.container.querySelector('#js-dropdown-assignee'),
},
milestone: {
reference: null,
gl: 'DropdownNonUser',
extraArguments: [`${this.baseEndpoint}/milestones.json`, '%'],
- element: document.querySelector('#js-dropdown-milestone'),
+ element: this.container.querySelector('#js-dropdown-milestone'),
},
label: {
reference: null,
gl: 'DropdownNonUser',
extraArguments: [`${this.baseEndpoint}/labels.json`, '~'],
- element: document.querySelector('#js-dropdown-label'),
+ element: this.container.querySelector('#js-dropdown-label'),
},
hint: {
reference: null,
gl: 'DropdownHint',
- element: document.querySelector('#js-dropdown-hint'),
+ element: this.container.querySelector('#js-dropdown-hint'),
},
};
}
static addWordToInput(tokenName, tokenValue = '', clicked = false) {
- const input = document.querySelector('.filtered-search');
+ const input = FilteredSearchContainer.container.querySelector('.filtered-search');
gl.FilteredSearchVisualTokens.addFilterVisualToken(tokenName, tokenValue);
input.value = '';
@@ -75,13 +77,13 @@
updateDropdownOffset(key) {
// Always align dropdown with the input field
- let offset = this.filteredSearchInput.getBoundingClientRect().left - document.querySelector('.scroll-container').getBoundingClientRect().left;
+ let offset = this.filteredSearchInput.getBoundingClientRect().left - this.container.querySelector('.scroll-container').getBoundingClientRect().left;
const maxInputWidth = 240;
const currentDropdownWidth = this.mapping[key].element.clientWidth || maxInputWidth;
// Make sure offset never exceeds the input container
- const offsetMaxWidth = document.querySelector('.scroll-container').clientWidth - currentDropdownWidth;
+ const offsetMaxWidth = this.container.querySelector('.scroll-container').clientWidth - currentDropdownWidth;
if (offsetMaxWidth < offset) {
offset = offsetMaxWidth;
}
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js
index f885932bd91..7ace51748aa 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js
@@ -1,9 +1,12 @@
+import FilteredSearchContainer from './container';
+
(() => {
class FilteredSearchManager {
constructor(page) {
- this.filteredSearchInput = document.querySelector('.filtered-search');
- this.clearSearchButton = document.querySelector('.clear-search');
- this.tokensContainer = document.querySelector('.tokens-container');
+ this.container = FilteredSearchContainer.container;
+ this.filteredSearchInput = this.container.querySelector('.filtered-search');
+ this.clearSearchButton = this.container.querySelector('.clear-search');
+ this.tokensContainer = this.container.querySelector('.tokens-container');
this.filteredSearchTokenKeys = gl.FilteredSearchTokenKeys;
if (this.filteredSearchInput) {
@@ -132,7 +135,7 @@
}
unselectEditTokens(e) {
- const inputContainer = document.querySelector('.filtered-search-input-container');
+ const inputContainer = this.container.querySelector('.filtered-search-input-container');
const isElementInFilteredSearch = inputContainer && inputContainer.contains(e.target);
const isElementInFilterDropdown = e.target.closest('.filter-dropdown') !== null;
const isElementTokensContainer = e.target.classList.contains('tokens-container');
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 320afa26130..e48d7196c7b 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
@@ -1,6 +1,8 @@
+import FilteredSearchContainer from './container';
+
class FilteredSearchVisualTokens {
static getLastVisualTokenBeforeInput() {
- const inputLi = document.querySelector('.input-token');
+ const inputLi = FilteredSearchContainer.container.querySelector('.input-token');
const lastVisualToken = inputLi && inputLi.previousElementSibling;
return {
@@ -10,7 +12,7 @@ class FilteredSearchVisualTokens {
}
static unselectTokens() {
- const otherTokens = document.querySelectorAll('.js-visual-token .selectable.selected');
+ const otherTokens = FilteredSearchContainer.container.querySelectorAll('.js-visual-token .selectable.selected');
[].forEach.call(otherTokens, t => t.classList.remove('selected'));
}
@@ -24,7 +26,7 @@ class FilteredSearchVisualTokens {
}
static removeSelectedToken() {
- const selected = document.querySelector('.js-visual-token .selected');
+ const selected = FilteredSearchContainer.container.querySelector('.js-visual-token .selected');
if (selected) {
const li = selected.closest('.js-visual-token');
@@ -54,8 +56,8 @@ class FilteredSearchVisualTokens {
}
li.querySelector('.name').innerText = name;
- const tokensContainer = document.querySelector('.tokens-container');
- const input = document.querySelector('.filtered-search');
+ const tokensContainer = FilteredSearchContainer.container.querySelector('.tokens-container');
+ const input = FilteredSearchContainer.container.querySelector('.filtered-search');
tokensContainer.insertBefore(li, input.parentElement);
}
@@ -77,14 +79,14 @@ class FilteredSearchVisualTokens {
const addVisualTokenElement = FilteredSearchVisualTokens.addVisualTokenElement;
if (isLastVisualTokenValid) {
- addVisualTokenElement(tokenName, tokenValue);
+ addVisualTokenElement(tokenName, tokenValue, false);
} else {
const previousTokenName = lastVisualToken.querySelector('.name').innerText;
- const tokensContainer = document.querySelector('.tokens-container');
+ const tokensContainer = FilteredSearchContainer.container.querySelector('.tokens-container');
tokensContainer.removeChild(lastVisualToken);
const value = tokenValue || tokenName;
- addVisualTokenElement(previousTokenName, value);
+ addVisualTokenElement(previousTokenName, value, false);
}
}
@@ -129,7 +131,7 @@ class FilteredSearchVisualTokens {
}
static tokenizeInput() {
- const input = document.querySelector('.filtered-search');
+ const input = FilteredSearchContainer.container.querySelector('.filtered-search');
const { isLastVisualTokenValid } =
gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
@@ -145,7 +147,7 @@ class FilteredSearchVisualTokens {
}
static editToken(token) {
- const input = document.querySelector('.filtered-search');
+ const input = FilteredSearchContainer.container.querySelector('.filtered-search');
FilteredSearchVisualTokens.tokenizeInput();
@@ -174,9 +176,9 @@ class FilteredSearchVisualTokens {
}
static moveInputToTheRight() {
- const input = document.querySelector('.filtered-search');
+ const input = FilteredSearchContainer.container.querySelector('.filtered-search');
const inputLi = input.parentElement;
- const tokenContainer = document.querySelector('.tokens-container');
+ const tokenContainer = FilteredSearchContainer.container.querySelector('.tokens-container');
FilteredSearchVisualTokens.tokenizeInput();
diff --git a/app/assets/javascripts/todos.js b/app/assets/javascripts/todos.js
index caaf6484a34..8be58023c84 100644
--- a/app/assets/javascripts/todos.js
+++ b/app/assets/javascripts/todos.js
@@ -5,6 +5,7 @@ class Todos {
constructor() {
this.initFilters();
this.bindEvents();
+ this.todo_ids = [];
this.cleanupWrapper = this.cleanup.bind(this);
document.addEventListener('beforeunload', this.cleanupWrapper);
@@ -17,16 +18,16 @@ class Todos {
unbindEvents() {
$('.js-done-todo, .js-undo-todo, .js-add-todo').off('click', this.updateRowStateClickedWrapper);
- $('.js-todos-mark-all').off('click', this.allDoneClickedWrapper);
+ $('.js-todos-mark-all', '.js-todos-undo-all').off('click', this.updateallStateClickedWrapper);
$('.todo').off('click', this.goToTodoUrl);
}
bindEvents() {
this.updateRowStateClickedWrapper = this.updateRowStateClicked.bind(this);
- this.allDoneClickedWrapper = this.allDoneClicked.bind(this);
+ this.updateAllStateClickedWrapper = this.updateAllStateClicked.bind(this);
$('.js-done-todo, .js-undo-todo, .js-add-todo').on('click', this.updateRowStateClickedWrapper);
- $('.js-todos-mark-all').on('click', this.allDoneClickedWrapper);
+ $('.js-todos-mark-all, .js-todos-undo-all').on('click', this.updateAllStateClickedWrapper);
$('.todo').on('click', this.goToTodoUrl);
}
@@ -57,14 +58,14 @@ class Todos {
e.preventDefault();
const target = e.target;
- target.setAttribute('disabled', '');
+ target.setAttribute('disabled', true);
target.classList.add('disabled');
$.ajax({
type: 'POST',
- url: target.getAttribute('href'),
+ url: target.dataset.href,
dataType: 'json',
data: {
- '_method': target.getAttribute('data-method'),
+ '_method': target.dataset.method,
},
success: (data) => {
this.updateRowState(target);
@@ -73,25 +74,6 @@ class Todos {
});
}
- allDoneClicked(e) {
- e.preventDefault();
- const $target = $(e.currentTarget);
- $target.disable();
- $.ajax({
- type: 'POST',
- url: $target.attr('href'),
- dataType: 'json',
- data: {
- '_method': 'delete',
- },
- success: (data) => {
- $target.remove();
- $('.js-todos-all').html('<div class="nothing-here-block">You\'re all done!</div>');
- this.updateBadges(data);
- },
- });
- }
-
updateRowState(target) {
const row = target.closest('li');
const restoreBtn = row.querySelector('.js-undo-todo');
@@ -112,6 +94,41 @@ class Todos {
}
}
+ updateAllStateClicked(e) {
+ e.preventDefault();
+
+ const target = e.currentTarget;
+ const requestData = { '_method': target.dataset.method, ids: this.todo_ids };
+ target.setAttribute('disabled', true);
+ target.classList.add('disabled');
+ $.ajax({
+ type: 'POST',
+ url: target.dataset.href,
+ dataType: 'json',
+ data: requestData,
+ success: (data) => {
+ this.updateAllState(target, data);
+ return this.updateBadges(data);
+ },
+ });
+ }
+
+ updateAllState(target, data) {
+ const markAllDoneBtn = document.querySelector('.js-todos-mark-all');
+ const undoAllBtn = document.querySelector('.js-todos-undo-all');
+ const todoListContainer = document.querySelector('.js-todos-list-container');
+ const nothingHereContainer = document.querySelector('.js-nothing-here-container');
+
+ target.removeAttribute('disabled');
+ target.classList.remove('disabled');
+
+ this.todo_ids = (target === markAllDoneBtn) ? data.updated_ids : [];
+ undoAllBtn.classList.toggle('hidden');
+ markAllDoneBtn.classList.toggle('hidden');
+ todoListContainer.classList.toggle('hidden');
+ nothingHereContainer.classList.toggle('hidden');
+ }
+
updateBadges(data) {
$(document).trigger('todo:toggle', data.count);
document.querySelector('.todos-pending .badge').innerHTML = data.count;
diff --git a/app/assets/stylesheets/framework/awards.scss b/app/assets/stylesheets/framework/awards.scss
index f363affa46c..546718ddaf8 100644
--- a/app/assets/stylesheets/framework/awards.scss
+++ b/app/assets/stylesheets/framework/awards.scss
@@ -96,11 +96,9 @@
.award-control {
margin: 3px 5px 3px 0;
- padding: 5px 6px;
+ padding: .35em .4em;
outline: 0;
- line-height: 1;
-
&.disabled {
cursor: default;
@@ -140,10 +138,12 @@
}
.icon,
+ gl-emoji,
.award-control-icon {
- float: left;
- margin-right: 5px;
- font-size: 18px;
+ vertical-align: middle;
+ margin-right: 0.15em;
+ font-size: 1.5em;
+ line-height: 1;
}
.award-control-icon-loading {
@@ -154,4 +154,8 @@
color: $border-gray-normal;
margin-top: 1px;
}
+
+ .award-control-text {
+ vertical-align: middle;
+ }
}
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index 9a36d76136b..f9ee33019cd 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -420,12 +420,9 @@
display: -webkit-flex;
display: flex;
- .form-control {
- margin-left: auto;
-
- @media (min-width: $screen-sm-min) {
- max-width: 200px;
- }
+ .issues-filters {
+ -webkit-flex: 1;
+ flex: 1;
}
}