diff options
author | Phil Hughes <me@iamphill.com> | 2017-05-16 10:37:05 +0100 |
---|---|---|
committer | Phil Hughes <me@iamphill.com> | 2017-05-16 10:37:10 +0100 |
commit | 86ab9edbb2836feb3eb52fe859f1bc9521787fc2 (patch) | |
tree | be902c3844b2ebd97b284ab8bdfff492809bf91a /app/assets | |
parent | 96a46521002f17aff2fc09f02778432ae049c6ee (diff) | |
parent | 5f2b142b66ed8f355b36b91907ca9bea17c070f2 (diff) | |
download | gitlab-ce-86ab9edbb2836feb3eb52fe859f1bc9521787fc2.tar.gz |
Merge branch 'issue-edit-inline' into issue-edit-inline-description-field
[ci skip]
Diffstat (limited to 'app/assets')
16 files changed, 175 insertions, 149 deletions
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js index 9fea563370f..57d247e11a9 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_manager.js +++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js @@ -16,10 +16,14 @@ class FilteredSearchManager { this.recentSearchesStore = new RecentSearchesStore({ isLocalStorageAvailable: RecentSearchesService.isAvailable(), }); - let recentSearchesKey = 'issue-recent-searches'; + const searchHistoryDropdownElement = document.querySelector('.js-filtered-search-history-dropdown'); + const projectPath = searchHistoryDropdownElement ? + searchHistoryDropdownElement.dataset.projectFullPath : 'project'; + let recentSearchesPagePrefix = 'issue-recent-searches'; if (page === 'merge_requests') { - recentSearchesKey = 'merge-request-recent-searches'; + recentSearchesPagePrefix = 'merge-request-recent-searches'; } + const recentSearchesKey = `${projectPath}-${recentSearchesPagePrefix}`; this.recentSearchesService = new RecentSearchesService(recentSearchesKey); // Fetch recent searches from localStorage @@ -47,7 +51,7 @@ class FilteredSearchManager { this.recentSearchesRoot = new RecentSearchesRoot( this.recentSearchesStore, this.recentSearchesService, - document.querySelector('.js-filtered-search-history-dropdown'), + searchHistoryDropdownElement, ); this.recentSearchesRoot.init(); diff --git a/app/assets/javascripts/filtered_search/stores/recent_searches_store.js b/app/assets/javascripts/filtered_search/stores/recent_searches_store.js index 066be69766a..35fc15e4c87 100644 --- a/app/assets/javascripts/filtered_search/stores/recent_searches_store.js +++ b/app/assets/javascripts/filtered_search/stores/recent_searches_store.js @@ -3,6 +3,7 @@ import _ from 'underscore'; class RecentSearchesStore { constructor(initialState = {}) { this.state = Object.assign({ + isLocalStorageAvailable: true, recentSearches: [], }, initialState); } diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue index 6ec66da0ed4..eb594cfb60b 100644 --- a/app/assets/javascripts/issue_show/components/app.vue +++ b/app/assets/javascripts/issue_show/components/app.vue @@ -7,7 +7,7 @@ import Service from '../services/index'; import Store from '../stores'; import titleComponent from './title.vue'; import descriptionComponent from './description.vue'; -import editActions from './edit_actions.vue'; +import formComponent from './form.vue'; export default { props: { @@ -60,19 +60,18 @@ export default { return { store, state: store.state, - formState: store.formState, showForm: false, }; }, computed: { - elementType() { - return this.showForm ? 'form' : 'div'; + formState() { + return this.store.formState; }, }, components: { descriptionComponent, titleComponent, - editActions, + formComponent, }, methods: { openForm() { @@ -82,8 +81,11 @@ export default { description: this.state.descriptionText, }; }, + closeForm() { + this.showForm = false; + }, updateIssuable() { - this.service.updateIssuable(this.formState) + this.service.updateIssuable(this.store.formState) .then(() => { eventHub.$emit('close.form'); }) @@ -134,32 +136,38 @@ export default { eventHub.$on('delete.issuable', this.deleteIssuable); eventHub.$on('update.issuable', this.updateIssuable); + eventHub.$on('close.form', this.closeForm); eventHub.$on('open.form', this.openForm); }, beforeDestroy() { eventHub.$off('delete.issuable', this.deleteIssuable); eventHub.$off('update.issuable', this.updateIssuable); - eventHub.$on('open.form', this.openForm); + eventHub.$off('close.form', this.closeForm); + eventHub.$off('open.form', this.openForm); }, }; </script> <template> - <div :is="elementType"> - <title-component - :store="store" - :show-form="showForm" - :issuable-ref="issuableRef" - :title-html="state.titleHtml" - :title-text="state.titleText" /> - <description-component - :store="store" - :show-form="showForm" - :can-update="canUpdate" - :markdown-preview-url="markdownPreviewUrl" - :markdown-docs="markdownDocs" /> - <edit-actions + <div> + <form-component v-if="canUpdate && showForm" - :can-destroy="canDestroy" /> + :form-state="formState" + :can-destroy="canDestroy" + :markdown-docs="markdownDocs" + :markdown-preview-url="markdownPreviewUrl" /> + <div v-else> + <title-component + :issuable-ref="issuableRef" + :title-html="state.titleHtml" + :title-text="state.titleText" /> + <description-component + v-if="state.descriptionHtml" + :can-update="canUpdate" + :description-html="state.descriptionHtml" + :description-text="state.descriptionText" + :updated-at="state.updatedAt" + :task-status="state.taskStatus" /> + </div> </div> </template> diff --git a/app/assets/javascripts/issue_show/components/description.vue b/app/assets/javascripts/issue_show/components/description.vue index fb542c70489..fc73d5db899 100644 --- a/app/assets/javascripts/issue_show/components/description.vue +++ b/app/assets/javascripts/issue_show/components/description.vue @@ -1,6 +1,5 @@ <script> import animateMixin from '../mixins/animate'; - import descriptionField from './fields/description.vue'; export default { mixins: [animateMixin], @@ -9,45 +8,32 @@ type: Boolean, required: true, }, - store: { - type: Object, + descriptionHtml: { + type: String, required: true, }, - showForm: { - type: Boolean, + descriptionText: { + type: String, required: true, }, - markdownPreviewUrl: { + updatedAt: { type: String, - required: true, + required: false, + default: '', }, - markdownDocs: { + taskStatus: { type: String, - required: true, + required: false, + default: '', }, }, data() { return { - state: this.store.state, preAnimation: false, pulseAnimation: false, timeAgoEl: $('.js-issue-edited-ago'), }; }, - computed: { - descriptionHtml() { - return this.state.descriptionHtml; - }, - descriptionText() { - return this.state.descriptionText; - }, - updatedAt() { - return this.state.updated_at; - }, - taskStatus() { - return this.state.taskStatus; - }, - }, watch: { descriptionHtml() { this.animateChange(); @@ -91,9 +77,6 @@ } }, }, - components: { - descriptionField, - }, mounted() { this.renderGFM(); }, @@ -101,32 +84,25 @@ </script> <template> - <div :class="{ 'common-note-form': showForm }"> - <description-field - v-if="showForm" - :store="store" - :markdown-preview-url="markdownPreviewUrl" - :markdown-docs="markdownDocs" /> + <div + v-else-if="descriptionHtml" + class="description" + :class="{ + 'js-task-list-container': canUpdate + }"> <div - v-else-if="descriptionHtml" - class="description" + class="wiki" :class="{ - 'js-task-list-container': canUpdate - }"> - <div - class="wiki" - :class="{ - 'issue-realtime-pre-pulse': preAnimation, - 'issue-realtime-trigger-pulse': pulseAnimation - }" - v-html="descriptionHtml" - ref="gfm-content"> - </div> - <textarea - class="hidden js-task-list-field" - v-if="descriptionText" - v-model="descriptionText"> - </textarea> + 'issue-realtime-pre-pulse': preAnimation, + 'issue-realtime-trigger-pulse': pulseAnimation + }" + v-html="descriptionHtml" + ref="gfm-content"> </div> + <textarea + class="hidden js-task-list-field" + v-if="descriptionText" + v-model="descriptionText"> + </textarea> </div> </template> diff --git a/app/assets/javascripts/issue_show/components/fields/description.vue b/app/assets/javascripts/issue_show/components/fields/description.vue index bade7634e72..b4c31811a0b 100644 --- a/app/assets/javascripts/issue_show/components/fields/description.vue +++ b/app/assets/javascripts/issue_show/components/fields/description.vue @@ -4,7 +4,7 @@ export default { props: { - store: { + formState: { type: Object, required: true, }, @@ -17,11 +17,6 @@ required: true, }, }, - data() { - return { - state: this.store.formState, - }; - }, components: { markdownField, }, @@ -29,7 +24,7 @@ </script> <template> - <div> + <div class="common-note-form"> <label class="sr-only" for="issue-description"> @@ -43,7 +38,7 @@ class="note-textarea js-gfm-input js-autosize markdown-area" data-supports-slash-commands="false" aria-label="Description" - v-model="state.description" + v-model="formState.description" ref="textatea" slot="textarea"> </textarea> diff --git a/app/assets/javascripts/issue_show/components/fields/title.vue b/app/assets/javascripts/issue_show/components/fields/title.vue index 732a4021205..01ae6fd3dd2 100644 --- a/app/assets/javascripts/issue_show/components/fields/title.vue +++ b/app/assets/javascripts/issue_show/components/fields/title.vue @@ -1,16 +1,11 @@ <script> export default { props: { - store: { + formState: { type: Object, required: true, }, }, - data() { - return { - state: this.store.formState, - }; - }, }; </script> @@ -27,6 +22,6 @@ type="text" placeholder="Issue title" aria-label="Issue title" - v-model="state.title" /> + v-model="formState.title" /> </fieldset> </template> diff --git a/app/assets/javascripts/issue_show/components/form.vue b/app/assets/javascripts/issue_show/components/form.vue new file mode 100644 index 00000000000..7732a1c194a --- /dev/null +++ b/app/assets/javascripts/issue_show/components/form.vue @@ -0,0 +1,44 @@ +<script> + import titleField from './fields/title.vue'; + import descriptionField from './fields/description.vue'; + import editActions from './edit_actions.vue'; + + export default { + props: { + canDestroy: { + type: Boolean, + required: true, + }, + formState: { + type: Object, + required: true, + }, + markdownPreviewUrl: { + type: String, + required: true, + }, + markdownDocs: { + type: String, + required: true, + }, + }, + components: { + titleField, + descriptionField, + editActions, + }, + }; +</script> + +<template> + <form> + <title-field + :form-state="formState" /> + <description-field + :form-state="formState" + :markdown-preview-url="markdownPreviewUrl" + :markdown-docs="markdownDocs" /> + <edit-actions + :can-destroy="canDestroy" /> + </form> +</template> diff --git a/app/assets/javascripts/issue_show/components/title.vue b/app/assets/javascripts/issue_show/components/title.vue index a61ce414891..a9dabd4cff1 100644 --- a/app/assets/javascripts/issue_show/components/title.vue +++ b/app/assets/javascripts/issue_show/components/title.vue @@ -1,12 +1,8 @@ <script> import animateMixin from '../mixins/animate'; - import titleField from './fields/title.vue'; export default { mixins: [animateMixin], - components: { - titleField, - }, data() { return { preAnimation: false, @@ -27,14 +23,6 @@ type: String, required: true, }, - store: { - type: Object, - required: true, - }, - showForm: { - type: Boolean, - required: true, - }, }, watch: { titleHtml() { @@ -53,19 +41,13 @@ </script> <template> - <div> - <title-field - v-if="showForm" - :store="store" /> - <h2 - v-else - class="title" - :class="{ - 'issue-realtime-pre-pulse': preAnimation, - 'issue-realtime-trigger-pulse': pulseAnimation - }" - v-html="titleHtml" - > - </h2> - </div> + <h2 + class="title" + :class="{ + 'issue-realtime-pre-pulse': preAnimation, + 'issue-realtime-trigger-pulse': pulseAnimation + }" + v-html="titleHtml" + > + </h2> </template> diff --git a/app/assets/javascripts/issue_show/services/index.js b/app/assets/javascripts/issue_show/services/index.js index 5da15882c25..0ceff34cf8b 100644 --- a/app/assets/javascripts/issue_show/services/index.js +++ b/app/assets/javascripts/issue_show/services/index.js @@ -8,15 +8,15 @@ export default class Service { this.endpoint = endpoint; this.resource = Vue.resource(this.endpoint, {}, { - rendered_title: { + realtimeChanges: { method: 'GET', - url: `${this.endpoint}/rendered_title`, + url: `${this.endpoint}/realtime_changes`, }, }); } getData() { - return this.resource.rendered_title(); + return this.resource.realtimeChanges(); } deleteIssuable() { diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index 7ba44835741..f143bfbfc29 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -24,7 +24,7 @@ const normalizeNewlines = function(str) { (function() { this.Notes = (function() { const MAX_VISIBLE_COMMIT_LIST_COUNT = 3; - const REGEX_SLASH_COMMANDS = /^\/\w+/gm; + const REGEX_SLASH_COMMANDS = /^\/\w+.*$/gm; Notes.interval = null; @@ -1170,6 +1170,7 @@ const normalizeNewlines = function(str) { */ Notes.prototype.createPlaceholderNote = function({ formContent, uniqueId, isDiscussionNote, currentUsername, currentUserFullname }) { const discussionClass = isDiscussionNote ? 'discussion' : ''; + const escapedFormContent = _.escape(formContent); const $tempNote = $( `<li id="${uniqueId}" class="note being-posted fade-in-half timeline-entry"> <div class="timeline-entry-inner"> @@ -1183,14 +1184,11 @@ const normalizeNewlines = function(str) { <span class="hidden-xs">${currentUserFullname}</span> <span class="note-headline-light">@${currentUsername}</span> </a> - <span class="note-headline-light"> - <i class="fa fa-spinner fa-spin" aria-label="Comment is being posted" aria-hidden="true"></i> - </span> </div> </div> <div class="note-body"> <div class="note-text"> - <p>${formContent}</p> + <p>${escapedFormContent}</p> </div> </div> </div> diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index 64e6ab391b6..d2f6a227128 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -24,10 +24,10 @@ } @mixin scrolling-links() { - white-space: nowrap; overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch; + display: flex; &::-webkit-scrollbar { display: none; @@ -35,6 +35,7 @@ } .nav-links { + display: flex; padding: 0; margin: 0; list-style: none; @@ -42,17 +43,16 @@ border-bottom: 1px solid $border-color; li { - display: inline-block; + display: flex; a { - display: inline-block; padding: $gl-btn-padding; padding-bottom: 11px; - margin-bottom: -1px; font-size: 14px; line-height: 28px; color: $gl-text-color-secondary; border-bottom: 2px solid transparent; + white-space: nowrap; &:hover, &:active, @@ -85,10 +85,10 @@ .container-fluid { background-color: $gray-normal; margin-bottom: 0; + display: flex; } li { - &.active a { border-bottom: none; color: $link-underline-blue; @@ -137,9 +137,9 @@ } .nav-links { - display: inline-block; margin-bottom: 0; border-bottom: none; + float: left; &.wide { width: 100%; @@ -337,6 +337,10 @@ border-bottom: none; height: 51px; + @media (min-width: $screen-sm-min) { + justify-content: center; + } + li { a { padding-top: 10px; @@ -347,6 +351,7 @@ .scrolling-tabs-container { position: relative; + overflow: hidden; .nav-links { @include scrolling-links(); @@ -484,10 +489,7 @@ .inner-page-scroll-tabs { position: relative; - - .nav-links { - padding-bottom: 1px; - } + overflow: hidden; .fade-right { @include fade(left, $white-light); diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 2b5ab539955..018f61ca3a8 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -53,6 +53,7 @@ .right-sidebar-expanded { padding-right: 0; + z-index: 300; @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { &:not(.wiki-sidebar):not(.build-sidebar) .content-wrapper { diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index 724b4080ee0..14a62b6cbf0 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -378,7 +378,7 @@ background-color: $row-hover; } - .fa-spinner { + .fa-refresh { font-size: 13px; margin-left: 3px; } diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index c4210ffd823..0184208ab82 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -23,16 +23,6 @@ .merge-manually { @extend .fixed-width-container; } - - .merge-request-tabs-holder { - &.affix { - border-bottom: 1px solid $border-color; - - .nav-links { - border: 0; - } - } - } } .merge-request-details { @@ -206,7 +196,7 @@ transition: width .3s; background: $gray-light; padding: 10px 20px; - z-index: 2; + z-index: 200; &.right-sidebar-expanded { width: $gutter_width; diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 0173a05b403..d208e54207e 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -121,6 +121,7 @@ .dropdown-menu { margin-top: 11px; + z-index: 200; } .ci-action-icon-wrapper { @@ -690,8 +691,9 @@ .merge-request-tabs-holder { top: $header-height; - z-index: 10; + z-index: 100; background-color: $white-light; + border-bottom: 1px solid $border-color; @media(min-width: $screen-sm-min) { position: sticky; @@ -711,6 +713,16 @@ padding-right: $gl-padding; } } + + .nav-links { + border: 0; + } +} + +.merge-request-tabs { + display: flex; + margin-bottom: 0; + padding: 0; } .limit-container-width { @@ -721,6 +733,15 @@ } } +.merge-request-tabs-container { + display: flex; + justify-content: space-between; + + @media (max-width: $screen-xs-max) { + flex-direction: column-reverse; + } +} + .limit-container-width:not(.container-limited) { .merge-request-tabs-holder:not(.affix) { .merge-request-tabs-container { diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 0600bb1cb1a..5b6aa9d74f6 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -609,6 +609,15 @@ ul.notes { } .line-resolve-all-container { + @media (min-width: $screen-sm-min) { + margin-right: 0; + padding-left: $gl-padding; + } + + > div { + white-space: nowrap; + } + .btn-group { margin-left: -4px; } |