summaryrefslogtreecommitdiff
path: root/app/assets
diff options
context:
space:
mode:
authorShinya Maeda <shinya@gitlab.com>2018-06-12 13:13:03 +0900
committerShinya Maeda <shinya@gitlab.com>2018-06-12 13:13:03 +0900
commitd7a3180d06e7b16728d4f23b1e68007c9c2f3b9a (patch)
treed111815ee462b6dd0b29af9c158d7d1abc24f505 /app/assets
parent644529590a263f8db215d288c2f59abbe632a09b (diff)
parent26c9d71666d0350b17431a75d8c31d0316bd7220 (diff)
downloadgitlab-ce-d7a3180d06e7b16728d4f23b1e68007c9c2f3b9a.tar.gz
Merge branch 'master' into build-chunks-on-object-storage
Diffstat (limited to 'app/assets')
-rw-r--r--app/assets/images/ci_favicons/dev/favicon_status_canceled.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/dev/favicon_status_created.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/dev/favicon_status_failed.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/dev/favicon_status_manual.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/dev/favicon_status_not_found.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/dev/favicon_status_pending.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/dev/favicon_status_running.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/dev/favicon_status_skipped.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/dev/favicon_status_success.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/dev/favicon_status_warning.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_canceled.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_canceled.pngbin0 -> 864 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_created.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_created.pngbin0 -> 889 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_failed.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_failed.pngbin0 -> 1015 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_manual.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_manual.pngbin0 -> 1067 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_not_found.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_not_found.pngbin0 -> 945 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_pending.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_pending.pngbin0 -> 919 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_running.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_running.pngbin0 -> 1077 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_skipped.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_skipped.pngbin0 -> 923 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_success.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_success.pngbin0 -> 1044 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_warning.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_warning.pngbin0 -> 830 bytes
-rw-r--r--app/assets/images/favicon-blue.pngbin0 -> 1522 bytes
-rw-r--r--app/assets/images/favicon-yellow.icobin5430 -> 0 bytes
-rw-r--r--app/assets/images/favicon-yellow.pngbin0 -> 1667 bytes
-rw-r--r--app/assets/images/favicon.icobin5430 -> 0 bytes
-rw-r--r--app/assets/images/favicon.pngbin0 -> 1611 bytes
-rw-r--r--app/assets/javascripts/boards/components/board_list.vue41
-rw-r--r--app/assets/javascripts/boards/components/board_new_issue.vue4
-rw-r--r--app/assets/javascripts/boards/components/new_list_dropdown.js1
-rw-r--r--app/assets/javascripts/boards/index.js2
-rw-r--r--app/assets/javascripts/boards/models/assignee.js12
-rw-r--r--app/assets/javascripts/boards/models/list.js87
-rw-r--r--app/assets/javascripts/boards/services/board_service.js10
-rw-r--r--app/assets/javascripts/boards/stores/boards_store.js24
-rw-r--r--app/assets/javascripts/clusters/stores/clusters_store.js2
-rw-r--r--app/assets/javascripts/diff_notes/diff_notes_bundle.js5
-rw-r--r--app/assets/javascripts/gl_dropdown.js6
-rw-r--r--app/assets/javascripts/group_label_subscription.js18
-rw-r--r--app/assets/javascripts/ide/components/panes/right.vue12
-rw-r--r--app/assets/javascripts/ide/components/repo_editor.vue19
-rw-r--r--app/assets/javascripts/ide/lib/common/model.js16
-rw-r--r--app/assets/javascripts/ide/lib/common/model_manager.js5
-rw-r--r--app/assets/javascripts/ide/lib/diff/controller.js4
-rw-r--r--app/assets/javascripts/ide/lib/editor.js36
-rw-r--r--app/assets/javascripts/ide/monaco_loader.js16
-rw-r--r--app/assets/javascripts/ide/stores/modules/merge_requests/actions.js9
-rw-r--r--app/assets/javascripts/ide/stores/modules/pipelines/actions.js12
-rw-r--r--app/assets/javascripts/init_changes_dropdown.js2
-rw-r--r--app/assets/javascripts/job.js11
-rw-r--r--app/assets/javascripts/label_manager.js15
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js51
-rw-r--r--app/assets/javascripts/lib/utils/datetime_utility.js113
-rw-r--r--app/assets/javascripts/lib/utils/http_status.js1
-rw-r--r--app/assets/javascripts/lib/utils/sticky.js23
-rw-r--r--app/assets/javascripts/lib/utils/url_utility.js4
-rw-r--r--app/assets/javascripts/main.js1
-rw-r--r--app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js14
-rw-r--r--app/assets/javascripts/project_label_subscription.js28
-rw-r--r--app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue10
-rw-r--r--app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue10
-rw-r--r--app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue10
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue11
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author_time.vue9
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue5
-rw-r--r--app/assets/javascripts/vue_shared/components/file_icon/file_icon_map.js2
-rw-r--r--app/assets/javascripts/vue_shared/models/assignee.js13
-rw-r--r--app/assets/stylesheets/bootstrap_migration.scss51
-rw-r--r--app/assets/stylesheets/framework/buttons.scss4
-rw-r--r--app/assets/stylesheets/framework/common.scss8
-rw-r--r--app/assets/stylesheets/framework/filters.scss1
-rw-r--r--app/assets/stylesheets/framework/forms.scss2
-rw-r--r--app/assets/stylesheets/framework/gitlab_theme.scss162
-rw-r--r--app/assets/stylesheets/framework/header.scss18
-rw-r--r--app/assets/stylesheets/framework/pagination.scss8
-rw-r--r--app/assets/stylesheets/framework/tables.scss90
-rw-r--r--app/assets/stylesheets/framework/timeline.scss4
-rw-r--r--app/assets/stylesheets/framework/variables.scss70
-rw-r--r--app/assets/stylesheets/pages/boards.scss3
-rw-r--r--app/assets/stylesheets/pages/builds.scss16
-rw-r--r--app/assets/stylesheets/pages/labels.scss237
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss86
-rw-r--r--app/assets/stylesheets/pages/profiles/preferences.scss75
-rw-r--r--app/assets/stylesheets/pages/search.scss5
-rw-r--r--app/assets/stylesheets/pages/settings.scss10
-rw-r--r--app/assets/stylesheets/pages/settings_ci_cd.scss9
-rw-r--r--app/assets/stylesheets/performance_bar.scss1
97 files changed, 1043 insertions, 499 deletions
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_canceled.ico b/app/assets/images/ci_favicons/dev/favicon_status_canceled.ico
deleted file mode 100644
index 4af3582b60d..00000000000
--- a/app/assets/images/ci_favicons/dev/favicon_status_canceled.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_created.ico b/app/assets/images/ci_favicons/dev/favicon_status_created.ico
deleted file mode 100644
index 13639da2e8a..00000000000
--- a/app/assets/images/ci_favicons/dev/favicon_status_created.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_failed.ico b/app/assets/images/ci_favicons/dev/favicon_status_failed.ico
deleted file mode 100644
index 5f0e711b104..00000000000
--- a/app/assets/images/ci_favicons/dev/favicon_status_failed.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_manual.ico b/app/assets/images/ci_favicons/dev/favicon_status_manual.ico
deleted file mode 100644
index 8b1168a1267..00000000000
--- a/app/assets/images/ci_favicons/dev/favicon_status_manual.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_not_found.ico b/app/assets/images/ci_favicons/dev/favicon_status_not_found.ico
deleted file mode 100644
index ed19b69e1c5..00000000000
--- a/app/assets/images/ci_favicons/dev/favicon_status_not_found.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_pending.ico b/app/assets/images/ci_favicons/dev/favicon_status_pending.ico
deleted file mode 100644
index 5dfefd4cc5a..00000000000
--- a/app/assets/images/ci_favicons/dev/favicon_status_pending.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_running.ico b/app/assets/images/ci_favicons/dev/favicon_status_running.ico
deleted file mode 100644
index a41539c0e3e..00000000000
--- a/app/assets/images/ci_favicons/dev/favicon_status_running.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_skipped.ico b/app/assets/images/ci_favicons/dev/favicon_status_skipped.ico
deleted file mode 100644
index 2c1ae552b93..00000000000
--- a/app/assets/images/ci_favicons/dev/favicon_status_skipped.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_success.ico b/app/assets/images/ci_favicons/dev/favicon_status_success.ico
deleted file mode 100644
index 70f0ca61eca..00000000000
--- a/app/assets/images/ci_favicons/dev/favicon_status_success.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_warning.ico b/app/assets/images/ci_favicons/dev/favicon_status_warning.ico
deleted file mode 100644
index db289e03eb1..00000000000
--- a/app/assets/images/ci_favicons/dev/favicon_status_warning.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_canceled.ico b/app/assets/images/ci_favicons/favicon_status_canceled.ico
deleted file mode 100644
index 23adcffff50..00000000000
--- a/app/assets/images/ci_favicons/favicon_status_canceled.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_canceled.png b/app/assets/images/ci_favicons/favicon_status_canceled.png
new file mode 100644
index 00000000000..8adaa9c600b
--- /dev/null
+++ b/app/assets/images/ci_favicons/favicon_status_canceled.png
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_created.ico b/app/assets/images/ci_favicons/favicon_status_created.ico
deleted file mode 100644
index f9d93b390d8..00000000000
--- a/app/assets/images/ci_favicons/favicon_status_created.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_created.png b/app/assets/images/ci_favicons/favicon_status_created.png
new file mode 100644
index 00000000000..ca788dd0034
--- /dev/null
+++ b/app/assets/images/ci_favicons/favicon_status_created.png
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_failed.ico b/app/assets/images/ci_favicons/favicon_status_failed.ico
deleted file mode 100644
index 28a22ebf724..00000000000
--- a/app/assets/images/ci_favicons/favicon_status_failed.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_failed.png b/app/assets/images/ci_favicons/favicon_status_failed.png
new file mode 100644
index 00000000000..93f1e2772fd
--- /dev/null
+++ b/app/assets/images/ci_favicons/favicon_status_failed.png
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_manual.ico b/app/assets/images/ci_favicons/favicon_status_manual.ico
deleted file mode 100644
index dbbf1abf30c..00000000000
--- a/app/assets/images/ci_favicons/favicon_status_manual.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_manual.png b/app/assets/images/ci_favicons/favicon_status_manual.png
new file mode 100644
index 00000000000..c926062c806
--- /dev/null
+++ b/app/assets/images/ci_favicons/favicon_status_manual.png
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_not_found.ico b/app/assets/images/ci_favicons/favicon_status_not_found.ico
deleted file mode 100644
index 49b9b232dd1..00000000000
--- a/app/assets/images/ci_favicons/favicon_status_not_found.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_not_found.png b/app/assets/images/ci_favicons/favicon_status_not_found.png
new file mode 100644
index 00000000000..df3049315a9
--- /dev/null
+++ b/app/assets/images/ci_favicons/favicon_status_not_found.png
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_pending.ico b/app/assets/images/ci_favicons/favicon_status_pending.ico
deleted file mode 100644
index 05962f3f148..00000000000
--- a/app/assets/images/ci_favicons/favicon_status_pending.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_pending.png b/app/assets/images/ci_favicons/favicon_status_pending.png
new file mode 100644
index 00000000000..f7d67d4a230
--- /dev/null
+++ b/app/assets/images/ci_favicons/favicon_status_pending.png
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_running.ico b/app/assets/images/ci_favicons/favicon_status_running.ico
deleted file mode 100644
index 7fa3d4d48d4..00000000000
--- a/app/assets/images/ci_favicons/favicon_status_running.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_running.png b/app/assets/images/ci_favicons/favicon_status_running.png
new file mode 100644
index 00000000000..ff4167c4b20
--- /dev/null
+++ b/app/assets/images/ci_favicons/favicon_status_running.png
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_skipped.ico b/app/assets/images/ci_favicons/favicon_status_skipped.ico
deleted file mode 100644
index b0c26b62068..00000000000
--- a/app/assets/images/ci_favicons/favicon_status_skipped.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_skipped.png b/app/assets/images/ci_favicons/favicon_status_skipped.png
new file mode 100644
index 00000000000..a9c36464b69
--- /dev/null
+++ b/app/assets/images/ci_favicons/favicon_status_skipped.png
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_success.ico b/app/assets/images/ci_favicons/favicon_status_success.ico
deleted file mode 100644
index b150960b5be..00000000000
--- a/app/assets/images/ci_favicons/favicon_status_success.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_success.png b/app/assets/images/ci_favicons/favicon_status_success.png
new file mode 100644
index 00000000000..bcc30c73f5f
--- /dev/null
+++ b/app/assets/images/ci_favicons/favicon_status_success.png
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_warning.ico b/app/assets/images/ci_favicons/favicon_status_warning.ico
deleted file mode 100644
index 7e71d71684d..00000000000
--- a/app/assets/images/ci_favicons/favicon_status_warning.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_warning.png b/app/assets/images/ci_favicons/favicon_status_warning.png
new file mode 100644
index 00000000000..6db3b0280f5
--- /dev/null
+++ b/app/assets/images/ci_favicons/favicon_status_warning.png
Binary files differ
diff --git a/app/assets/images/favicon-blue.png b/app/assets/images/favicon-blue.png
new file mode 100644
index 00000000000..2229fe79462
--- /dev/null
+++ b/app/assets/images/favicon-blue.png
Binary files differ
diff --git a/app/assets/images/favicon-yellow.ico b/app/assets/images/favicon-yellow.ico
deleted file mode 100644
index b650f277fb6..00000000000
--- a/app/assets/images/favicon-yellow.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/favicon-yellow.png b/app/assets/images/favicon-yellow.png
new file mode 100644
index 00000000000..2d5289818b4
--- /dev/null
+++ b/app/assets/images/favicon-yellow.png
Binary files differ
diff --git a/app/assets/images/favicon.ico b/app/assets/images/favicon.ico
deleted file mode 100644
index 3479cbbb46f..00000000000
--- a/app/assets/images/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/favicon.png b/app/assets/images/favicon.png
new file mode 100644
index 00000000000..845e0ec34a5
--- /dev/null
+++ b/app/assets/images/favicon.png
Binary files differ
diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue
index 84a7f277227..0692c96e767 100644
--- a/app/assets/javascripts/boards/components/board_list.vue
+++ b/app/assets/javascripts/boards/components/board_list.vue
@@ -87,10 +87,46 @@ export default {
mounted() {
const options = gl.issueBoards.getBoardSortableDefaultOptions({
scroll: true,
- group: 'issues',
disabled: this.disabled,
filter: '.board-list-count, .is-disabled',
dataIdAttr: 'data-issue-id',
+ group: {
+ name: 'issues',
+ /**
+ * Dynamically determine between which containers
+ * items can be moved or copied as
+ * Assignee lists (EE feature) require this behavior
+ */
+ pull: (to, from, dragEl, e) => {
+ // As per Sortable's docs, `to` should provide
+ // reference to exact sortable container on which
+ // we're trying to drag element, but either it is
+ // a library's bug or our markup structure is too complex
+ // that `to` never points to correct container
+ // See https://github.com/RubaXa/Sortable/issues/1037
+ //
+ // So we use `e.target` which is always accurate about
+ // which element we're currently dragging our card upon
+ // So from there, we can get reference to actual container
+ // and thus the container type to enable Copy or Move
+ if (e.target) {
+ const containerEl = e.target.closest('.js-board-list') || e.target.querySelector('.js-board-list');
+ const toBoardType = containerEl.dataset.boardType;
+
+ if (toBoardType) {
+ const fromBoardType = this.list.type;
+
+ if ((fromBoardType === 'assignee' && toBoardType === 'label') ||
+ (fromBoardType === 'label' && toBoardType === 'assignee')) {
+ return 'clone';
+ }
+ }
+ }
+
+ return true;
+ },
+ revertClone: true,
+ },
onStart: (e) => {
const card = this.$refs.issue[e.oldIndex];
@@ -179,10 +215,11 @@ export default {
:list="list"
v-if="list.type !== 'closed' && showIssueForm"/>
<ul
- class="board-list"
+ class="board-list js-board-list"
v-show="!loading"
ref="list"
:data-board="list.id"
+ :data-board-type="list.type"
:class="{ 'is-smaller': showIssueForm }">
<board-card
v-for="(issue, index) in issues"
diff --git a/app/assets/javascripts/boards/components/board_new_issue.vue b/app/assets/javascripts/boards/components/board_new_issue.vue
index e8dfd95f7ae..297c9eff38c 100644
--- a/app/assets/javascripts/boards/components/board_new_issue.vue
+++ b/app/assets/javascripts/boards/components/board_new_issue.vue
@@ -49,11 +49,12 @@ export default {
this.error = false;
const labels = this.list.label ? [this.list.label] : [];
+ const assignees = this.list.assignee ? [this.list.assignee] : [];
const issue = new ListIssue({
title: this.title,
labels,
subscribed: true,
- assignees: [],
+ assignees,
project_id: this.selectedProject.id,
});
@@ -141,4 +142,3 @@ export default {
</div>
</div>
</template>
-
diff --git a/app/assets/javascripts/boards/components/new_list_dropdown.js b/app/assets/javascripts/boards/components/new_list_dropdown.js
index 71f49319c36..6dcd4aaec43 100644
--- a/app/assets/javascripts/boards/components/new_list_dropdown.js
+++ b/app/assets/javascripts/boards/components/new_list_dropdown.js
@@ -56,6 +56,7 @@ gl.issueBoards.newListDropdownInit = () => {
filterable: true,
selectable: true,
multiSelect: true,
+ containerSelector: '.js-tab-container-labels .dropdown-page-one .dropdown-content',
clicked (options) {
const { e } = options;
const label = options.selectedObj;
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index 29ab13b8e0b..cdad8d238e3 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -7,6 +7,7 @@ import Vue from 'vue';
import Flash from '~/flash';
import { __ } from '~/locale';
import '~/vue_shared/models/label';
+import '~/vue_shared/models/assignee';
import FilteredSearchBoards from './filtered_search_boards';
import eventHub from './eventhub';
@@ -15,7 +16,6 @@ import './models/issue';
import './models/list';
import './models/milestone';
import './models/project';
-import './models/assignee';
import './stores/boards_store';
import ModalStore from './stores/modal_store';
import BoardService from './services/board_service';
diff --git a/app/assets/javascripts/boards/models/assignee.js b/app/assets/javascripts/boards/models/assignee.js
deleted file mode 100644
index 05dd449e4fd..00000000000
--- a/app/assets/javascripts/boards/models/assignee.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/* eslint-disable no-unused-vars */
-
-class ListAssignee {
- constructor(user, defaultAvatar) {
- this.id = user.id;
- this.name = user.name;
- this.username = user.username;
- this.avatar = user.avatar_url || defaultAvatar;
- }
-}
-
-window.ListAssignee = ListAssignee;
diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js
index 7144f4190e7..a79dd62e2e4 100644
--- a/app/assets/javascripts/boards/models/list.js
+++ b/app/assets/javascripts/boards/models/list.js
@@ -1,12 +1,14 @@
/* 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 ListLabel from '~/vue_shared/models/label';
+import ListAssignee from '~/vue_shared/models/assignee';
import queryData from '../utils/query_data';
const PER_PAGE = 20;
class List {
- constructor (obj, defaultAvatar) {
+ constructor(obj, defaultAvatar) {
this.id = obj.id;
this._uid = this.guid();
this.position = obj.position;
@@ -24,6 +26,9 @@ class List {
if (obj.label) {
this.label = new ListLabel(obj.label);
+ } else if (obj.user) {
+ this.assignee = new ListAssignee(obj.user);
+ this.title = this.assignee.name;
}
if (this.type !== 'blank' && this.id) {
@@ -34,14 +39,25 @@ class List {
}
guid() {
- const s4 = () => Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
+ const s4 = () =>
+ Math.floor((1 + Math.random()) * 0x10000)
+ .toString(16)
+ .substring(1);
return `${s4()}${s4()}-${s4()}-${s4()}-${s4()}-${s4()}${s4()}${s4()}`;
}
- save () {
+ save() {
+ const entity = this.label || this.assignee;
+ let entityType = '';
+ if (this.label) {
+ entityType = 'label_id';
+ } else {
+ entityType = 'assignee_id';
+ }
+
return gl.boardService.createList(this.label.id)
.then(res => res.data)
- .then((data) => {
+ .then(data => {
this.id = data.id;
this.type = data.list_type;
this.position = data.position;
@@ -50,25 +66,23 @@ class List {
});
}
- destroy () {
+ destroy() {
const index = gl.issueBoards.BoardsStore.state.lists.indexOf(this);
gl.issueBoards.BoardsStore.state.lists.splice(index, 1);
gl.issueBoards.BoardsStore.updateNewListDropdown(this.id);
- gl.boardService.destroyList(this.id)
- .catch(() => {
- // TODO: handle request error
- });
+ gl.boardService.destroyList(this.id).catch(() => {
+ // TODO: handle request error
+ });
}
- update () {
- gl.boardService.updateList(this.id, this.position)
- .catch(() => {
- // TODO: handle request error
- });
+ update() {
+ gl.boardService.updateList(this.id, this.position).catch(() => {
+ // TODO: handle request error
+ });
}
- nextPage () {
+ nextPage() {
if (this.issuesSize > this.issues.length) {
if (this.issues.length / PER_PAGE >= 1) {
this.page += 1;
@@ -78,7 +92,7 @@ class List {
}
}
- getIssues (emptyIssues = true) {
+ getIssues(emptyIssues = true) {
const data = queryData(gl.issueBoards.BoardsStore.filter.path, { page: this.page });
if (this.label && data.label_name) {
@@ -89,7 +103,8 @@ class List {
this.loading = true;
}
- return gl.boardService.getIssuesForList(this.id, data)
+ return gl.boardService
+ .getIssuesForList(this.id, data)
.then(res => res.data)
.then((data) => {
this.loading = false;
@@ -103,11 +118,12 @@ class List {
});
}
- newIssue (issue) {
+ newIssue(issue) {
this.addIssue(issue, null, 0);
this.issuesSize += 1;
- return gl.boardService.newIssue(this.id, issue)
+ return gl.boardService
+ .newIssue(this.id, issue)
.then(res => res.data)
.then((data) => {
issue.id = data.id;
@@ -123,13 +139,13 @@ class List {
});
}
- createIssues (data) {
- data.forEach((issueObj) => {
+ createIssues(data) {
+ data.forEach(issueObj => {
this.addIssue(new ListIssue(issueObj, this.defaultAvatar));
});
}
- addIssue (issue, listFrom, newIndex) {
+ addIssue(issue, listFrom, newIndex) {
let moveBeforeId = null;
let moveAfterId = null;
@@ -152,6 +168,13 @@ class List {
issue.addLabel(this.label);
}
+ if (this.assignee) {
+ if (listFrom && listFrom.type === 'assignee') {
+ issue.removeAssignee(listFrom.assignee);
+ }
+ issue.addAssignee(this.assignee);
+ }
+
if (listFrom) {
this.issuesSize += 1;
@@ -160,29 +183,29 @@ class List {
}
}
- moveIssue (issue, oldIndex, newIndex, moveBeforeId, moveAfterId) {
+ moveIssue(issue, oldIndex, newIndex, moveBeforeId, moveAfterId) {
this.issues.splice(oldIndex, 1);
this.issues.splice(newIndex, 0, issue);
- gl.boardService.moveIssue(issue.id, null, null, moveBeforeId, moveAfterId)
- .catch(() => {
- // TODO: handle request error
- });
+ gl.boardService.moveIssue(issue.id, null, null, moveBeforeId, moveAfterId).catch(() => {
+ // TODO: handle request error
+ });
}
updateIssueLabel(issue, listFrom, moveBeforeId, moveAfterId) {
- gl.boardService.moveIssue(issue.id, listFrom.id, this.id, moveBeforeId, moveAfterId)
+ gl.boardService
+ .moveIssue(issue.id, listFrom.id, this.id, moveBeforeId, moveAfterId)
.catch(() => {
// TODO: handle request error
});
}
- findIssue (id) {
+ findIssue(id) {
return this.issues.find(issue => issue.id === id);
}
- removeIssue (removeIssue) {
- this.issues = this.issues.filter((issue) => {
+ removeIssue(removeIssue) {
+ this.issues = this.issues.filter(issue => {
const matchesRemove = removeIssue.id === issue.id;
if (matchesRemove) {
diff --git a/app/assets/javascripts/boards/services/board_service.js b/app/assets/javascripts/boards/services/board_service.js
index 7c90597f77c..029b0971f2c 100644
--- a/app/assets/javascripts/boards/services/board_service.js
+++ b/app/assets/javascripts/boards/services/board_service.js
@@ -30,11 +30,13 @@ export default class BoardService {
return axios.post(this.listsEndpointGenerate, {});
}
- createList(labelId) {
+ createList(entityId, entityType) {
+ const list = {
+ [entityType]: entityId,
+ };
+
return axios.post(this.listsEndpoint, {
- list: {
- label_id: labelId,
- },
+ list,
});
}
diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js
index 20e78edf2a2..7dc83843e9b 100644
--- a/app/assets/javascripts/boards/stores/boards_store.js
+++ b/app/assets/javascripts/boards/stores/boards_store.js
@@ -103,8 +103,15 @@ gl.issueBoards.BoardsStore = {
const listLabels = issueLists.map(listIssue => listIssue.label);
if (!issueTo) {
- // Add to new lists issues if it doesn't already exist
- listTo.addIssue(issue, listFrom, newIndex);
+ // Check if target list assignee is already present in this issue
+ if ((listTo.type === 'assignee' && listFrom.type === 'assignee') &&
+ issue.findAssignee(listTo.assignee)) {
+ const targetIssue = listTo.findIssue(issue.id);
+ targetIssue.removeAssignee(listFrom.assignee);
+ } else {
+ // Add to new lists issues if it doesn't already exist
+ listTo.addIssue(issue, listFrom, newIndex);
+ }
} else {
listTo.updateIssueLabel(issue, listFrom);
issueTo.removeLabel(listFrom.label);
@@ -115,7 +122,11 @@ gl.issueBoards.BoardsStore = {
list.removeIssue(issue);
});
issue.removeLabels(listLabels);
- } else {
+ } else if (listTo.type === 'backlog' && listFrom.type === 'assignee') {
+ issue.removeAssignee(listFrom.assignee);
+ listFrom.removeIssue(issue);
+ } else if ((listTo.type !== 'label' && listFrom.type === 'assignee') ||
+ (listTo.type !== 'assignee' && listFrom.type === 'label')) {
listFrom.removeIssue(issue);
}
},
@@ -126,11 +137,12 @@ gl.issueBoards.BoardsStore = {
list.moveIssue(issue, oldIndex, newIndex, beforeId, afterId);
},
findList (key, val, type = 'label') {
- return this.state.lists.filter((list) => {
- const byType = type ? list['type'] === type : true;
+ const filteredList = this.state.lists.filter((list) => {
+ const byType = type ? (list.type === type) || (list.type === 'assignee') : true;
return list[key] === val && byType;
- })[0];
+ });
+ return filteredList[0];
},
updateFiltersUrl () {
history.pushState(null, null, `?${this.filter.path}`);
diff --git a/app/assets/javascripts/clusters/stores/clusters_store.js b/app/assets/javascripts/clusters/stores/clusters_store.js
index 3a4ac09f67c..d90db7b103c 100644
--- a/app/assets/javascripts/clusters/stores/clusters_store.js
+++ b/app/assets/javascripts/clusters/stores/clusters_store.js
@@ -95,7 +95,7 @@ export default class ClusterStore {
this.state.applications.jupyter.hostname =
serverAppEntry.hostname ||
(this.state.applications.ingress.externalIp
- ? `jupyter.${this.state.applications.ingress.externalIp}.xip.io`
+ ? `jupyter.${this.state.applications.ingress.externalIp}.nip.io`
: '');
}
});
diff --git a/app/assets/javascripts/diff_notes/diff_notes_bundle.js b/app/assets/javascripts/diff_notes/diff_notes_bundle.js
index e17daec6a92..d5161ab7df9 100644
--- a/app/assets/javascripts/diff_notes/diff_notes_bundle.js
+++ b/app/assets/javascripts/diff_notes/diff_notes_bundle.js
@@ -69,9 +69,10 @@ export default () => {
gl.diffNotesCompileComponents();
- if (!hasVueMRDiscussionsCookie()) {
+ const resolveCountAppEl = document.querySelector('#resolve-count-app');
+ if (!hasVueMRDiscussionsCookie() && resolveCountAppEl) {
new Vue({
- el: '#resolve-count-app',
+ el: resolveCountAppEl,
components: {
'resolve-count': ResolveCount
},
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index 746a06b7c4f..7fbba7e27cb 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -602,7 +602,11 @@ GitLabDropdown = (function() {
var selector;
selector = '.dropdown-content';
if (this.dropdown.find(".dropdown-toggle-page").length) {
- selector = ".dropdown-page-one .dropdown-content";
+ if (this.options.containerSelector) {
+ selector = this.options.containerSelector;
+ } else {
+ selector = '.dropdown-page-one .dropdown-content';
+ }
}
return $(selector, this.dropdown).empty();
diff --git a/app/assets/javascripts/group_label_subscription.js b/app/assets/javascripts/group_label_subscription.js
index 5648cb9a888..d33e3a37580 100644
--- a/app/assets/javascripts/group_label_subscription.js
+++ b/app/assets/javascripts/group_label_subscription.js
@@ -1,7 +1,12 @@
import $ from 'jquery';
+import { __ } from '~/locale';
import axios from './lib/utils/axios_utils';
import flash from './flash';
-import { __ } from './locale';
+
+const tooltipTitles = {
+ group: __('Unsubscribe at group level'),
+ project: __('Unsubscribe at project level'),
+};
export default class GroupLabelSubscription {
constructor(container) {
@@ -35,6 +40,7 @@ export default class GroupLabelSubscription {
this.$unsubscribeButtons.attr('data-url', url);
axios.post(url)
+ .then(() => GroupLabelSubscription.setNewTooltip($btn))
.then(() => this.toggleSubscriptionButtons())
.catch(() => flash(__('There was an error when subscribing to this label.')));
}
@@ -44,4 +50,14 @@ export default class GroupLabelSubscription {
this.$subscribeButtons.toggleClass('hidden');
this.$unsubscribeButtons.toggleClass('hidden');
}
+
+ static setNewTooltip($button) {
+ if (!$button.hasClass('js-subscribe-button')) return;
+
+ const type = $button.hasClass('js-group-level') ? 'group' : 'project';
+ const newTitle = tooltipTitles[type];
+
+ $('.js-unsubscribe-button', $button.closest('.label-actions-list'))
+ .tooltip('hide').attr('title', newTitle).tooltip('_fixTitle');
+ }
}
diff --git a/app/assets/javascripts/ide/components/panes/right.vue b/app/assets/javascripts/ide/components/panes/right.vue
index aafd6a15a78..dd7fc8f1e01 100644
--- a/app/assets/javascripts/ide/components/panes/right.vue
+++ b/app/assets/javascripts/ide/components/panes/right.vue
@@ -5,6 +5,7 @@ import Icon from '../../../vue_shared/components/icon.vue';
import { rightSidebarViews } from '../../constants';
import PipelinesList from '../pipelines/list.vue';
import JobsDetail from '../jobs/detail.vue';
+import ResizablePanel from '../resizable_panel.vue';
export default {
directives: {
@@ -14,6 +15,7 @@ export default {
Icon,
PipelinesList,
JobsDetail,
+ ResizablePanel,
},
computed: {
...mapState(['rightPane']),
@@ -40,12 +42,16 @@ export default {
<div
class="multi-file-commit-panel ide-right-sidebar"
>
- <div
- class="multi-file-commit-panel-inner"
+ <resizable-panel
v-if="rightPane"
+ class="multi-file-commit-panel-inner"
+ :collapsible="false"
+ :initial-width="350"
+ :min-size="350"
+ side="right"
>
<component :is="rightPane" />
- </div>
+ </resizable-panel>
<nav class="ide-activity-bar">
<ul class="list-unstyled">
<li>
diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue
index 93453989c08..d365745d78b 100644
--- a/app/assets/javascripts/ide/components/repo_editor.vue
+++ b/app/assets/javascripts/ide/components/repo_editor.vue
@@ -1,10 +1,8 @@
<script>
-/* global monaco */
import { mapState, mapGetters, mapActions } from 'vuex';
import flash from '~/flash';
import ContentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue';
import { activityBarViews, viewerTypes } from '../constants';
-import monacoLoader from '../monaco_loader';
import Editor from '../lib/editor';
import ExternalLink from './external_link.vue';
@@ -50,7 +48,7 @@ export default {
// Compare key to allow for files opened in review mode to be cached differently
if (oldVal.key !== this.file.key) {
- this.initMonaco();
+ this.initEditor();
if (this.currentActivityView !== activityBarViews.edit) {
this.setFileViewMode({
@@ -84,15 +82,10 @@ export default {
this.editor.dispose();
},
mounted() {
- if (this.editor && monaco) {
- this.initMonaco();
- } else {
- monacoLoader(['vs/editor/editor.main'], () => {
- this.editor = Editor.create(monaco);
-
- this.initMonaco();
- });
+ if (!this.editor) {
+ this.editor = Editor.create();
}
+ this.initEditor();
},
methods: {
...mapActions([
@@ -105,7 +98,7 @@ export default {
'updateViewer',
'removePendingTab',
]),
- initMonaco() {
+ initEditor() {
if (this.shouldHideEditor) return;
this.editor.clearEditor();
@@ -118,7 +111,7 @@ export default {
this.createEditorInstance();
})
.catch(err => {
- flash('Error setting up monaco. Please try again.', 'alert', document, null, false, true);
+ flash('Error setting up editor. Please try again.', 'alert', document, null, false, true);
throw err;
});
},
diff --git a/app/assets/javascripts/ide/lib/common/model.js b/app/assets/javascripts/ide/lib/common/model.js
index e5149b1f3ad..78e6f632728 100644
--- a/app/assets/javascripts/ide/lib/common/model.js
+++ b/app/assets/javascripts/ide/lib/common/model.js
@@ -1,32 +1,32 @@
+import { editor as monacoEditor, Uri } from 'monaco-editor';
import Disposable from './disposable';
import eventHub from '../../eventhub';
export default class Model {
- constructor(monaco, file, head = null) {
- this.monaco = monaco;
+ constructor(file, head = null) {
this.disposable = new Disposable();
this.file = file;
this.head = head;
this.content = file.content !== '' ? file.content : file.raw;
this.disposable.add(
- (this.originalModel = this.monaco.editor.createModel(
+ (this.originalModel = monacoEditor.createModel(
head ? head.content : this.file.raw,
undefined,
- new this.monaco.Uri(null, null, `original/${this.path}`),
+ new Uri(false, false, `original/${this.path}`),
)),
- (this.model = this.monaco.editor.createModel(
+ (this.model = monacoEditor.createModel(
this.content,
undefined,
- new this.monaco.Uri(null, null, this.path),
+ new Uri(false, false, this.path),
)),
);
if (this.file.mrChange) {
this.disposable.add(
- (this.baseModel = this.monaco.editor.createModel(
+ (this.baseModel = monacoEditor.createModel(
this.file.baseRaw,
undefined,
- new this.monaco.Uri(null, null, `target/${this.path}`),
+ new Uri(false, false, `target/${this.path}`),
)),
);
}
diff --git a/app/assets/javascripts/ide/lib/common/model_manager.js b/app/assets/javascripts/ide/lib/common/model_manager.js
index 7f643969480..bd9b8fc3fcc 100644
--- a/app/assets/javascripts/ide/lib/common/model_manager.js
+++ b/app/assets/javascripts/ide/lib/common/model_manager.js
@@ -3,8 +3,7 @@ import Disposable from './disposable';
import Model from './model';
export default class ModelManager {
- constructor(monaco) {
- this.monaco = monaco;
+ constructor() {
this.disposable = new Disposable();
this.models = new Map();
}
@@ -22,7 +21,7 @@ export default class ModelManager {
return this.getModel(file.key);
}
- const model = new Model(this.monaco, file, head);
+ const model = new Model(file, head);
this.models.set(model.path, model);
this.disposable.add(model);
diff --git a/app/assets/javascripts/ide/lib/diff/controller.js b/app/assets/javascripts/ide/lib/diff/controller.js
index f579424cf33..046e562ba2b 100644
--- a/app/assets/javascripts/ide/lib/diff/controller.js
+++ b/app/assets/javascripts/ide/lib/diff/controller.js
@@ -1,4 +1,4 @@
-/* global monaco */
+import { Range } from 'monaco-editor';
import { throttle } from 'underscore';
import DirtyDiffWorker from './diff_worker';
import Disposable from '../common/disposable';
@@ -16,7 +16,7 @@ export const getDiffChangeType = change => {
};
export const getDecorator = change => ({
- range: new monaco.Range(change.lineNumber, 1, change.endLineNumber, 1),
+ range: new Range(change.lineNumber, 1, change.endLineNumber, 1),
options: {
isWholeLine: true,
linesDecorationsClassName: `dirty-diff dirty-diff-${getDiffChangeType(change)}`,
diff --git a/app/assets/javascripts/ide/lib/editor.js b/app/assets/javascripts/ide/lib/editor.js
index 9c3bb9cc17d..02038fcb534 100644
--- a/app/assets/javascripts/ide/lib/editor.js
+++ b/app/assets/javascripts/ide/lib/editor.js
@@ -1,4 +1,5 @@
import _ from 'underscore';
+import { editor as monacoEditor, KeyCode, KeyMod } from 'monaco-editor';
import store from '../stores';
import DecorationsController from './decorations/controller';
import DirtyDiffController from './diff/controller';
@@ -8,6 +9,11 @@ import editorOptions, { defaultEditorOptions } from './editor_options';
import gitlabTheme from './themes/gl_theme';
import keymap from './keymap.json';
+function setupMonacoTheme() {
+ monacoEditor.defineTheme(gitlabTheme.themeName, gitlabTheme.monacoTheme);
+ monacoEditor.setTheme('gitlab');
+}
+
export const clearDomElement = el => {
if (!el || !el.firstChild) return;
@@ -17,24 +23,22 @@ export const clearDomElement = el => {
};
export default class Editor {
- static create(monaco) {
- if (this.editorInstance) return this.editorInstance;
-
- this.editorInstance = new Editor(monaco);
-
+ static create() {
+ if (!this.editorInstance) {
+ this.editorInstance = new Editor();
+ }
return this.editorInstance;
}
- constructor(monaco) {
- this.monaco = monaco;
+ constructor() {
this.currentModel = null;
this.instance = null;
this.dirtyDiffController = null;
this.disposable = new Disposable();
- this.modelManager = new ModelManager(this.monaco);
+ this.modelManager = new ModelManager();
this.decorationsController = new DecorationsController(this);
- this.setupMonacoTheme();
+ setupMonacoTheme();
this.debouncedUpdate = _.debounce(() => {
this.updateDimensions();
@@ -46,7 +50,7 @@ export default class Editor {
clearDomElement(domElement);
this.disposable.add(
- (this.instance = this.monaco.editor.create(domElement, {
+ (this.instance = monacoEditor.create(domElement, {
...defaultEditorOptions,
})),
(this.dirtyDiffController = new DirtyDiffController(
@@ -66,7 +70,7 @@ export default class Editor {
clearDomElement(domElement);
this.disposable.add(
- (this.instance = this.monaco.editor.createDiffEditor(domElement, {
+ (this.instance = monacoEditor.createDiffEditor(domElement, {
...defaultEditorOptions,
quickSuggestions: false,
occurrencesHighlight: false,
@@ -122,17 +126,11 @@ export default class Editor {
modified: model.getModel(),
});
- this.monaco.editor.createDiffNavigator(this.instance, {
+ monacoEditor.createDiffNavigator(this.instance, {
alwaysRevealFirst: true,
});
}
- setupMonacoTheme() {
- this.monaco.editor.defineTheme(gitlabTheme.themeName, gitlabTheme.monacoTheme);
-
- this.monaco.editor.setTheme('gitlab');
- }
-
clearEditor() {
if (this.instance) {
this.instance.setModel(null);
@@ -200,7 +198,7 @@ export default class Editor {
const getKeyCode = key => {
const monacoKeyMod = key.indexOf('KEY_') === 0;
- return monacoKeyMod ? this.monaco.KeyCode[key] : this.monaco.KeyMod[key];
+ return monacoKeyMod ? KeyCode[key] : KeyMod[key];
};
keymap.forEach(command => {
diff --git a/app/assets/javascripts/ide/monaco_loader.js b/app/assets/javascripts/ide/monaco_loader.js
deleted file mode 100644
index 142a220097b..00000000000
--- a/app/assets/javascripts/ide/monaco_loader.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import monacoContext from 'monaco-editor/dev/vs/loader';
-
-monacoContext.require.config({
- paths: {
- vs: `${__webpack_public_path__}monaco-editor/vs`, // eslint-disable-line camelcase
- },
-});
-
-// ignore CDN config and use local assets path for service worker which cannot be cross-domain
-const relativeRootPath = (gon && gon.relative_url_root) || '';
-const monacoPath = `${relativeRootPath}/assets/webpack/monaco-editor/vs`;
-window.MonacoEnvironment = { getWorkerUrl: () => `${monacoPath}/base/worker/workerMain.js` };
-
-// eslint-disable-next-line no-underscore-dangle
-window.__monaco_context__ = monacoContext;
-export default monacoContext.require;
diff --git a/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js b/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js
index 5beb8fac71f..4e1df80b3a2 100644
--- a/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js
@@ -31,10 +31,15 @@ export const openMergeRequest = ({ commit, dispatch }, { projectPath, id }) => {
commit(rootTypes.CLEAR_PROJECTS, null, { root: true });
commit(rootTypes.SET_CURRENT_MERGE_REQUEST, `${id}`, { root: true });
commit(rootTypes.RESET_OPEN_FILES, null, { root: true });
- dispatch('pipelines/stopPipelinePolling', null, { root: true });
- dispatch('pipelines/clearEtagPoll', null, { root: true });
dispatch('pipelines/resetLatestPipeline', null, { root: true });
dispatch('setCurrentBranchId', '', { root: true });
+ dispatch('pipelines/stopPipelinePolling', null, { root: true })
+ .then(() => {
+ dispatch('pipelines/clearEtagPoll', null, { root: true });
+ })
+ .catch(e => {
+ throw e;
+ });
router.push(`/project/${projectPath}/merge_requests/${id}`);
};
diff --git a/app/assets/javascripts/ide/stores/modules/pipelines/actions.js b/app/assets/javascripts/ide/stores/modules/pipelines/actions.js
index 0a4ea80c4c1..6718f7eae4e 100644
--- a/app/assets/javascripts/ide/stores/modules/pipelines/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/pipelines/actions.js
@@ -12,8 +12,12 @@ let eTagPoll;
export const clearEtagPoll = () => {
eTagPoll = null;
};
-export const stopPipelinePolling = () => eTagPoll && eTagPoll.stop();
-export const restartPipelinePolling = () => eTagPoll && eTagPoll.restart();
+export const stopPipelinePolling = () => {
+ if (eTagPoll) eTagPoll.stop();
+};
+export const restartPipelinePolling = () => {
+ if (eTagPoll) eTagPoll.restart();
+};
export const requestLatestPipeline = ({ commit }) => commit(types.REQUEST_LATEST_PIPELINE);
export const receiveLatestPipelineError = ({ commit, dispatch }) => {
@@ -51,9 +55,9 @@ export const fetchLatestPipeline = ({ dispatch, rootGetters }) => {
Visibility.change(() => {
if (!Visibility.hidden()) {
- eTagPoll.restart();
+ dispatch('restartPipelinePolling');
} else {
- eTagPoll.stop();
+ dispatch('stopPipelinePolling');
}
});
};
diff --git a/app/assets/javascripts/init_changes_dropdown.js b/app/assets/javascripts/init_changes_dropdown.js
index 09cca1dc7d9..5c5a6e01848 100644
--- a/app/assets/javascripts/init_changes_dropdown.js
+++ b/app/assets/javascripts/init_changes_dropdown.js
@@ -1,5 +1,5 @@
import $ from 'jquery';
-import stickyMonitor from './lib/utils/sticky';
+import { stickyMonitor } from './lib/utils/sticky';
export default (stickyTop) => {
stickyMonitor(document.querySelector('.js-diff-files-changed'), stickyTop);
diff --git a/app/assets/javascripts/job.js b/app/assets/javascripts/job.js
index e76bae08699..fc13f467675 100644
--- a/app/assets/javascripts/job.js
+++ b/app/assets/javascripts/job.js
@@ -1,6 +1,6 @@
import $ from 'jquery';
import _ from 'underscore';
-import StickyFill from 'stickyfilljs';
+import { polyfillSticky } from './lib/utils/sticky';
import axios from './lib/utils/axios_utils';
import { visitUrl } from './lib/utils/url_utility';
import bp from './breakpoints';
@@ -70,14 +70,7 @@ export default class Job extends LogOutputBehaviours {
}
initAffixTopArea() {
- /**
- If the browser does not support position sticky, it returns the position as static.
- If the browser does support sticky, then we allow the browser to handle it, if not
- then we use a polyfill
- */
- if (this.$topBar.css('position') !== 'static') return;
-
- StickyFill.add(this.$topBar);
+ polyfillSticky(this.$topBar);
}
scrollToBottom() {
diff --git a/app/assets/javascripts/label_manager.js b/app/assets/javascripts/label_manager.js
index 8c3de6e4045..8b01024b7d4 100644
--- a/app/assets/javascripts/label_manager.js
+++ b/app/assets/javascripts/label_manager.js
@@ -13,6 +13,7 @@ export default class LabelManager {
this.otherLabels = otherLabels || $('.js-other-labels');
this.errorMessage = 'Unable to update label prioritization at this time';
this.emptyState = document.querySelector('#js-priority-labels-empty-state');
+ this.$badgeItemTemplate = $('#js-badge-item-template');
this.sortable = Sortable.create(this.prioritizedLabels.get(0), {
filter: '.empty-message',
forceFallback: true,
@@ -63,7 +64,11 @@ export default class LabelManager {
$target = this.otherLabels;
$from = this.prioritizedLabels;
}
- $label.detach().appendTo($target);
+
+ const $detachedLabel = $label.detach();
+ this.toggleLabelPriorityBadge($detachedLabel, action);
+ $detachedLabel.appendTo($target);
+
if ($from.find('li').length) {
$from.find('.empty-message').removeClass('hidden');
}
@@ -88,6 +93,14 @@ export default class LabelManager {
}
}
+ toggleLabelPriorityBadge($label, action) {
+ if (action === 'remove') {
+ $('.js-priority-badge', $label).remove();
+ } else {
+ $('.label-links', $label).append(this.$badgeItemTemplate.clone().html());
+ }
+ }
+
onPrioritySortUpdate() {
this.savePrioritySort()
.catch(() => flash(this.errorMessage));
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index 8b5445d012b..d55d0585031 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -384,6 +384,49 @@ export const backOff = (fn, timeout = 60000) => {
});
};
+export const createOverlayIcon = (iconPath, overlayPath) => {
+ const faviconImage = document.createElement('img');
+
+ return new Promise((resolve) => {
+ faviconImage.onload = () => {
+ const size = 32;
+
+ const canvas = document.createElement('canvas');
+ canvas.width = size;
+ canvas.height = size;
+
+ const context = canvas.getContext('2d');
+ context.clearRect(0, 0, size, size);
+ context.drawImage(
+ faviconImage, 0, 0, faviconImage.width, faviconImage.height, 0, 0, size, size,
+ );
+
+ const overlayImage = document.createElement('img');
+ overlayImage.onload = () => {
+ context.drawImage(
+ overlayImage, 0, 0, overlayImage.width, overlayImage.height, 0, 0, size, size,
+ );
+
+ const faviconWithOverlayUrl = canvas.toDataURL();
+
+ resolve(faviconWithOverlayUrl);
+ };
+ overlayImage.src = overlayPath;
+ };
+ faviconImage.src = iconPath;
+ });
+};
+
+export const setFaviconOverlay = (overlayPath) => {
+ const faviconEl = document.getElementById('favicon');
+
+ if (!faviconEl) { return null; }
+
+ const iconPath = faviconEl.getAttribute('data-original-href');
+
+ return createOverlayIcon(iconPath, overlayPath).then(faviconWithOverlayUrl => faviconEl.setAttribute('href', faviconWithOverlayUrl));
+};
+
export const setFavicon = (faviconPath) => {
const faviconEl = document.getElementById('favicon');
if (faviconEl && faviconPath) {
@@ -393,8 +436,9 @@ export const setFavicon = (faviconPath) => {
export const resetFavicon = () => {
const faviconEl = document.getElementById('favicon');
- const originalFavicon = faviconEl ? faviconEl.getAttribute('href') : null;
+
if (faviconEl) {
+ const originalFavicon = faviconEl.getAttribute('data-original-href');
faviconEl.setAttribute('href', originalFavicon);
}
};
@@ -403,10 +447,9 @@ export const setCiStatusFavicon = pageUrl =>
axios.get(pageUrl)
.then(({ data }) => {
if (data && data.favicon) {
- setFavicon(data.favicon);
- } else {
- resetFavicon();
+ return setFaviconOverlay(data.favicon);
}
+ return resetFavicon();
})
.catch(resetFavicon);
diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js
index 0ff23bbb061..7cca32dc6fa 100644
--- a/app/assets/javascripts/lib/utils/datetime_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime_utility.js
@@ -79,37 +79,37 @@ export function getTimeago() {
if (!timeagoInstance) {
const localeRemaining = function getLocaleRemaining(number, index) {
return [
- [s__('Timeago|less than a minute ago'), s__('Timeago|right now')],
- [s__('Timeago|less than a minute ago'), s__('Timeago|%s seconds remaining')],
- [s__('Timeago|about a minute ago'), s__('Timeago|1 minute remaining')],
+ [s__('Timeago|just now'), s__('Timeago|right now')],
+ [s__('Timeago|%s seconds ago'), s__('Timeago|%s seconds remaining')],
+ [s__('Timeago|1 minute ago'), s__('Timeago|1 minute remaining')],
[s__('Timeago|%s minutes ago'), s__('Timeago|%s minutes remaining')],
- [s__('Timeago|about an hour ago'), s__('Timeago|1 hour remaining')],
- [s__('Timeago|about %s hours ago'), s__('Timeago|%s hours remaining')],
- [s__('Timeago|a day ago'), s__('Timeago|1 day remaining')],
+ [s__('Timeago|1 hour ago'), s__('Timeago|1 hour remaining')],
+ [s__('Timeago|%s hours ago'), s__('Timeago|%s hours remaining')],
+ [s__('Timeago|1 day ago'), s__('Timeago|1 day remaining')],
[s__('Timeago|%s days ago'), s__('Timeago|%s days remaining')],
- [s__('Timeago|a week ago'), s__('Timeago|1 week remaining')],
+ [s__('Timeago|1 week ago'), s__('Timeago|1 week remaining')],
[s__('Timeago|%s weeks ago'), s__('Timeago|%s weeks remaining')],
- [s__('Timeago|a month ago'), s__('Timeago|1 month remaining')],
+ [s__('Timeago|1 month ago'), s__('Timeago|1 month remaining')],
[s__('Timeago|%s months ago'), s__('Timeago|%s months remaining')],
- [s__('Timeago|a year ago'), s__('Timeago|1 year remaining')],
+ [s__('Timeago|1 year ago'), s__('Timeago|1 year remaining')],
[s__('Timeago|%s years ago'), s__('Timeago|%s years remaining')],
][index];
};
const locale = function getLocale(number, index) {
return [
- [s__('Timeago|less than a minute ago'), s__('Timeago|right now')],
- [s__('Timeago|less than a minute ago'), s__('Timeago|in %s seconds')],
- [s__('Timeago|about a minute ago'), s__('Timeago|in 1 minute')],
+ [s__('Timeago|just now'), s__('Timeago|right now')],
+ [s__('Timeago|%s seconds ago'), s__('Timeago|in %s seconds')],
+ [s__('Timeago|1 minute ago'), s__('Timeago|in 1 minute')],
[s__('Timeago|%s minutes ago'), s__('Timeago|in %s minutes')],
- [s__('Timeago|about an hour ago'), s__('Timeago|in 1 hour')],
- [s__('Timeago|about %s hours ago'), s__('Timeago|in %s hours')],
- [s__('Timeago|a day ago'), s__('Timeago|in 1 day')],
+ [s__('Timeago|1 hour ago'), s__('Timeago|in 1 hour')],
+ [s__('Timeago|%s hours ago'), s__('Timeago|in %s hours')],
+ [s__('Timeago|1 day ago'), s__('Timeago|in 1 day')],
[s__('Timeago|%s days ago'), s__('Timeago|in %s days')],
- [s__('Timeago|a week ago'), s__('Timeago|in 1 week')],
+ [s__('Timeago|1 week ago'), s__('Timeago|in 1 week')],
[s__('Timeago|%s weeks ago'), s__('Timeago|in %s weeks')],
- [s__('Timeago|a month ago'), s__('Timeago|in 1 month')],
+ [s__('Timeago|1 month ago'), s__('Timeago|in 1 month')],
[s__('Timeago|%s months ago'), s__('Timeago|in %s months')],
- [s__('Timeago|a year ago'), s__('Timeago|in 1 year')],
+ [s__('Timeago|1 year ago'), s__('Timeago|in 1 year')],
[s__('Timeago|%s years ago'), s__('Timeago|in %s years')],
][index];
};
@@ -270,6 +270,17 @@ export const totalDaysInMonth = date => {
};
/**
+ * Returns number of days in a quarter from provided
+ * months array.
+ *
+ * @param {Array} quarter
+ */
+export const totalDaysInQuarter = quarter => quarter.reduce(
+ (acc, month) => acc + totalDaysInMonth(month),
+ 0,
+);
+
+/**
* Returns list of Dates referring to Sundays of the month
* based on provided date
*
@@ -309,42 +320,27 @@ export const getSundays = date => {
};
/**
- * Returns list of Dates representing a timeframe of Months from month of provided date (inclusive)
- * up to provided length
- *
- * For eg;
- * If current month is January 2018 and `length` provided is `6`
- * Then this method will return list of Date objects as follows;
- *
- * [ October 2017, November 2017, December 2017, January 2018, February 2018, March 2018 ]
- *
- * If current month is March 2018 and `length` provided is `3`
- * Then this method will return list of Date objects as follows;
- *
- * [ February 2018, March 2018, April 2018 ]
+ * Returns list of Dates representing a timeframe of months from startDate and length
*
+ * @param {Date} startDate
* @param {Number} length
- * @param {Date} date
*/
-export const getTimeframeWindow = (length, date) => {
- if (!length) {
+export const getTimeframeWindowFrom = (startDate, length) => {
+ if (!(startDate instanceof Date) || !length) {
return [];
}
- const currentDate = date instanceof Date ? date : new Date();
- const currentMonthIndex = Math.floor(length / 2);
- const timeframe = [];
-
- // Move date object backward to the first month of timeframe
- currentDate.setDate(1);
- currentDate.setMonth(currentDate.getMonth() - currentMonthIndex);
-
- // Iterate and update date for the size of length
+ // Iterate and set date for the size of length
// and push date reference to timeframe list
- for (let i = 0; i < length; i += 1) {
- timeframe.push(new Date(currentDate.getTime()));
- currentDate.setMonth(currentDate.getMonth() + 1);
- }
+ const timeframe = new Array(length)
+ .fill()
+ .map(
+ (val, i) => new Date(
+ startDate.getFullYear(),
+ startDate.getMonth() + i,
+ 1,
+ ),
+ );
// Change date of last timeframe item to last date of the month
timeframe[length - 1].setDate(totalDaysInMonth(timeframe[length - 1]));
@@ -352,6 +348,29 @@ export const getTimeframeWindow = (length, date) => {
return timeframe;
};
+/**
+ * Returns count of day within current quarter from provided date
+ * and array of months for the quarter
+ *
+ * Eg;
+ * If date is 15 Feb 2018
+ * and quarter is [Jan, Feb, Mar]
+ *
+ * Then 15th Feb is 46th day of the quarter
+ * Where 31 (days in Jan) + 15 (date of Feb).
+ *
+ * @param {Date} date
+ * @param {Array} quarter
+ */
+export const dayInQuarter = (date, quarter) => quarter.reduce((acc, month) => {
+ if (date.getMonth() > month.getMonth()) {
+ return acc + totalDaysInMonth(month);
+ } else if (date.getMonth() === month.getMonth()) {
+ return acc + date.getDate();
+ }
+ return acc + 0;
+}, 0);
+
window.gl = window.gl || {};
window.gl.utils = {
...(window.gl.utils || {}),
diff --git a/app/assets/javascripts/lib/utils/http_status.js b/app/assets/javascripts/lib/utils/http_status.js
index bb151929431..229d53b18b0 100644
--- a/app/assets/javascripts/lib/utils/http_status.js
+++ b/app/assets/javascripts/lib/utils/http_status.js
@@ -8,4 +8,5 @@ export default {
OK: 200,
MULTIPLE_CHOICES: 300,
BAD_REQUEST: 400,
+ NOT_FOUND: 404,
};
diff --git a/app/assets/javascripts/lib/utils/sticky.js b/app/assets/javascripts/lib/utils/sticky.js
index 098afcfa1b4..15a4dd62012 100644
--- a/app/assets/javascripts/lib/utils/sticky.js
+++ b/app/assets/javascripts/lib/utils/sticky.js
@@ -1,3 +1,5 @@
+import StickyFill from 'stickyfilljs';
+
export const createPlaceholder = () => {
const placeholder = document.createElement('div');
placeholder.classList.add('sticky-placeholder');
@@ -28,7 +30,16 @@ export const isSticky = (el, scrollY, stickyTop, insertPlaceholder) => {
}
};
-export default (el, stickyTop, insertPlaceholder = true) => {
+/**
+ * Create a listener that will toggle a 'is-stuck' class, based on the current scroll position.
+ *
+ * - If the current environment does not support `position: sticky`, do nothing.
+ *
+ * @param {HTMLElement} el The `position: sticky` element.
+ * @param {Number} stickyTop Used to determine when an element is stuck.
+ * @param {Boolean} insertPlaceholder Should a placeholder element be created when element is stuck?
+ */
+export const stickyMonitor = (el, stickyTop, insertPlaceholder = true) => {
if (!el) return;
if (typeof CSS === 'undefined' || !(CSS.supports('(position: -webkit-sticky) or (position: sticky)'))) return;
@@ -37,3 +48,13 @@ export default (el, stickyTop, insertPlaceholder = true) => {
passive: true,
});
};
+
+/**
+ * Polyfill the `position: sticky` behavior.
+ *
+ * - If the current environment supports `position: sticky`, do nothing.
+ * - Can receive an iterable element list (NodeList, jQuery collection, etc.) or single HTMLElement.
+ */
+export const polyfillSticky = (el) => {
+ StickyFill.add(el);
+};
diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js
index dd17544b656..72b72f4247d 100644
--- a/app/assets/javascripts/lib/utils/url_utility.js
+++ b/app/assets/javascripts/lib/utils/url_utility.js
@@ -85,9 +85,9 @@ export function redirectTo(url) {
}
export function webIDEUrl(route = undefined) {
- let returnUrl = `${gon.relative_url_root}/-/ide/`;
+ let returnUrl = `${gon.relative_url_root || ''}/-/ide/`;
if (route) {
- returnUrl += `project${route}`;
+ returnUrl += `project${route.replace(new RegExp(`^${gon.relative_url_root || ''}`), '')}`;
}
return returnUrl;
}
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index 9803bebfd10..c9ce838cd48 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -144,6 +144,7 @@ document.addEventListener('DOMContentLoaded', () => {
$body.tooltip({
selector: '.has-tooltip, [data-toggle="tooltip"]',
trigger: 'hover',
+ boundary: 'viewport',
placement(tip, el) {
return $(el).data('placement') || 'bottom';
},
diff --git a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js
index 6c2a785c0af..37ef77c8e43 100644
--- a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js
+++ b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js
@@ -22,4 +22,18 @@ document.addEventListener('DOMContentLoaded', () => {
errorBox: variableListEl.querySelector('.js-ci-variable-error-box'),
saveEndpoint: variableListEl.dataset.saveEndpoint,
});
+
+ // hide extra auto devops settings based on data-attributes
+ const autoDevOpsSettings = document.querySelector('.js-auto-devops-settings');
+ const autoDevOpsExtraSettings = document.querySelector('.js-extra-settings');
+
+ autoDevOpsSettings.addEventListener('click', event => {
+ const target = event.target;
+ if (target.classList.contains('js-toggle-extra-settings')) {
+ autoDevOpsExtraSettings.classList.toggle(
+ 'hidden',
+ !!(target.dataset && target.dataset.hideExtraSettings),
+ );
+ }
+ });
});
diff --git a/app/assets/javascripts/project_label_subscription.js b/app/assets/javascripts/project_label_subscription.js
index 6f06944ebb6..9049f87e037 100644
--- a/app/assets/javascripts/project_label_subscription.js
+++ b/app/assets/javascripts/project_label_subscription.js
@@ -3,6 +3,17 @@ import { __ } from './locale';
import axios from './lib/utils/axios_utils';
import flash from './flash';
+const tooltipTitles = {
+ group: {
+ subscribed: __('Unsubscribe at group level'),
+ unsubscribed: __('Subscribe at group level'),
+ },
+ project: {
+ subscribed: __('Unsubscribe at project level'),
+ unsubscribed: __('Subscribe at project level'),
+ },
+};
+
export default class ProjectLabelSubscription {
constructor(container) {
this.$container = $(container);
@@ -15,12 +26,10 @@ export default class ProjectLabelSubscription {
event.preventDefault();
const $btn = $(event.currentTarget);
- const $span = $btn.find('span');
const url = $btn.attr('data-url');
const oldStatus = $btn.attr('data-status');
$btn.addClass('disabled');
- $span.toggleClass('hidden');
axios.post(url).then(() => {
let newStatus;
@@ -32,21 +41,28 @@ export default class ProjectLabelSubscription {
[newStatus, newAction] = ['unsubscribed', 'Subscribe'];
}
- $span.toggleClass('hidden');
$btn.removeClass('disabled');
this.$buttons.attr('data-status', newStatus);
this.$buttons.find('> span').text(newAction);
- this.$buttons.map((button) => {
+ this.$buttons.map((i, button) => {
const $button = $(button);
+ const originalTitle = $button.attr('data-original-title');
- if ($button.attr('data-original-title')) {
- $button.tooltip('hide').attr('data-original-title', newAction).tooltip('_fixTitle');
+ if (originalTitle) {
+ ProjectLabelSubscription.setNewTitle($button, originalTitle, newStatus, newAction);
}
return button;
});
}).catch(() => flash(__('There was an error subscribing to this label.')));
}
+
+ static setNewTitle($button, originalTitle, newStatus) {
+ const type = /group/.test(originalTitle) ? 'group' : 'project';
+ const newTitle = tooltipTitles[type][newStatus];
+
+ $button.attr('title', newTitle).tooltip('_fixTitle');
+ }
}
diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue
index ab7d2d41ece..6ed35c0a981 100644
--- a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue
+++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue
@@ -89,14 +89,13 @@ export default {
<div>
<div
class="js-gcp-machine-type-dropdown dropdown"
- :class="{ 'gl-show-field-errors': hasErrors }"
>
<dropdown-hidden-input
:name="fieldName"
:value="selectedMachineType"
/>
<dropdown-button
- :class="{ 'gl-field-error-outline': hasErrors }"
+ :class="{ 'border-danger': hasErrors }"
:is-disabled="isDisabled"
:is-loading="isLoading"
:toggle-text="toggleText"
@@ -132,8 +131,11 @@ export default {
</div>
</div>
<span
- class="form-text text-muted"
- :class="{ 'gl-field-error': hasErrors }"
+ class="form-text"
+ :class="{
+ 'text-danger': hasErrors,
+ 'text-muted': !hasErrors
+ }"
v-if="hasErrors"
>
{{ errorMessage }}
diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue
index 25350ef0fa9..542d4d21a22 100644
--- a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue
+++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue
@@ -147,7 +147,6 @@ export default {
<div>
<div
class="js-gcp-project-id-dropdown dropdown"
- :class="{ 'gl-show-field-errors': hasErrors }"
>
<dropdown-hidden-input
:name="fieldName"
@@ -155,7 +154,7 @@ export default {
/>
<dropdown-button
:class="{
- 'gl-field-error-outline': hasErrors,
+ 'border-danger': hasErrors,
'read-only': hasOneProject
}"
:is-disabled="isDisabled"
@@ -193,8 +192,11 @@ export default {
</div>
</div>
<span
- class="form-text text-muted"
- :class="{ 'gl-field-error': hasErrors }"
+ class="form-text"
+ :class="{
+ 'text-danger': hasErrors,
+ 'text-muted': !hasErrors
+ }"
v-html="helpText"
></span>
</div>
diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue
index 8ee4eefcd91..bc28f8b5df4 100644
--- a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue
+++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue
@@ -63,14 +63,13 @@ export default {
<div>
<div
class="js-gcp-zone-dropdown dropdown"
- :class="{ 'gl-show-field-errors': hasErrors }"
>
<dropdown-hidden-input
:name="fieldName"
:value="selectedZone"
/>
<dropdown-button
- :class="{ 'gl-field-error-outline': hasErrors }"
+ :class="{ 'border-danger': hasErrors }"
:is-disabled="isDisabled"
:is-loading="isLoading"
:toggle-text="toggleText"
@@ -106,8 +105,11 @@ export default {
</div>
</div>
<span
- class="form-text text-muted"
- :class="{ 'gl-field-error': hasErrors }"
+ class="form-text"
+ :class="{
+ 'text-danger': hasErrors,
+ 'text-muted': !hasErrors
+ }"
v-if="hasErrors"
>
{{ errorMessage }}
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue b/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue
index 6f79310b1cc..0e139cd7f5e 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue
@@ -1,8 +1,12 @@
<script>
import { parseSeconds, stringifyTime } from '../../../lib/utils/pretty_time';
+import tooltip from '../../../vue_shared/directives/tooltip';
export default {
name: 'TimeTrackingComparisonPane',
+ directives: {
+ tooltip,
+ },
props: {
timeSpent: {
type: Number,
@@ -51,17 +55,12 @@ export default {
<div class="time-tracking-comparison-pane">
<div
class="compare-meter"
- data-toggle="tooltip"
- data-placement="top"
- role="timeRemainingDisplay"
- :aria-valuenow="timeRemainingTooltip"
:title="timeRemainingTooltip"
- :data-original-title="timeRemainingTooltip"
+ v-tooltip
:class="timeRemainingStatusClass"
>
<div
class="meter-container"
- role="timeSpentPercent"
:aria-valuenow="timeRemainingPercent"
>
<div
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author_time.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author_time.vue
index 644e4b7d81a..a9868486e83 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author_time.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author_time.vue
@@ -1,11 +1,15 @@
<script>
+ import tooltip from '~/vue_shared/directives/tooltip';
import MrWidgetAuthor from './mr_widget_author.vue';
export default {
- name: 'MRWidgetAuthorTime',
+ name: 'MrWidgetAuthorTime',
components: {
MrWidgetAuthor,
},
+ directives: {
+ tooltip,
+ },
props: {
actionText: {
type: String,
@@ -32,8 +36,7 @@
<mr-widget-author :author="author" />
<time
:title="dateTitle"
- data-toggle="tooltip"
- data-placement="top"
+ v-tooltip
data-container="body"
>
{{ dateReadable }}
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue
index 68b691fc914..25a57e520ee 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue
@@ -1,11 +1,11 @@
<script>
- import mrWidgetAuthorTime from '../../components/mr_widget_author_time.vue';
+ import MrWidgetAuthorTime from '../../components/mr_widget_author_time.vue';
import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetClosed',
components: {
- mrWidgetAuthorTime,
+ MrWidgetAuthorTime,
statusIcon,
},
props: {
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue
index eb581b807a2..bc95706f47d 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue
@@ -4,7 +4,7 @@
import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import { s__, __ } from '~/locale';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
- import mrWidgetAuthorTime from '../../components/mr_widget_author_time.vue';
+ import MrWidgetAuthorTime from '../../components/mr_widget_author_time.vue';
import statusIcon from '../mr_widget_status_icon.vue';
import eventHub from '../../event_hub';
@@ -14,7 +14,7 @@
tooltip,
},
components: {
- mrWidgetAuthorTime,
+ MrWidgetAuthorTime,
loadingIcon,
statusIcon,
ClipboardButton,
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
index c20d07a169d..098e8178265 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
@@ -36,7 +36,7 @@ import {
notify,
SourceBranchRemovalStatus,
} from './dependencies';
-import { setFavicon } from '../lib/utils/common_utils';
+import { setFaviconOverlay } from '../lib/utils/common_utils';
export default {
el: '#js-vue-mr-widget',
@@ -159,8 +159,9 @@ export default {
},
setFaviconHelper() {
if (this.mr.ciStatusFaviconPath) {
- setFavicon(this.mr.ciStatusFaviconPath);
+ return setFaviconOverlay(this.mr.ciStatusFaviconPath);
}
+ return Promise.resolve();
},
fetchDeployments() {
return this.service.fetchDeployments()
diff --git a/app/assets/javascripts/vue_shared/components/file_icon/file_icon_map.js b/app/assets/javascripts/vue_shared/components/file_icon/file_icon_map.js
index 9ffbaae3ea5..9bca1993ccc 100644
--- a/app/assets/javascripts/vue_shared/components/file_icon/file_icon_map.js
+++ b/app/assets/javascripts/vue_shared/components/file_icon/file_icon_map.js
@@ -513,7 +513,7 @@ const fileNameIcons = {
'credits.md': 'credits',
'credits.md.rendered': 'credits',
'.flowconfig': 'flow',
- 'favicon.ico': 'favicon',
+ 'favicon.png': 'favicon',
'karma.conf.js': 'karma',
'karma.conf.ts': 'karma',
'karma.conf.coffee': 'karma',
diff --git a/app/assets/javascripts/vue_shared/models/assignee.js b/app/assets/javascripts/vue_shared/models/assignee.js
new file mode 100644
index 00000000000..4a29b0d0581
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/models/assignee.js
@@ -0,0 +1,13 @@
+export default class ListAssignee {
+ constructor(obj, defaultAvatar) {
+ this.id = obj.id;
+ this.name = obj.name;
+ this.username = obj.username;
+ this.avatar = obj.avatar_url || obj.avatar || defaultAvatar;
+ this.path = obj.path;
+ this.state = obj.state;
+ this.webUrl = obj.web_url || obj.webUrl;
+ }
+}
+
+window.ListAssignee = ListAssignee;
diff --git a/app/assets/stylesheets/bootstrap_migration.scss b/app/assets/stylesheets/bootstrap_migration.scss
index 3785aaa43f0..88d6bae349a 100644
--- a/app/assets/stylesheets/bootstrap_migration.scss
+++ b/app/assets/stylesheets/bootstrap_migration.scss
@@ -89,6 +89,10 @@ a {
color: $gl-link-color;
}
+hr {
+ overflow: hidden;
+}
+
.form-group.row .col-form-label {
// Bootstrap 4 aligns labels to the left
// for horizontal forms
@@ -107,7 +111,7 @@ code {
background-color: $red-100;
border-radius: 3px;
- .code & {
+ .code > & {
background-color: inherit;
padding: unset;
}
@@ -118,10 +122,6 @@ code {
}
}
-.code {
- padding: 9.5px;
-}
-
table {
// Remove any table border lines
border-spacing: 0;
@@ -213,6 +213,10 @@ table {
border-bottom: 1px solid $well-inner-border;
}
}
+
+ .badge.badge-gray {
+ background-color: $well-expand-item;
+ }
}
.card {
@@ -233,6 +237,13 @@ table {
}
}
+.card-header {
+ h3.card-title,
+ h4.card-title {
+ margin-top: 0;
+ }
+}
+
.nav-tabs {
// Override bootstrap's default border
border-bottom: 0;
@@ -251,3 +262,33 @@ table {
pre code {
white-space: pre-wrap;
}
+
+.alert-danger {
+ background-color: $red-500;
+ border-color: $red-500;
+}
+
+.alert-warning,
+.alert-danger,
+.flash-notice {
+ border-radius: 0;
+ color: $white-light;
+
+ h4,
+ a,
+ .alert-link {
+ color: $white-light;
+ }
+}
+
+input[type=color].form-control {
+ height: $input-height;
+}
+
+.toggle-sidebar-button {
+ .collapse-text,
+ .icon-angle-double-left,
+ .icon-angle-double-right {
+ color: $gl-text-color-secondary;
+ }
+}
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 0115f542c88..88b174491dd 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -497,6 +497,10 @@ fieldset[disabled] .btn,
}
}
+[readonly] {
+ cursor: default;
+}
+
.btn-no-padding {
padding: 0;
}
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 996e5c1512d..e5197e27b82 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -305,14 +305,6 @@ img.emoji {
margin-bottom: 10px;
}
-.btn-sign-in {
- text-shadow: none;
-
- @include media-breakpoint-up(sm) {
- margin-top: 8px;
- }
-}
-
.side-filters {
fieldset {
margin-bottom: 15px;
diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss
index 0ee5748952a..551a7e852ae 100644
--- a/app/assets/stylesheets/framework/filters.scss
+++ b/app/assets/stylesheets/framework/filters.scss
@@ -299,6 +299,7 @@
height: 14px;
width: 14px;
vertical-align: middle;
+ margin-bottom: 4px;
}
.dropdown-toggle-text {
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index c76ea532912..03520f42997 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -170,7 +170,7 @@ label {
}
.form-control::-webkit-input-placeholder {
- color: $gl-text-color-secondary;
+ color: $placeholder-text-color;
}
.input-group {
diff --git a/app/assets/stylesheets/framework/gitlab_theme.scss b/app/assets/stylesheets/framework/gitlab_theme.scss
index 14dd3879bdc..b40d02f381a 100644
--- a/app/assets/stylesheets/framework/gitlab_theme.scss
+++ b/app/assets/stylesheets/framework/gitlab_theme.scss
@@ -3,26 +3,26 @@
*/
@mixin gitlab-theme(
- $color-100,
- $color-200,
- $color-500,
- $color-700,
- $color-800,
- $color-900,
+ $location-badge-color,
+ $search-and-nav-links,
+ $active-tab-border,
+ $border-and-box-shadow,
+ $sidebar-text,
+ $nav-svg-color,
$color-alternate
) {
// Header
.navbar-gitlab {
- background-color: $color-900;
+ background-color: $nav-svg-color;
.navbar-collapse {
- color: $color-200;
+ color: $search-and-nav-links;
}
.container-fluid {
.navbar-toggler {
- border-left: 1px solid lighten($color-700, 10%);
+ border-left: 1px solid lighten($border-and-box-shadow, 10%);
}
}
@@ -31,40 +31,40 @@
> li {
> a:hover,
> a:focus {
- background-color: rgba($color-200, 0.2);
+ background-color: rgba($search-and-nav-links, 0.2);
}
&.active > a,
&.dropdown.show > a {
- color: $color-900;
+ color: $nav-svg-color;
background-color: $color-alternate;
}
&.line-separator {
- border-left: 1px solid rgba($color-200, 0.2);
+ border-left: 1px solid rgba($search-and-nav-links, 0.2);
}
}
}
.navbar-sub-nav {
- color: $color-200;
+ color: $search-and-nav-links;
}
.nav {
> li {
- color: $color-200;
+ color: $search-and-nav-links;
> a {
&.header-user-dropdown-toggle {
.header-user-avatar {
- border-color: $color-200;
+ border-color: $search-and-nav-links;
}
}
&:hover,
&:focus {
@include media-breakpoint-up(sm) {
- background-color: rgba($color-200, 0.2);
+ background-color: rgba($search-and-nav-links, 0.2);
}
svg {
@@ -75,12 +75,12 @@
&.active > a,
&.dropdown.show > a {
- color: $color-900;
+ color: $nav-svg-color;
background-color: $color-alternate;
&:hover {
svg {
- fill: $color-900;
+ fill: $nav-svg-color;
}
}
}
@@ -88,7 +88,7 @@
.impersonated-user,
.impersonated-user:hover {
svg {
- fill: $color-900;
+ fill: $nav-svg-color;
}
}
}
@@ -99,34 +99,34 @@
> a {
&:hover,
&:focus {
- background-color: rgba($color-200, 0.2);
+ background-color: rgba($search-and-nav-links, 0.2);
}
}
}
.search {
form {
- background-color: rgba($color-200, 0.2);
+ background-color: rgba($search-and-nav-links, 0.2);
&:hover {
- background-color: rgba($color-200, 0.3);
+ background-color: rgba($search-and-nav-links, 0.3);
}
}
.location-badge {
- color: $color-100;
- background-color: rgba($color-200, 0.1);
- border-right: 1px solid $color-800;
+ color: $location-badge-color;
+ background-color: rgba($search-and-nav-links, 0.1);
+ border-right: 1px solid $sidebar-text;
}
.search-input::placeholder {
- color: rgba($color-200, 0.8);
+ color: rgba($search-and-nav-links, 0.8);
}
.search-input-wrap {
.search-icon,
.clear-icon {
- fill: rgba($color-200, 0.8);
+ fill: rgba($search-and-nav-links, 0.8);
}
}
@@ -141,38 +141,34 @@
.search-input-wrap {
.search-icon {
- fill: rgba($color-200, 0.8);
+ fill: rgba($search-and-nav-links, 0.8);
}
}
}
}
- .btn-sign-in {
- background-color: $color-100;
- color: $color-900;
- }
// Sidebar
.nav-sidebar li.active {
- box-shadow: inset 4px 0 0 $color-700;
+ box-shadow: inset 4px 0 0 $border-and-box-shadow;
> a {
- color: $color-800;
+ color: $sidebar-text;
}
svg {
- fill: $color-800;
+ fill: $sidebar-text;
}
}
.sidebar-top-level-items > li.active .badge.badge-pill {
- color: $color-800;
+ color: $sidebar-text;
}
.nav-links li {
&.active a,
a.active {
- border-bottom: 2px solid $color-500;
+ border-bottom: 2px solid $active-tab-border;
.badge.badge-pill {
font-weight: $gl-font-weight-bold;
@@ -181,27 +177,27 @@
}
.branch-header-title {
- color: $color-700;
+ color: $border-and-box-shadow;
}
.ide-file-list .file.file-active {
- color: $color-700;
+ color: $border-and-box-shadow;
}
.ide-sidebar-link {
&.active {
- color: $color-700;
- box-shadow: inset 3px 0 $color-700;
+ color: $border-and-box-shadow;
+ box-shadow: inset 3px 0 $border-and-box-shadow;
&.is-right {
- box-shadow: inset -3px 0 $color-700;
+ box-shadow: inset -3px 0 $border-and-box-shadow;
}
}
}
}
body {
- &.ui_indigo {
+ &.ui-indigo {
@include gitlab-theme(
$indigo-100,
$indigo-200,
@@ -213,19 +209,19 @@ body {
);
}
- &.ui_dark {
+ &.ui-light-indigo {
@include gitlab-theme(
- $theme-gray-100,
- $theme-gray-200,
- $theme-gray-500,
- $theme-gray-700,
- $theme-gray-800,
- $theme-gray-900,
+ $indigo-100,
+ $indigo-200,
+ $indigo-500,
+ $indigo-500,
+ $indigo-700,
+ $indigo-700,
$white-light
);
}
- &.ui_blue {
+ &.ui-blue {
@include gitlab-theme(
$theme-blue-100,
$theme-blue-200,
@@ -237,7 +233,19 @@ body {
);
}
- &.ui_green {
+ &.ui-light-blue {
+ @include gitlab-theme(
+ $theme-light-blue-100,
+ $theme-light-blue-200,
+ $theme-light-blue-500,
+ $theme-light-blue-500,
+ $theme-light-blue-700,
+ $theme-light-blue-700,
+ $white-light
+ );
+ }
+
+ &.ui-green {
@include gitlab-theme(
$theme-green-100,
$theme-green-200,
@@ -249,7 +257,55 @@ body {
);
}
- &.ui_light {
+ &.ui-light-green {
+ @include gitlab-theme(
+ $theme-green-100,
+ $theme-green-200,
+ $theme-green-500,
+ $theme-green-500,
+ $theme-light-green-700,
+ $theme-light-green-700,
+ $white-light
+ );
+ }
+
+ &.ui-red {
+ @include gitlab-theme(
+ $theme-red-100,
+ $theme-red-200,
+ $theme-red-500,
+ $theme-red-700,
+ $theme-red-800,
+ $theme-red-900,
+ $white-light
+ );
+ }
+
+ &.ui-light-red {
+ @include gitlab-theme(
+ $theme-light-red-100,
+ $theme-light-red-200,
+ $theme-light-red-500,
+ $theme-light-red-500,
+ $theme-light-red-700,
+ $theme-light-red-700,
+ $white-light
+ );
+ }
+
+ &.ui-dark {
+ @include gitlab-theme(
+ $theme-gray-100,
+ $theme-gray-200,
+ $theme-gray-500,
+ $theme-gray-700,
+ $theme-gray-800,
+ $theme-gray-900,
+ $white-light
+ );
+ }
+
+ &.ui-light {
@include gitlab-theme(
$theme-gray-900,
$theme-gray-700,
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 094134b63b0..1d5bdaa8961 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -139,6 +139,8 @@
}
.nav {
+ flex-wrap: nowrap;
+
> li:not(.d-none) a {
@include media-breakpoint-down(xs) {
margin-left: 0;
@@ -158,11 +160,12 @@
}
.navbar-toggler {
+ position: relative;
right: -10px;
border-radius: 0;
min-width: 45px;
padding: 0;
- margin-right: -7px;
+ margin: $gl-padding-8 -7px $gl-padding-8 0;
font-size: 14px;
text-align: center;
color: currentColor;
@@ -186,6 +189,7 @@
display: -webkit-flex;
display: flex;
padding-right: 10px;
+ flex-direction: row;
}
li {
@@ -290,6 +294,10 @@
margin: 8px;
}
}
+
+ .dropdown-menu {
+ position: absolute;
+ }
}
.navbar-sub-nav {
@@ -437,12 +445,18 @@
}
.btn-sign-in {
- margin-top: 3px;
+ background-color: $indigo-100;
+ color: $indigo-900;
font-weight: $gl-font-weight-bold;
+ line-height: 18px;
&:hover {
background-color: $white-light;
}
+
+ @include media-breakpoint-down(xs) {
+ margin-top: $gl-padding-4;
+ }
}
.navbar-nav {
diff --git a/app/assets/stylesheets/framework/pagination.scss b/app/assets/stylesheets/framework/pagination.scss
index 50a1b1c446d..61d02511ff4 100644
--- a/app/assets/stylesheets/framework/pagination.scss
+++ b/app/assets/stylesheets/framework/pagination.scss
@@ -4,3 +4,11 @@
text-decoration: none;
}
}
+
+.page-item {
+ &.active {
+ .page-link {
+ z-index: 3;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/framework/tables.scss b/app/assets/stylesheets/framework/tables.scss
index a10bd1544c5..ba9de6941ac 100644
--- a/app/assets/stylesheets/framework/tables.scss
+++ b/app/assets/stylesheets/framework/tables.scss
@@ -1,5 +1,6 @@
.table-holder {
margin: 0;
+ overflow: auto;
}
table {
@@ -38,6 +39,11 @@ table {
&.wide {
width: 55%;
}
+
+ &.table-th-transparent {
+ background: none;
+ color: $gl-text-color-secondary;
+ }
}
td {
@@ -45,9 +51,91 @@ table {
}
}
}
+
+ &.responsive-table {
+ @include media-breakpoint-down(sm) {
+ thead {
+ display: none;
+ }
+
+ table,
+ tbody,
+ td {
+ display: block;
+ }
+
+ td {
+ color: $gl-text-color-secondary;
+ }
+
+ tbody td.responsive-table-cell {
+ padding: $gl-padding 0;
+ width: 100%;
+ display: flex;
+ text-align: right;
+ align-items: center;
+ justify-content: space-between;
+
+ &[data-column]::before {
+ content: attr(data-column);
+ display: block;
+ text-align: left;
+ padding-right: $gl-padding;
+ color: $gl-text-color-secondary;
+ }
+
+ &:not([data-column]) {
+ flex-direction: row-reverse;
+ }
+ }
+
+ tr.responsive-table-border-start,
+ tr.responsive-table-border-end {
+ display: block;
+ border: solid $gl-text-color-quaternary;
+ padding-left: 0;
+ padding-right: 0;
+
+ > td {
+ border-color: $gl-text-color-quaternary;
+
+ &,
+ &:last-child {
+ padding-left: $gl-padding;
+ padding-right: $gl-padding;
+ }
+ }
+ }
+
+ tr.responsive-table-border-start {
+ border-width: 1px 1px 0;
+ border-radius: $border-radius-default $border-radius-default 0 0;
+ padding-top: 0;
+ padding-bottom: 0;
+
+ > td:first-child {
+ border-top: 0; // always have the <table> top border
+ }
+
+ > td:last-child {
+ border-bottom: 1px solid $gl-text-color-quaternary;
+ }
+ }
+
+ tr.responsive-table-border-end {
+ border-width: 0 1px 1px;
+ border-radius: 0 0 $border-radius-default $border-radius-default;
+ margin-bottom: 2 * $gl-padding;
+
+ > :last-child {
+ border-bottom: 0;
+ }
+ }
+ }
+ }
}
-.responsive-table {
+.responsive-table:not(table) {
@include media-breakpoint-down(sm) {
th {
width: 100%;
diff --git a/app/assets/stylesheets/framework/timeline.scss b/app/assets/stylesheets/framework/timeline.scss
index 75c11590547..dfb145debe7 100644
--- a/app/assets/stylesheets/framework/timeline.scss
+++ b/app/assets/stylesheets/framework/timeline.scss
@@ -4,7 +4,7 @@
padding: 0;
&::before {
- @include notes-media('max', map-get($grid-breakpoints, xs)) {
+ @include notes-media('max', map-get($grid-breakpoints, sm)) {
background: none;
}
}
@@ -34,7 +34,7 @@
.timeline-entry-inner {
position: relative;
- @include notes-media('max', map-get($grid-breakpoints, xs)) {
+ @include notes-media('max', map-get($grid-breakpoints, sm)) {
.timeline-icon {
display: none;
}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 946223cfff0..d1179df96a9 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -99,7 +99,7 @@ $theme-gray-200: #dfdfdf;
$theme-gray-300: #cccccc;
$theme-gray-400: #bababa;
$theme-gray-500: #a7a7a7;
-$theme-gray-600: #949494;
+$theme-gray-600: #919191;
$theme-gray-700: #707070;
$theme-gray-800: #4f4f4f;
$theme-gray-900: #2e2e2e;
@@ -117,6 +117,15 @@ $theme-blue-800: #25496e;
$theme-blue-900: #1a3652;
$theme-blue-950: #0f2235;
+$theme-light-blue-50: #f2f7fc;
+$theme-light-blue-100: #ebf1f7;
+$theme-light-blue-200: #c9dcf2;
+$theme-light-blue-300: #83abd4;
+$theme-light-blue-400: #4d86bf;
+$theme-light-blue-500: #367cc2;
+$theme-light-blue-600: #3771ab;
+$theme-light-blue-700: #2261a1;
+
$theme-green-50: #f2faf6;
$theme-green-100: #e4f3ea;
$theme-green-200: #c0dfcd;
@@ -129,6 +138,29 @@ $theme-green-800: #145d33;
$theme-green-900: #0d4524;
$theme-green-950: #072d16;
+$theme-light-green-700: #156b39;
+
+$theme-red-50: #fcf4f2;
+$theme-red-100: #fae9e6;
+$theme-red-200: #ebcac5;
+$theme-red-300: #d99b91;
+$theme-red-400: #b0655a;
+$theme-red-500: #ad4a3b;
+$theme-red-600: #9e4133;
+$theme-red-700: #912f20;
+$theme-red-800: #78291d;
+$theme-red-900: #691a16;
+$theme-red-950: #36140f;
+
+$theme-light-red-50: #fff6f5;
+$theme-light-red-100: #fae2de;
+$theme-light-red-200: #f7d5d0;
+$theme-light-red-300: #d9796a;
+$theme-light-red-400: #cf604e;
+$theme-light-red-500: #c24b38;
+$theme-light-red-600: #b03927;
+$theme-light-red-700: #a62e21;
+
$black: #000;
$black-transparent: rgba(0, 0, 0, 0.3);
$almost-black: #242424;
@@ -142,11 +174,6 @@ $border-gray-normal-dashed: darken($gray-normal, $darken-border-dashed-factor);
$border-gray-dark: darken($white-normal, $darken-border-factor);
/*
- * Override Bootstrap 4 variables
- */
-$secondary: $gray-light;
-
-/*
* UI elements
*/
$border-color: #e5e5e5;
@@ -164,7 +191,7 @@ $gl-font-weight-normal: 400;
$gl-font-weight-bold: 600;
$gl-text-color: #2e2e2e;
$gl-text-color-secondary: #707070;
-$gl-text-color-tertiary: #949494;
+$gl-text-color-tertiary: #919191;
$gl-text-color-quaternary: #d6d6d6;
$gl-text-color-inverted: rgba(255, 255, 255, 1);
$gl-text-color-secondary-inverted: rgba(255, 255, 255, 0.85);
@@ -408,6 +435,22 @@ $badge-bg: rgba(0, 0, 0, 0.07);
$badge-color: $gl-text-color-secondary;
/*
+* Pagination
+*/
+$pagination-padding-y: 6px;
+$pagination-padding-x: 16px;
+$pagination-line-height: 20px;
+$pagination-border-color: $border-color;
+$pagination-active-bg: $blue-600;
+$pagination-active-border-color: $blue-600;
+$pagination-hover-bg: $blue-50;
+$pagination-hover-border-color: $border-color;
+$pagination-hover-color: $gl-text-color;
+$pagination-disabled-color: #cdcdcd;
+$pagination-disabled-bg: $gray-light;
+$pagination-disabled-border-color: $border-color;
+
+/*
* Status icons
*/
$status-icon-size: 22px;
@@ -776,3 +819,16 @@ $modal-body-height: 134px;
Prometheus
*/
$prometheus-table-row-highlight-color: $theme-gray-100;
+
+$priority-label-empty-state-width: 114px;
+
+/*
+ * Override Bootstrap 4 variables
+ */
+
+$secondary: $gray-light;
+$input-disabled-bg: $gray-light;
+$input-border-color: $theme-gray-200;
+$input-color: $gl-text-color;
+$font-family-sans-serif: $regular_font;
+$font-family-monospace: $monospace_font;
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index 1c3d312f7ac..b2416a3d5bc 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -282,9 +282,6 @@
box-shadow: 0 1px 2px $issue-boards-card-shadow;
list-style: none;
- // as a fallback, hide overflow content so that dragging and dropping still works
- overflow: hidden;
-
&:not(:last-child) {
margin-bottom: 5px;
}
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index 9213ccd4cdf..f030189af06 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -12,26 +12,22 @@
@keyframes blinking-dots {
0% {
background-color: rgba($white-light, 1);
- box-shadow: 12px 0 0 0 rgba($white-light, 0.2),
- 24px 0 0 0 rgba($white-light, 0.2);
+ box-shadow: 12px 0 0 0 rgba($white-light, 0.2), 24px 0 0 0 rgba($white-light, 0.2);
}
25% {
background-color: rgba($white-light, 0.4);
- box-shadow: 12px 0 0 0 rgba($white-light, 2),
- 24px 0 0 0 rgba($white-light, 0.2);
+ box-shadow: 12px 0 0 0 rgba($white-light, 2), 24px 0 0 0 rgba($white-light, 0.2);
}
75% {
background-color: rgba($white-light, 0.4);
- box-shadow: 12px 0 0 0 rgba($white-light, 0.2),
- 24px 0 0 0 rgba($white-light, 1);
+ box-shadow: 12px 0 0 0 rgba($white-light, 0.2), 24px 0 0 0 rgba($white-light, 1);
}
100% {
background-color: rgba($white-light, 1);
- box-shadow: 12px 0 0 0 rgba($white-light, 0.2),
- 24px 0 0 0 rgba($white-light, 0.2);
+ box-shadow: 12px 0 0 0 rgba($white-light, 0.2), 24px 0 0 0 rgba($white-light, 0.2);
}
}
@@ -71,6 +67,10 @@
.bash {
display: block;
}
+
+ &.build-trace-rounded {
+ border-radius: $border-radius-base;
+ }
}
.top-bar {
diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss
index 25f011a534b..785df23a355 100644
--- a/app/assets/stylesheets/pages/labels.scss
+++ b/app/assets/stylesheets/pages/labels.scss
@@ -57,69 +57,8 @@
border-bottom-left-radius: $border-radius-base;
}
-.label-row {
- .label-name {
- display: inline-block;
- margin-bottom: 10px;
-
- @include media-breakpoint-up(sm) {
- width: 200px;
- margin-left: $gl-padding * 2;
- margin-bottom: 0;
- }
-
- .badge {
- overflow: hidden;
- text-overflow: ellipsis;
- max-width: 100%;
- }
- }
-
- .label-type {
- display: block;
- margin-bottom: 10px;
- margin-left: 50px;
-
- @include media-breakpoint-up(sm) {
- display: inline-block;
- width: 100px;
- margin-left: 10px;
- margin-bottom: 0;
- vertical-align: top;
- }
- }
-
- .label-description {
- display: block;
- margin-bottom: 10px;
-
- .description-text {
- margin-bottom: $gl-padding;
- }
-
- a {
- color: $blue-600;
- }
-
- @include media-breakpoint-up(sm) {
- display: inline-block;
- max-width: 50%;
- margin-left: 10px;
- margin-bottom: 0;
- vertical-align: top;
- }
- }
-
- .badge {
- padding: 4px $grid-size;
- font-size: $label-font-size;
- position: relative;
- top: ($grid-size / 2);
- }
-}
-
.color-label {
- padding: 0 $grid-size;
+ padding: $gl-padding-4 $grid-size;
line-height: 16px;
border-radius: $label-border-radius;
color: $white-light;
@@ -133,26 +72,29 @@
}
.manage-labels-list {
- @media(min-width: map-get($grid-breakpoints, md)) {
- &.content-list li {
- padding: $gl-padding 0;
- }
- }
-
> li:not(.empty-message):not(.is-not-draggable) {
background-color: $white-light;
- cursor: move;
- cursor: -webkit-grab;
- cursor: -moz-grab;
-
- &:active {
- cursor: -webkit-grabbing;
- cursor: -moz-grabbing;
- }
+ margin-bottom: 5px;
+ display: flex;
+ justify-content: space-between;
+ padding: $gl-padding;
+ border-radius: $border-radius-default;
&.sortable-ghost {
opacity: 0.3;
}
+
+ .prioritized-labels & {
+ box-shadow: 0 1px 2px $issue-boards-card-shadow;
+ cursor: move;
+ cursor: -webkit-grab;
+ cursor: -moz-grab;
+
+ &:active {
+ cursor: -webkit-grabbing;
+ cursor: -moz-grabbing;
+ }
+ }
}
.btn-action {
@@ -170,36 +112,11 @@
}
}
}
-
- .dropdown {
- @include media-breakpoint-up(sm) {
- float: right;
- }
- }
-
- @include media-breakpoint-down(xs) {
- .dropdown-menu {
- min-width: 100%;
- }
- }
-}
-
-.draggable-handler {
- display: inline-block;
- vertical-align: top;
- margin: 5px 0;
- opacity: 0;
- transition: opacity .3s;
- color: $gray-darkest;
}
.prioritized-labels {
margin-bottom: 30px;
- h5 {
- font-size: $gl-font-size;
- }
-
.add-priority {
display: none;
color: $gray-light;
@@ -214,31 +131,11 @@
}
.other-labels {
- h5 {
- font-size: $gl-font-size;
- }
-
.remove-priority {
display: none;
}
}
-.toggle-priority {
- display: inline-block;
- vertical-align: top;
-
- button {
- border-color: transparent;
- padding: 5px 8px;
- vertical-align: top;
- font-size: 14px;
-
- &:hover {
- border-color: transparent;
- }
- }
-}
-
.filtered-labels {
font-size: 0;
padding: 12px 16px;
@@ -292,10 +189,8 @@
}
.label-subscribe-button {
- @media(min-width: map-get($grid-breakpoints, md)) {
- min-width: 105px;
- margin-left: $gl-padding;
- }
+ width: 105px;
+ font-weight: 200;
.label-subscribe-button-icon {
&[disabled] {
@@ -332,3 +227,95 @@
font-size: $label-font-size;
}
}
+
+.labels-container {
+ background-color: $gray-light;
+ border-radius: $border-radius-default;
+ padding: $gl-padding $gl-padding-8;
+}
+
+.label-actions-list {
+ list-style: none;
+ flex-shrink: 0;
+ padding: 0;
+}
+
+.label-badge {
+ color: $theme-gray-900;
+ font-weight: $gl-font-weight-normal;
+ padding: $gl-padding-4 $gl-padding-8;
+ border-radius: $border-radius-default;
+ font-size: $label-font-size;
+}
+
+.label-badge-blue {
+ background-color: $theme-blue-100;
+}
+
+.label-badge-gray {
+ background-color: $theme-gray-100;
+}
+
+.label-links {
+ list-style: none;
+ padding: 0;
+ white-space: nowrap;
+}
+
+.label-link-item {
+ padding: 0;
+}
+
+.label-list-item {
+ .content-list &::before,
+ .content-list &::after {
+ content: none;
+ }
+
+ .label-name {
+ width: 150px;
+ flex-shrink: 0;
+
+ .label {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ max-width: 100%;
+ }
+ }
+
+ .label-description {
+ flex-grow: 1;
+
+ a {
+ color: $blue-600;
+ }
+ }
+
+ .label {
+ padding: 4px $grid-size;
+ font-size: $label-font-size;
+ position: relative;
+ top: $gl-padding-4;
+ }
+
+ .label-action {
+ color: $theme-gray-800;
+ cursor: pointer;
+
+ svg {
+ fill: $theme-gray-800;
+ }
+
+ &:hover {
+ color: $blue-600;
+
+ svg {
+ fill: $blue-600;
+ }
+ }
+ }
+}
+
+.priority-labels-empty-state .svg-content img {
+ max-width: $priority-label-empty-state-width;
+}
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index f85f66b9c0b..30428fd198d 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -321,18 +321,17 @@
}
.build-failures {
+ th {
+ border-top: 0;
+ }
+
.build-state {
padding: 20px 2px;
.build-name {
- float: right;
font-weight: $gl-font-weight-normal;
}
- .ci-status-icon-failed svg {
- vertical-align: middle;
- }
-
.stage {
color: $gl-text-color-secondary;
font-weight: $gl-font-weight-normal;
@@ -344,6 +343,81 @@
border: 0;
line-height: initial;
}
+
+ .build-trace-row td {
+ border-top: 0;
+ border-bottom-width: 1px;
+ border-bottom-style: solid;
+ padding-top: 0;
+ }
+
+ .build-trace {
+ width: 100%;
+ text-align: left;
+ margin-top: $gl-padding;
+ }
+
+ .build-name {
+ width: 196px;
+
+ a {
+ font-weight: $gl-font-weight-bold;
+ color: $gl-text-color;
+ text-decoration: none;
+
+ &:focus,
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+ }
+
+ .build-actions {
+ width: 70px;
+ text-align: right;
+ }
+
+ .build-stage {
+ width: 140px;
+ }
+
+ .ci-status-icon-failed {
+ padding: 10px 0 10px 12px;
+ width: 12px + 24px; // padding-left + svg width
+ }
+
+ .build-icon svg {
+ width: 24px;
+ height: 24px;
+ vertical-align: middle;
+ }
+
+ .build-state,
+ .build-trace-row {
+ > td:last-child {
+ padding-right: 0;
+ }
+ }
+
+ @include media-breakpoint-down(sm) {
+ td:empty {
+ display: none;
+ }
+
+ .ci-table {
+ margin-top: 2 * $gl-padding;
+ }
+
+ .build-trace-container {
+ padding-top: $gl-padding;
+ padding-bottom: $gl-padding;
+ }
+
+ .build-trace {
+ margin-bottom: 0;
+ margin-top: 0;
+ }
+ }
}
.pipeline-tab-content {
@@ -929,7 +1003,7 @@ button.mini-pipeline-graph-dropdown-toggle {
&.dropdown-menu {
transform: translate(-80%, 0);
- @media(min-width: map-get($grid-breakpoints, md)) {
+ @media (min-width: map-get($grid-breakpoints, md)) {
transform: translate(-50%, 0);
right: auto;
left: 50%;
diff --git a/app/assets/stylesheets/pages/profiles/preferences.scss b/app/assets/stylesheets/pages/profiles/preferences.scss
index 68d40b56133..babe81cb0f7 100644
--- a/app/assets/stylesheets/pages/profiles/preferences.scss
+++ b/app/assets/stylesheets/pages/profiles/preferences.scss
@@ -1,25 +1,3 @@
-@mixin application-theme-preview($color-1, $color-2, $color-3, $color-4) {
- .one {
- background-color: $color-1;
- border-top-left-radius: $border-radius-default;
- }
-
- .two {
- background-color: $color-2;
- border-top-right-radius: $border-radius-default;
- }
-
- .three {
- background-color: $color-3;
- border-bottom-left-radius: $border-radius-default;
- }
-
- .four {
- background-color: $color-4;
- border-bottom-right-radius: $border-radius-default;
- }
-}
-
.multi-file-editor-options {
label {
margin-right: 20px;
@@ -38,44 +16,61 @@
.application-theme {
label {
- margin-right: 20px;
+ margin: 0 $gl-padding $gl-padding 0;
text-align: center;
}
.preview {
font-size: 0;
- margin-bottom: 10px;
+ height: 48px;
+ border-radius: 4px;
+ min-width: 135px;
+ margin-bottom: $gl-padding-8;
+
+ &.ui-indigo {
+ background-color: $indigo-900;
+ }
+
+ &.ui-light-indigo {
+ background-color: $indigo-700;
+ }
- &.indigo {
- @include application-theme-preview($indigo-900, $indigo-700, $indigo-800, $indigo-500);
+ &.ui-blue {
+ background-color: $theme-blue-900;
}
- &.dark {
- @include application-theme-preview($theme-gray-900, $theme-gray-700, $theme-gray-800, $theme-gray-600);
+ &.ui-light-blue {
+ background-color: $theme-light-blue-700;
}
- &.light {
- @include application-theme-preview($theme-gray-600, $theme-gray-200, $theme-gray-400, $theme-gray-100);
+ &.ui-green {
+ background-color: $theme-green-900;
}
- &.blue {
- @include application-theme-preview($theme-blue-900, $theme-blue-700, $theme-blue-800, $theme-blue-500);
+ &.ui-light-green {
+ background-color: $theme-light-green-700;
}
- &.green {
- @include application-theme-preview($theme-green-900, $theme-green-700, $theme-green-800, $theme-green-500);
+ &.ui-red {
+ background-color: $theme-red-900;
+ }
+
+ &.ui-light-red {
+ background-color: $theme-light-red-700;
+ }
+
+ &.ui-dark {
+ background-color: $theme-gray-900;
+ }
+
+ &.ui-light {
+ background-color: $theme-gray-200;
}
}
.preview-row {
display: block;
}
-
- .quadrant {
- display: inline-block;
- height: 50px;
- width: 80px;
- }
}
.syntax-theme {
diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss
index a35c4ff7c80..765c926751a 100644
--- a/app/assets/stylesheets/pages/search.scss
+++ b/app/assets/stylesheets/pages/search.scss
@@ -18,7 +18,8 @@
.file-finder-input:hover,
.issuable-search-form:hover,
.search-text-input:hover,
-.form-control:hover {
+.form-control:hover,
+:not[readonly] {
border-color: lighten($dropdown-input-focus-border, 20%);
box-shadow: 0 0 4px lighten($search-input-focus-shadow-color, 20%);
}
@@ -28,7 +29,7 @@ input[type="checkbox"]:hover {
}
.search {
- margin: 4px 8px 0;
+ margin: 0 8px;
form {
@extend .form-control;
diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss
index 16e999341da..1f8e61257a9 100644
--- a/app/assets/stylesheets/pages/settings.scss
+++ b/app/assets/stylesheets/pages/settings.scss
@@ -127,12 +127,16 @@
color: $gl-danger;
}
-.service-settings .form-control-label {
- padding-top: 0;
+.service-settings {
+ input[type="radio"],
+ input[type="checkbox"] {
+ margin-top: 10px;
+ }
}
.integration-settings-form {
- .card.card-body {
+ .card.card-body,
+ .info-well {
padding: $gl-padding / 2;
box-shadow: none;
}
diff --git a/app/assets/stylesheets/pages/settings_ci_cd.scss b/app/assets/stylesheets/pages/settings_ci_cd.scss
index a355e2dee24..777fdb3581e 100644
--- a/app/assets/stylesheets/pages/settings_ci_cd.scss
+++ b/app/assets/stylesheets/pages/settings_ci_cd.scss
@@ -16,3 +16,12 @@
.registry-placeholder {
min-height: 60px;
}
+
+.auto-devops-card {
+ margin-bottom: $gl-vert-padding;
+
+ > .card-body {
+ border-radius: $card-border-radius;
+ padding: $gl-padding $gl-padding-24;
+ }
+}
diff --git a/app/assets/stylesheets/performance_bar.scss b/app/assets/stylesheets/performance_bar.scss
index 06ef58531d7..8cdf2275551 100644
--- a/app/assets/stylesheets/performance_bar.scss
+++ b/app/assets/stylesheets/performance_bar.scss
@@ -15,6 +15,7 @@
color: $perf-bar-text;
select {
+ color: $perf-bar-text;
width: 200px;
}