From 1e74073707dd035db9072595ac5c420a571bd1a9 Mon Sep 17 00:00:00 2001 From: Bryce Johnson Date: Wed, 16 Nov 2016 19:15:50 +0100 Subject: [PATCH 1/9] Subscribe milestone to issuable updates. --- app/assets/javascripts/milestone_select.js | 39 +++++++++++++--------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js index 67796083790a..14d390a434eb 100644 --- a/app/assets/javascripts/milestone_select.js +++ b/app/assets/javascripts/milestone_select.js @@ -32,6 +32,28 @@ milestoneLinkNoneTemplate = 'None'; collapsedSidebarLabelTemplate = _.template(' <%- title %> '); } + + var renderMethod = function(data) { + $dropdown.trigger('loaded.gl.dropdown'); + $loading.fadeOut(); + $selectbox.hide(); + $value.css('display', ''); + if (data.milestone != null) { + data.milestone.namespace = _this.currentProject.namespace; + data.milestone.path = _this.currentProject.path; + data.milestone.remaining = gl.utils.timeFor(data.milestone.due_date); + $value.html(milestoneLinkTemplate(data.milestone)); + return $sidebarCollapsedValue.find('span').html(collapsedSidebarLabelTemplate(data.milestone)); + } else { + $value.html(milestoneLinkNoneTemplate); + return $sidebarCollapsedValue.find('span').text('No'); + } + }; + + if (gl.IssuableResource) { + gl.IssuableResource.subscribe(renderMethod); + } + return $dropdown.glDropdown({ showMenuAbove: showMenuAbove, data: function(term, callback) { @@ -154,22 +176,7 @@ type: 'PUT', url: issueUpdateURL, data: data - }).done(function(data) { - $dropdown.trigger('loaded.gl.dropdown'); - $loading.fadeOut(); - $selectbox.hide(); - $value.css('display', ''); - if (data.milestone != null) { - data.milestone.namespace = _this.currentProject.namespace; - data.milestone.path = _this.currentProject.path; - data.milestone.remaining = gl.utils.timeFor(data.milestone.due_date); - $value.html(milestoneLinkTemplate(data.milestone)); - return $sidebarCollapsedValue.find('span').html(collapsedSidebarLabelTemplate(data.milestone)); - } else { - $value.html(milestoneLinkNoneTemplate); - return $sidebarCollapsedValue.find('span').text('No'); - } - }); + }).done(renderMethod); } } }); -- GitLab From 66e0c7f175a9cb58c9873ba09bdc6632875cb0fc Mon Sep 17 00:00:00 2001 From: Bryce Johnson Date: Wed, 16 Nov 2016 20:02:52 +0100 Subject: [PATCH 2/9] Subscribe DueDateSelect to Issuable resource. --- app/assets/javascripts/due_date_select.js.es6 | 65 +++++++++++-------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/app/assets/javascripts/due_date_select.js.es6 b/app/assets/javascripts/due_date_select.js.es6 index fd7f961aab98..ddc7191ef610 100644 --- a/app/assets/javascripts/due_date_select.js.es6 +++ b/app/assets/javascripts/due_date_select.js.es6 @@ -25,6 +25,7 @@ this.initRemoveDueDate(); this.initDatePicker(); this.initStopPropagation(); + this.subscribeToIssuableUpdates(); } initGlDropdown() { @@ -72,6 +73,12 @@ }); } + subscribeToIssuableUpdates() { + if (gl.IssuableResource) { + gl.IssuableResource.subscribe(this.renderUpdatedDueDate.bind(this)); + } + } + saveDueDate(isDropdown) { this.parseSelectedDate(); this.prepSelectedDate(); @@ -107,39 +114,43 @@ }); } + renderUpdatedDueDate(data) { + const selectedDateValue = data.due_date; + + this.displayedDate = data.due_date !== null ? $.datepicker.formatDate('M d, yy', new Date(selectedDateValue)) : 'No due date'; + + const displayedDateStyle = this.displayedDate !== 'No due date' ? 'bold' : 'no-value'; + + this.$loading.fadeIn(); + + if (this.isDropdown) { + this.$dropdown.trigger('loading.gl.dropdown'); + this.$selectbox.hide(); + } + + this.$value.css('display', ''); + this.$valueContent.html(`${this.displayedDate}`); + this.$sidebarValue.html(this.displayedDate); + + selectedDateValue !== null ? + $('.js-remove-due-date-holder').removeClass('hidden') : + $('.js-remove-due-date-holder').addClass('hidden'); + + if (this.isDropdown) { + this.$dropdown.trigger('loaded.gl.dropdown'); + this.$dropdown.dropdown('toggle'); + } + return this.$loading.fadeOut(); + } + submitSelectedDate(isDropdown) { + this.isDropdown = isDropdown; return $.ajax({ type: 'PUT', url: this.issueUpdateURL, data: this.datePayload, dataType: 'json', - beforeSend: () => { - const selectedDateValue = this.datePayload[this.abilityName].due_date; - const displayedDateStyle = this.displayedDate !== 'No due date' ? 'bold' : 'no-value'; - - this.$loading.fadeIn(); - - if (isDropdown) { - this.$dropdown.trigger('loading.gl.dropdown'); - this.$selectbox.hide(); - } - - this.$value.css('display', ''); - this.$valueContent.html(`${this.displayedDate}`); - this.$sidebarValue.html(this.displayedDate); - - return selectedDateValue.length ? - $('.js-remove-due-date-holder').removeClass('hidden') : - $('.js-remove-due-date-holder').addClass('hidden'); - - } - }).done((data) => { - if (isDropdown) { - this.$dropdown.trigger('loaded.gl.dropdown'); - this.$dropdown.dropdown('toggle'); - } - return this.$loading.fadeOut(); - }); + }).done((data) => this.renderUpdatedDueDate(data)) } } -- GitLab From f0c35b82689c8169e4426901a4cbb16cd4c5f8bf Mon Sep 17 00:00:00 2001 From: Bryce Johnson Date: Wed, 16 Nov 2016 20:03:33 +0100 Subject: [PATCH 3/9] Subscribe label select to IssuableResource. --- app/assets/javascripts/labels_select.js | 105 +++++++++++++----------- 1 file changed, 56 insertions(+), 49 deletions(-) diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js index 812d5cde685b..c2e05b431e13 100644 --- a/app/assets/javascripts/labels_select.js +++ b/app/assets/javascripts/labels_select.js @@ -50,6 +50,61 @@ new gl.CreateLabelDropdown($dropdown.closest('.dropdown').find('.dropdown-new-label'), namespacePath, projectPath); } + + var renderMethod = function(data) { + var labelCount, template, labelTooltipTitle, labelTitles; + $loading.fadeOut(); + $dropdown.trigger('loaded.gl.dropdown'); + $selectbox.hide(); + data.issueURLSplit = issueURLSplit; + labelCount = 0; + if (data.labels.length) { + template = labelHTMLTemplate(data); + labelCount = data.labels.length; + } + else { + template = labelNoneHTMLTemplate; + } + $value.removeAttr('style').html(template); + $sidebarCollapsedValue.text(labelCount); + + if (data.labels.length) { + labelTitles = data.labels.map(function(label) { + return label.title; + }); + + if (labelTitles.length > 5) { + labelTitles = labelTitles.slice(0, 5); + labelTitles.push('and ' + (data.labels.length - 5) + ' more'); + } + + labelTooltipTitle = labelTitles.join(', '); + } + else { + labelTooltipTitle = ''; + $sidebarLabelTooltip.tooltip('destroy'); + } + + $sidebarLabelTooltip + .attr('title', labelTooltipTitle) + .tooltip('fixTitle'); + + $('.has-tooltip', $value).tooltip({ + container: 'body' + }); + return $value.find('a').each(function(i) { + return setTimeout((function(_this) { + return function() { + return gl.animate.animate($(_this), 'pulse'); + }; + })(this), 200 * i); + }); + }; + + if (gl.IssuableResource) { + gl.IssuableResource.subscribe(renderMethod); + } + saveLabelData = function() { var data, selected; selected = $dropdown.closest('.selectbox').find("input[name='" + fieldName + "']").map(function() { @@ -72,55 +127,7 @@ url: issueUpdateURL, dataType: 'JSON', data: data - }).done(function(data) { - var labelCount, template, labelTooltipTitle, labelTitles; - $loading.fadeOut(); - $dropdown.trigger('loaded.gl.dropdown'); - $selectbox.hide(); - data.issueURLSplit = issueURLSplit; - labelCount = 0; - if (data.labels.length) { - template = labelHTMLTemplate(data); - labelCount = data.labels.length; - } - else { - template = labelNoneHTMLTemplate; - } - $value.removeAttr('style').html(template); - $sidebarCollapsedValue.text(labelCount); - - if (data.labels.length) { - labelTitles = data.labels.map(function(label) { - return label.title; - }); - - if (labelTitles.length > 5) { - labelTitles = labelTitles.slice(0, 5); - labelTitles.push('and ' + (data.labels.length - 5) + ' more'); - } - - labelTooltipTitle = labelTitles.join(', '); - } - else { - labelTooltipTitle = ''; - $sidebarLabelTooltip.tooltip('destroy'); - } - - $sidebarLabelTooltip - .attr('title', labelTooltipTitle) - .tooltip('fixTitle'); - - $('.has-tooltip', $value).tooltip({ - container: 'body' - }); - return $value.find('a').each(function(i) { - return setTimeout((function(_this) { - return function() { - return gl.animate.animate($(_this), 'pulse'); - }; - })(this), 200 * i); - }); - }); + }).done(renderMethod); }; return $dropdown.glDropdown({ showMenuAbove: showMenuAbove, -- GitLab From b8c7c5a55dfcff21cfd9d332756b74b02dcb2593 Mon Sep 17 00:00:00 2001 From: Bryce Johnson Date: Wed, 16 Nov 2016 20:11:07 +0100 Subject: [PATCH 4/9] Subscribe assignee select to Issuable resource. --- app/assets/javascripts/users_select.js | 51 +++++++++++++++----------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js index c6e18fad8322..632b438f06f7 100644 --- a/app/assets/javascripts/users_select.js +++ b/app/assets/javascripts/users_select.js @@ -48,6 +48,34 @@ }); }; + var renderMethod = function(data) { + var user; + $dropdown.trigger('loaded.gl.dropdown'); + $loading.fadeOut(); + $selectbox.hide(); + if (data.assignee) { + user = { + name: data.assignee.name, + username: data.assignee.username, + avatar: data.assignee.avatar_url + }; + } else { + user = { + name: 'Unassigned', + username: '', + avatar: '' + }; + } + + $value.html(assigneeTemplate(user)); + $collapsedSidebar.attr('title', user.name).tooltip('fixTitle'); + return $collapsedSidebar.html(collapsedAssigneeTemplate(user)); + }; + + if (gl.IssuableResource) { + gl.IssuableResource.subscribe(renderMethod); + } + $block.on('click', '.js-assign-yourself', function(e) { e.preventDefault(); @@ -76,28 +104,7 @@ dataType: 'json', url: issueURL, data: data - }).done(function(data) { - var user; - $dropdown.trigger('loaded.gl.dropdown'); - $loading.fadeOut(); - $selectbox.hide(); - if (data.assignee) { - user = { - name: data.assignee.name, - username: data.assignee.username, - avatar: data.assignee.avatar_url - }; - } else { - user = { - name: 'Unassigned', - username: '', - avatar: '' - }; - } - $value.html(assigneeTemplate(user)); - $collapsedSidebar.attr('title', user.name).tooltip('fixTitle'); - return $collapsedSidebar.html(collapsedAssigneeTemplate(user)); - }); + }).done(renderMethod); }; collapsedAssigneeTemplate = _.template('<% if( avatar ) { %> <% } else { %> <% } %>'); assigneeTemplate = _.template('<% if (username) { %> <% if( avatar ) { %> <% } %> <%- name %> @<%- username %> <% } else { %> No assignee - assign yourself <% } %>'); -- GitLab From 9ee88679bdf8e49ea1b3e00ae4a2af4f6dec0811 Mon Sep 17 00:00:00 2001 From: Bryce Johnson Date: Fri, 18 Nov 2016 19:07:20 +0100 Subject: [PATCH 5/9] Quick implementation of autoupdate. Lots left to do. --- app/assets/javascripts/labels_select.js | 4 ++-- app/assets/javascripts/subbable_resource.js.es6 | 2 +- app/views/shared/issuable/_sidebar.html.haml | 11 +++++++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js index c2e05b431e13..b9c15e586174 100644 --- a/app/assets/javascripts/labels_select.js +++ b/app/assets/javascripts/labels_select.js @@ -58,7 +58,7 @@ $selectbox.hide(); data.issueURLSplit = issueURLSplit; labelCount = 0; - if (data.labels.length) { + if (data.labels && data.labels.length) { template = labelHTMLTemplate(data); labelCount = data.labels.length; } @@ -68,7 +68,7 @@ $value.removeAttr('style').html(template); $sidebarCollapsedValue.text(labelCount); - if (data.labels.length) { + if (data.labels && data.labels.length) { labelTitles = data.labels.map(function(label) { return label.title; }); diff --git a/app/assets/javascripts/subbable_resource.js.es6 b/app/assets/javascripts/subbable_resource.js.es6 index 932120157a34..531584571070 100644 --- a/app/assets/javascripts/subbable_resource.js.es6 +++ b/app/assets/javascripts/subbable_resource.js.es6 @@ -11,7 +11,7 @@ class SubbableResource { constructor(resourcePath) { this.endpoint = resourcePath; - + debugger; // TODO: Switch to axios.create this.resource = $.ajax; this.subscribers = []; diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index f166fac105dc..4235b4dd6a37 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -167,6 +167,17 @@ = clipboard_button(clipboard_text: project_ref) :javascript + gl.IssuableResource = new gl.SubbableResource('#{issuable_json_path(issuable)}'); + function fetcher() { + return gl.IssuableResource.get.call(gl.IssuableResource, { type: 'GET', url: gl.IssuableResource.endpoint }); + } + new gl.SmartInterval({ + startingInterval: 5000, + maxInterval: 5000, + increaseByFactorOf: 2, + lazyStart: false, + callback: fetcher + }); new MilestoneSelect('{"namespace":"#{@project.namespace.path}","path":"#{@project.path}"}'); new LabelsSelect(); new IssuableContext('#{escape_javascript(current_user.to_json(only: [:username, :id, :name]))}'); -- GitLab From ecc78c4db7249f7a53f79e64d83c17957390d65c Mon Sep 17 00:00:00 2001 From: Bryce Johnson Date: Tue, 22 Nov 2016 23:39:31 +0100 Subject: [PATCH 6/9] Stub out issuable sidebar components. --- .../assignee_select_bundle.js.es6 | 3 + .../components/assignee_select.js.es6 | 64 +++++++++ .../components/due_date_select.js.es6 | 64 +++++++++ .../due_date_select_bundle.js.es6 | 3 + .../issuable/issuable_bundle.js.es6 | 33 +++++ .../components/labels_select.js.es6 | 61 +++++++++ .../labels_select/labels_select_bundle.js.es6 | 0 .../components/milestone_select.js.es6 | 56 ++++++++ .../milestone_select_bundle.js.es6 | 2 + .../components/collapsed_state.js.es6 | 35 +++++ .../components/content_panes.js.es6 | 81 ++++++++++++ .../components/help_state.js.es6 | 27 ++++ .../components/time_tracker.js.es6 | 103 +++++++++++++++ .../time_tracking/time_tracking_bundle.js.es6 | 60 +++++++++ .../javascripts/lib/utils/datetime_utility.js | 4 + .../javascripts/subbable_resource.js.es6 | 1 - .../shared/issuable/_participants.html.haml | 4 +- app/views/shared/issuable/_sidebar.html.haml | 121 +++--------------- config/application.rb | 1 + 19 files changed, 614 insertions(+), 109 deletions(-) create mode 100644 app/assets/javascripts/issuable/assignee_select/assignee_select_bundle.js.es6 create mode 100644 app/assets/javascripts/issuable/assignee_select/components/assignee_select.js.es6 create mode 100644 app/assets/javascripts/issuable/due_date_select/components/due_date_select.js.es6 create mode 100644 app/assets/javascripts/issuable/due_date_select/due_date_select_bundle.js.es6 create mode 100644 app/assets/javascripts/issuable/issuable_bundle.js.es6 create mode 100644 app/assets/javascripts/issuable/labels_select/components/labels_select.js.es6 create mode 100644 app/assets/javascripts/issuable/labels_select/labels_select_bundle.js.es6 create mode 100644 app/assets/javascripts/issuable/milestone_select/components/milestone_select.js.es6 create mode 100644 app/assets/javascripts/issuable/milestone_select/milestone_select_bundle.js.es6 create mode 100644 app/assets/javascripts/issuable/time_tracking/components/collapsed_state.js.es6 create mode 100644 app/assets/javascripts/issuable/time_tracking/components/content_panes.js.es6 create mode 100644 app/assets/javascripts/issuable/time_tracking/components/help_state.js.es6 create mode 100644 app/assets/javascripts/issuable/time_tracking/components/time_tracker.js.es6 create mode 100644 app/assets/javascripts/issuable/time_tracking/time_tracking_bundle.js.es6 diff --git a/app/assets/javascripts/issuable/assignee_select/assignee_select_bundle.js.es6 b/app/assets/javascripts/issuable/assignee_select/assignee_select_bundle.js.es6 new file mode 100644 index 000000000000..4a1ac4db0388 --- /dev/null +++ b/app/assets/javascripts/issuable/assignee_select/assignee_select_bundle.js.es6 @@ -0,0 +1,3 @@ +//= require vue +//= require_directory ./components + diff --git a/app/assets/javascripts/issuable/assignee_select/components/assignee_select.js.es6 b/app/assets/javascripts/issuable/assignee_select/components/assignee_select.js.es6 new file mode 100644 index 000000000000..5a9fc79811cb --- /dev/null +++ b/app/assets/javascripts/issuable/assignee_select/components/assignee_select.js.es6 @@ -0,0 +1,64 @@ +((gl) => { + Vue.component('issuable-due-date-select', { + props: ['admin', 'due_date'], + computed: { + hasDueDate() { + return !!this.due_date; + }, + dueDateHuman() { + return gl.utils.formatDateNoTime(this.due_date); + } + }, + methods: { + removeDueDate() { + console.log("Removed Due Date") + }, + editDueDate() { + console.log("Edit due date"); + } + }, + mounted() { + console.log("Init GL Dropdown or Droplab"); + }, + template: ` +
+ +
+ Due date + Edit +
+
+ {{ dueDateHuman }} + No due date + remove due date +
+
+ Dropdown Select (INSERT DROPLAB) +
+
+ ` + }); + + /** + * + * .block.due_date + - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) + .selectbox.hide-collapsed + = f.hidden_field :due_date, value: issuable.due_date + .dropdown + %button.dropdown-menu-toggle.js-due-date-select{ type: 'button', data: { toggle: 'dropdown', + field_name: "#{issuable.to_ability_name}[due_date]", ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable) } } + %span.dropdown-toggle-text Due date + = icon('chevron-down') + .dropdown-menu.dropdown-menu-due-date + = dropdown_title('Due date') + = dropdown_content do + .js-due-date-calendar + + */ + +})(window.gl || (window.gl = {})); diff --git a/app/assets/javascripts/issuable/due_date_select/components/due_date_select.js.es6 b/app/assets/javascripts/issuable/due_date_select/components/due_date_select.js.es6 new file mode 100644 index 000000000000..5a9fc79811cb --- /dev/null +++ b/app/assets/javascripts/issuable/due_date_select/components/due_date_select.js.es6 @@ -0,0 +1,64 @@ +((gl) => { + Vue.component('issuable-due-date-select', { + props: ['admin', 'due_date'], + computed: { + hasDueDate() { + return !!this.due_date; + }, + dueDateHuman() { + return gl.utils.formatDateNoTime(this.due_date); + } + }, + methods: { + removeDueDate() { + console.log("Removed Due Date") + }, + editDueDate() { + console.log("Edit due date"); + } + }, + mounted() { + console.log("Init GL Dropdown or Droplab"); + }, + template: ` +
+ +
+ Due date + Edit +
+
+ {{ dueDateHuman }} + No due date + remove due date +
+
+ Dropdown Select (INSERT DROPLAB) +
+
+ ` + }); + + /** + * + * .block.due_date + - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) + .selectbox.hide-collapsed + = f.hidden_field :due_date, value: issuable.due_date + .dropdown + %button.dropdown-menu-toggle.js-due-date-select{ type: 'button', data: { toggle: 'dropdown', + field_name: "#{issuable.to_ability_name}[due_date]", ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable) } } + %span.dropdown-toggle-text Due date + = icon('chevron-down') + .dropdown-menu.dropdown-menu-due-date + = dropdown_title('Due date') + = dropdown_content do + .js-due-date-calendar + + */ + +})(window.gl || (window.gl = {})); diff --git a/app/assets/javascripts/issuable/due_date_select/due_date_select_bundle.js.es6 b/app/assets/javascripts/issuable/due_date_select/due_date_select_bundle.js.es6 new file mode 100644 index 000000000000..4a1ac4db0388 --- /dev/null +++ b/app/assets/javascripts/issuable/due_date_select/due_date_select_bundle.js.es6 @@ -0,0 +1,3 @@ +//= require vue +//= require_directory ./components + diff --git a/app/assets/javascripts/issuable/issuable_bundle.js.es6 b/app/assets/javascripts/issuable/issuable_bundle.js.es6 new file mode 100644 index 000000000000..d6ded836017d --- /dev/null +++ b/app/assets/javascripts/issuable/issuable_bundle.js.es6 @@ -0,0 +1,33 @@ +//= require ./due_date_select/due_date_select_bundle +((gl) => { + $(() => { + const $issuableSidebar = document.getElementById('issuable-sidebar-app'); + const issuableData = JSON.parse($issuableSidebar.dataset['issuable']); + const isAdminData = Boolean($issuableSidebar.dataset['admin']); + const issuableEndpoint = ''; + + new Vue({ + el: $issuableSidebar, + data: { + issuable: issuableData, + admin: isAdminData, + endpoint: issuableEndpoint + }, + mounted() { + console.log('PARENT VUE INSTANCE: ', this); + + gl.IssuableResource = new gl.SubbableResource(this.endpoint); + gl.IssuableResource.subscribe((data)=> { + this.issuable = data; + }); + new gl.SmartInterval({ + startingInterval: 5000, + maxInterval: 5000, + increaseByFactorOf: 2, + lazyStart: false, + callback: gl.IssuableResource.get.bind(gl.IssuableResource) + }); + } + }); + }); +})(window.gl || (window.gl = {})); diff --git a/app/assets/javascripts/issuable/labels_select/components/labels_select.js.es6 b/app/assets/javascripts/issuable/labels_select/components/labels_select.js.es6 new file mode 100644 index 000000000000..efcbcea9551d --- /dev/null +++ b/app/assets/javascripts/issuable/labels_select/components/labels_select.js.es6 @@ -0,0 +1,61 @@ +((gl) => { + Vue.component('issuable-labels-select', { + props: ['admin', 'labels'], + computed: { + hasDueDate() { + return !!this.labels; + }, + dueDateHuman() { + return gl.utils.formatDateNoTime(this.labels); + } + }, + methods: { + removeLabel() { + }, + createLabel() { + } + }, + mounted() { + console.log("Init GL Dropdown or Droplab"); + }, + template: ` + + ` + }); + + /** + * + * - selected_labels = issuable.labels + .block.labels + .sidebar-collapsed-icon.js-sidebar-labels-tooltip{ title: issuable_labels_tooltip(issuable.labels_array), data: { placement: "left", container: "body" } } + = icon('tags') + %span + = selected_labels.size + .title.hide-collapsed + Labels + = icon('spinner spin', class: 'block-loading') + - if can_edit_issuable + = link_to 'Edit', '#', class: 'edit-link pull-right' + .value.issuable-show-labels.hide-collapsed{ class: ("has-labels" if selected_labels.any?) } + - if selected_labels.any? + - selected_labels.each do |label| + = link_to_label(label, type: issuable.to_ability_name) + - else + %span.no-value None + .selectbox.hide-collapsed + - selected_labels.each do |label| + = hidden_field_tag "#{issuable.to_ability_name}[label_names][]", label.id, id: nil + .dropdown + %button.dropdown-menu-toggle.js-label-select.js-multiselect.js-label-sidebar-dropdown{type: "button", data: {toggle: "dropdown", default_label: "Labels", field_name: "#{issuable.to_ability_name}[label_names][]", ability_name: issuable.to_ability_name, show_no: "true", show_any: "true", namespace_path: @project.try(:namespace).try(:path), project_path: @project.try(:path), issue_update: issuable_json_path(issuable), labels: (namespace_project_labels_path(@project.namespace, @project, :json) if @project)}} + %span.dropdown-toggle-text{ class: ("is-default" if selected_labels.empty?)} + = multi_label_name(selected_labels, "Labels") + = icon('chevron-down') + .dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable + = render partial: "shared/issuable/label_page_default" + - if can? current_user, :admin_label, @project and @project + = render partial: "shared/issuable/label_page_create" + * + * + */ + +})(window.gl || (window.gl = {})); diff --git a/app/assets/javascripts/issuable/labels_select/labels_select_bundle.js.es6 b/app/assets/javascripts/issuable/labels_select/labels_select_bundle.js.es6 new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/app/assets/javascripts/issuable/milestone_select/components/milestone_select.js.es6 b/app/assets/javascripts/issuable/milestone_select/components/milestone_select.js.es6 new file mode 100644 index 000000000000..0f4ff0311dc3 --- /dev/null +++ b/app/assets/javascripts/issuable/milestone_select/components/milestone_select.js.es6 @@ -0,0 +1,56 @@ +((gl) => { + Vue.component('issuable-milestone-select', { + props: ['admin', 'milestone'], + computed: { + hasMilestone() { + return !!this.milestone; + } + }, + methods: { + removeMilestone() { + console.log("Removed Due Date") + }, + editMilestone() { + console.log("Edit due date"); + } + }, + mounted() { + console.log("Init GL Dropdown or Droplab"); + }, + template: ` + + ` + }); + + /** + * + .block.milestone + .sidebar-collapsed-icon + = icon('clock-o') + %span + - if issuable.milestone + %span.has-tooltip{title: milestone_remaining_days(issuable.milestone), data: {container: 'body', html: 1, placement: 'left'}} + = issuable.milestone.title + - else + None + .title.hide-collapsed + Milestone + = icon('spinner spin', class: 'block-loading') + - if can_edit_issuable + = link_to 'Edit', '#', class: 'edit-link pull-right' + .value.hide-collapsed + - if issuable.milestone + = link_to issuable.milestone.title, namespace_project_milestone_path(@project.namespace, @project, issuable.milestone), class: "bold has-tooltip", title: milestone_remaining_days(issuable.milestone), data: { container: "body", html: 1 } + - else + %span.no-value None + + .selectbox.hide-collapsed + = f.hidden_field 'milestone_id', value: issuable.milestone_id, id: nil + = dropdown_tag('Milestone', options: { title: 'Assign milestone', toggle_class: 'js-milestone-select js-extra-options', filter: true, dropdown_class: 'dropdown-menu-selectable', placeholder: 'Search milestones', data: { show_no: true, field_name: "#{issuable.to_ability_name}[milestone_id]", project_id: @project.id, issuable_id: issuable.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable), use_id: true }}) + + + + + */ + +})(window.gl || (window.gl = {})); diff --git a/app/assets/javascripts/issuable/milestone_select/milestone_select_bundle.js.es6 b/app/assets/javascripts/issuable/milestone_select/milestone_select_bundle.js.es6 new file mode 100644 index 000000000000..cebf50a11468 --- /dev/null +++ b/app/assets/javascripts/issuable/milestone_select/milestone_select_bundle.js.es6 @@ -0,0 +1,2 @@ +//= require vue +//= require_directory ./components \ No newline at end of file diff --git a/app/assets/javascripts/issuable/time_tracking/components/collapsed_state.js.es6 b/app/assets/javascripts/issuable/time_tracking/components/collapsed_state.js.es6 new file mode 100644 index 000000000000..076f2b370a4f --- /dev/null +++ b/app/assets/javascripts/issuable/time_tracking/components/collapsed_state.js.es6 @@ -0,0 +1,35 @@ +//= require lib/utils/pretty_time + +((gl) => { + const PrettyTime = gl.PrettyTime; + + Vue.component('time-tracking-collapsed-state', { + name: 'time-tracking-collapsed-state', + props: ['showComparisonState', 'showSpentOnlyState', 'showEstimateOnlyState', 'showNoTimeTrackingState', 'timeSpentHuman', 'timeEstimateHuman', 'stopwatchSvg'], + methods: { + abbreviateTime(timeStr) { + return PrettyTime.abbreviateTime(timeStr); + }, + }, + template: ` + + `, + }); +})(window.gl || (window.gl = {})); + diff --git a/app/assets/javascripts/issuable/time_tracking/components/content_panes.js.es6 b/app/assets/javascripts/issuable/time_tracking/components/content_panes.js.es6 new file mode 100644 index 000000000000..a74baf6c33df --- /dev/null +++ b/app/assets/javascripts/issuable/time_tracking/components/content_panes.js.es6 @@ -0,0 +1,81 @@ +//= require lib/utils/pretty_time + +((gl) => { + const PrettyTime = gl.PrettyTime; + + Vue.component('time-tracking-no-tracking-pane', { + name: 'time-tracking-no-tracking-pane', + template: '
No estimate or time spent
', + }); + + Vue.component('time-tracking-estimate-only-pane', { + name: 'time-tracking-estimate-only-pane', + props: ['timeEstimateHuman'], + template: '
Estimated: {{ timeEstimateHuman }}
', + }); + + Vue.component('time-tracking-spent-only-pane', { + name: 'time-tracking-spent-only-pane', + props: ['timeSpentHuman'], + template: '
Spent: {{ timeSpentHuman }}
', + }); + + Vue.component('time-tracking-comparison-pane', { + name: 'time-tracking-comparison-pane', + props: ['timeSpent', 'timeEstimate', 'timeSpentHuman', 'timeEstimateHuman'], + computed: { + parsedRemaining() { + const diffSeconds = this.timeEstimate - this.timeSpent; + return PrettyTime.parseSeconds(diffSeconds); + }, + timeRemainingHuman() { + return PrettyTime.stringifyTime(this.parsedRemaining); + }, + timeRemainingTooltip() { + const prefix = this.timeRemainingMinutes < 0 ? 'Over by' : 'Time remaining:'; + return `${prefix} ${this.timeRemainingHuman}`; + }, + + /* Diff values for comparison meter */ + timeRemainingMinutes() { + return this.timeEstimate - this.timeSpent; + }, + timeRemainingPercent() { + return `${Math.floor(((this.timeSpent / this.timeEstimate) * 100))}%`; + }, + timeRemainingStatusClass() { + return this.timeEstimate >= this.timeSpent ? 'within_estimate' : 'over_estimate'; + }, + /* Parsed time values */ + parsedEstimate() { + return PrettyTime.parseSeconds(this.timeEstimate); + }, + parsedSpent() { + return PrettyTime.parseSeconds(this.timeSpent); + }, + }, + template: ` +
+
+
+
+
+
+
+ Spent + {{ timeSpentHuman }} +
+
+ Est + {{ timeEstimateHuman }} +
+
+
+
+ `, + }); +})(window.gl || (window.gl = {})); diff --git a/app/assets/javascripts/issuable/time_tracking/components/help_state.js.es6 b/app/assets/javascripts/issuable/time_tracking/components/help_state.js.es6 new file mode 100644 index 000000000000..759bfefa42a8 --- /dev/null +++ b/app/assets/javascripts/issuable/time_tracking/components/help_state.js.es6 @@ -0,0 +1,27 @@ +(() => { + Vue.component('time-tracking-help-state', { + name: 'time-tracking-help-state', + data() { + return { + docsUrl: '/help/workflow/time_tracking.md', + }; + }, + template: ` +
+
+

Track time with slash commands

+

Slash commands can be used in the issues description and comment boxes.

+

+ /estimate + will update the estimated time with the latest command. +

+

+ /spend + will update the sum of the time spent. +

+ Learn more +
+
+ `, + }); +})(); diff --git a/app/assets/javascripts/issuable/time_tracking/components/time_tracker.js.es6 b/app/assets/javascripts/issuable/time_tracking/components/time_tracker.js.es6 new file mode 100644 index 000000000000..306e1167de53 --- /dev/null +++ b/app/assets/javascripts/issuable/time_tracking/components/time_tracker.js.es6 @@ -0,0 +1,103 @@ +//= require vue +(() => { + gl.IssuableTimeTracker = Vue.component('issuable-time-tracker', { + name: 'issuable-time-tracker', + props: ['time_estimate', 'time_spent', 'human_time_estimate', 'human_time_spent', 'stopwatchSvg'], + data() { + return { + showHelp: false, + }; + }, + computed: { + timeSpent() { + return this.time_spent; + }, + timeEstimate() { + return this.time_estimate; + }, + timeEstimateHuman() { + return this.human_time_estimate; + }, + timeSpentHuman() { + return this.human_time_spent; + }, + hasTimeSpent() { + return !!this.timeSpent; + }, + hasTimeEstimate() { + return !!this.timeEstimate; + }, + showComparisonState() { + return this.hasTimeEstimate && this.hasTimeSpent; + }, + showEstimateOnlyState() { + return this.hasTimeEstimate && !this.hasTimeSpent; + }, + showSpentOnlyState() { + return this.hasTimeSpent && !this.hasTimeEstimate; + }, + showNoTimeTrackingState() { + return !this.hasTimeEstimate && !this.hasTimeSpent; + }, + showHelpState() { + return !!this.showHelp; + }, + }, + methods: { + toggleHelpState(show) { + this.showHelp = show; + }, + }, + template: ` +
+ + +
+ Time tracking +
+ +
+
+ +
+
+
+ + + + + + + + + + + + +
+
+ `, + }); +})(); diff --git a/app/assets/javascripts/issuable/time_tracking/time_tracking_bundle.js.es6 b/app/assets/javascripts/issuable/time_tracking/time_tracking_bundle.js.es6 new file mode 100644 index 000000000000..22285db33282 --- /dev/null +++ b/app/assets/javascripts/issuable/time_tracking/time_tracking_bundle.js.es6 @@ -0,0 +1,60 @@ +//= require vue +//= require_directory ./components +//= require smart_interval +//= require subbable_resource + +(() => { + /* This Vue instance represents what will become the parent instance for the + * sidebar. It will be responsible for managing `issuable` state and propagating + * changes to sidebar components. + */ + + class IssuableTimeTracking { + constructor(issuableJSON) { + const parsedIssuable = JSON.parse(issuableJSON); + return this.initComponent(parsedIssuable); + } + + initComponent(parsedIssuable) { + this.parentInstance = new Vue({ + el: '#issuable-time-tracker', + data: { + issuable: parsedIssuable, + }, + methods: { + fetchIssuable() { + return gl.IssuableResource.get.call(gl.IssuableResource, { + type: 'GET', + url: gl.IssuableResource.endpoint, + }); + }, + updateState(data) { + this.issuable = data; + }, + subscribeToUpdates() { + gl.IssuableResource.subscribe(data => this.updateState(data)); + }, + listenForSlashCommands() { + $(document).on('ajax:success', '.gfm-form', (e, data) => { + const subscribedCommands = ['spend_time', 'time_estimate']; + const changedCommands = data.commands_changes; + + if (changedCommands && _.intersection(subscribedCommands, changedCommands).length) { + this.fetchIssuable(); + } + }); + }, + }, + created() { + this.fetchIssuable(); + }, + mounted() { + this.subscribeToUpdates(); + this.listenForSlashCommands(); + }, + }); + } + } + + gl.IssuableTimeTracking = IssuableTimeTracking; +})(window.gl || (window.gl = {})); diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js index d480fdc882bf..12ffdb609493 100644 --- a/app/assets/javascripts/lib/utils/datetime_utility.js +++ b/app/assets/javascripts/lib/utils/datetime_utility.js @@ -14,6 +14,10 @@ return dateFormat(datetime, 'mmm d, yyyy h:MMtt Z'); }; + w.gl.utils.formatDateNoTime = function(datetime) { + return dateFormat(datetime, 'mmm d, yyyy'); + }; + w.gl.utils.getDayName = function(date) { return this.days[date.getDay()]; }; diff --git a/app/assets/javascripts/subbable_resource.js.es6 b/app/assets/javascripts/subbable_resource.js.es6 index 531584571070..abdf3314975a 100644 --- a/app/assets/javascripts/subbable_resource.js.es6 +++ b/app/assets/javascripts/subbable_resource.js.es6 @@ -11,7 +11,6 @@ class SubbableResource { constructor(resourcePath) { this.endpoint = resourcePath; - debugger; // TODO: Switch to axios.create this.resource = $.ajax; this.subscribers = []; diff --git a/app/views/shared/issuable/_participants.html.haml b/app/views/shared/issuable/_participants.html.haml index 33a9a4948579..e6da8d20a6d8 100644 --- a/app/views/shared/issuable/_participants.html.haml +++ b/app/views/shared/issuable/_participants.html.haml @@ -16,5 +16,5 @@ %div.participants-more %a.js-participants-more{href: "#", data: {original_text: "+ #{participants_size - 7} more", less_text: "- show less"}} + #{participants_extra} more -:javascript - IssuableContext.prototype.PARTICIPANTS_ROW_COUNT = #{participants_row}; +// :javascript +// IssuableContext.prototype.PARTICIPANTS_ROW_COUNT = #{participants_row}; diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 4235b4dd6a37..ac7cd42d590d 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -1,6 +1,9 @@ - todo = issuable_todo(issuable) +- content_for :page_specific_javascripts do + = page_specific_javascript_tag('issuable/issuable_bundle.js') +- can_edit_issuable = can?(current_user, :"admin_#{issuable.to_ability_name}", @project) %aside.right-sidebar{ class: sidebar_gutter_collapsed_class } - .issuable-sidebar + #issuable-sidebar-app.issuable-sidebar{ 'data-issuable' => issuable.to_json, 'data-admin' => can_edit_issuable.to_s } - can_edit_issuable = can?(current_user, :"admin_#{issuable.to_ability_name}", @project) .block.issuable-sidebar-header - if current_user @@ -48,95 +51,12 @@ .selectbox.hide-collapsed = f.hidden_field 'assignee_id', value: issuable.assignee_id, id: 'issue_assignee_id' = dropdown_tag('Select assignee', options: { toggle_class: 'js-user-search js-author-search', title: 'Assign to', filter: true, dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author', placeholder: 'Search users', data: { first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), author_id: issuable.author_id, field_name: "#{issuable.to_ability_name}[assignee_id]", issue_update: issuable_json_path(issuable), ability_name: issuable.to_ability_name, null_user: true } }) - - .block.milestone - .sidebar-collapsed-icon - = icon('clock-o') - %span - - if issuable.milestone - %span.has-tooltip{title: milestone_remaining_days(issuable.milestone), data: {container: 'body', html: 1, placement: 'left'}} - = issuable.milestone.title - - else - None - .title.hide-collapsed - Milestone - = icon('spinner spin', class: 'block-loading') - - if can_edit_issuable - = link_to 'Edit', '#', class: 'edit-link pull-right' - .value.hide-collapsed - - if issuable.milestone - = link_to issuable.milestone.title, namespace_project_milestone_path(@project.namespace, @project, issuable.milestone), class: "bold has-tooltip", title: milestone_remaining_days(issuable.milestone), data: { container: "body", html: 1 } - - else - %span.no-value None - - .selectbox.hide-collapsed - = f.hidden_field 'milestone_id', value: issuable.milestone_id, id: nil - = dropdown_tag('Milestone', options: { title: 'Assign milestone', toggle_class: 'js-milestone-select js-extra-options', filter: true, dropdown_class: 'dropdown-menu-selectable', placeholder: 'Search milestones', data: { show_no: true, field_name: "#{issuable.to_ability_name}[milestone_id]", project_id: @project.id, issuable_id: issuable.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable), use_id: true }}) - + + %issuable-milestone-select{':milestone' => 'issuable.milestone', ':admin' => 'admin'} - if issuable.has_attribute?(:due_date) - .block.due_date - .sidebar-collapsed-icon - = icon('calendar') - %span.js-due-date-sidebar-value - = issuable.due_date.try(:to_s, :medium) || 'None' - .title.hide-collapsed - Due date - = icon('spinner spin', class: 'block-loading') - - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) - = link_to 'Edit', '#', class: 'edit-link pull-right' - .value.hide-collapsed - %span.value-content - - if issuable.due_date - %span.bold= issuable.due_date.to_s(:medium) - - else - %span.no-value No due date - - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) - %span.no-value.js-remove-due-date-holder{ class: ("hidden" if issuable.due_date.nil?) } - \- - %a.js-remove-due-date{ href: "#", role: "button" } - remove due date - - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) - .selectbox.hide-collapsed - = f.hidden_field :due_date, value: issuable.due_date - .dropdown - %button.dropdown-menu-toggle.js-due-date-select{ type: 'button', data: { toggle: 'dropdown', field_name: "#{issuable.to_ability_name}[due_date]", ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable) } } - %span.dropdown-toggle-text Due date - = icon('chevron-down') - .dropdown-menu.dropdown-menu-due-date - = dropdown_title('Due date') - = dropdown_content do - .js-due-date-calendar - + %issuable-due-date-select{':due_date' => 'issuable.due_date', ':admin' => 'admin'} - if @labels && @labels.any? - - selected_labels = issuable.labels - .block.labels - .sidebar-collapsed-icon.js-sidebar-labels-tooltip{ title: issuable_labels_tooltip(issuable.labels_array), data: { placement: "left", container: "body" } } - = icon('tags') - %span - = selected_labels.size - .title.hide-collapsed - Labels - = icon('spinner spin', class: 'block-loading') - - if can_edit_issuable - = link_to 'Edit', '#', class: 'edit-link pull-right' - .value.issuable-show-labels.hide-collapsed{ class: ("has-labels" if selected_labels.any?) } - - if selected_labels.any? - - selected_labels.each do |label| - = link_to_label(label, type: issuable.to_ability_name) - - else - %span.no-value None - .selectbox.hide-collapsed - - selected_labels.each do |label| - = hidden_field_tag "#{issuable.to_ability_name}[label_names][]", label.id, id: nil - .dropdown - %button.dropdown-menu-toggle.js-label-select.js-multiselect.js-label-sidebar-dropdown{type: "button", data: {toggle: "dropdown", default_label: "Labels", field_name: "#{issuable.to_ability_name}[label_names][]", ability_name: issuable.to_ability_name, show_no: "true", show_any: "true", namespace_path: @project.try(:namespace).try(:path), project_path: @project.try(:path), issue_update: issuable_json_path(issuable), labels: (namespace_project_labels_path(@project.namespace, @project, :json) if @project)}} - %span.dropdown-toggle-text{ class: ("is-default" if selected_labels.empty?)} - = multi_label_name(selected_labels, "Labels") - = icon('chevron-down') - .dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable - = render partial: "shared/issuable/label_page_default" - - if can? current_user, :admin_label, @project and @project - = render partial: "shared/issuable/label_page_create" + %issuable-labels-select{':labels' => 'issuable.labels', 'admin' => 'admin' } = render "shared/issuable/participants", participants: issuable.participants(current_user) - if current_user @@ -166,21 +86,10 @@ = project_ref = clipboard_button(clipboard_text: project_ref) - :javascript - gl.IssuableResource = new gl.SubbableResource('#{issuable_json_path(issuable)}'); - function fetcher() { - return gl.IssuableResource.get.call(gl.IssuableResource, { type: 'GET', url: gl.IssuableResource.endpoint }); - } - new gl.SmartInterval({ - startingInterval: 5000, - maxInterval: 5000, - increaseByFactorOf: 2, - lazyStart: false, - callback: fetcher - }); - new MilestoneSelect('{"namespace":"#{@project.namespace.path}","path":"#{@project.path}"}'); - new LabelsSelect(); - new IssuableContext('#{escape_javascript(current_user.to_json(only: [:username, :id, :name]))}'); - new Subscription('.subscription') - new gl.DueDateSelectors(); - sidebar = new Sidebar(); + :javascript + new MilestoneSelect('{"namespace":"#{@project.namespace.path}","path":"#{@project.path}"}'); + new LabelsSelect(); + new IssuableContext('#{escape_javascript(current_user.to_json(only: [:username, :id, :name]))}'); + new Subscription('.subscription') + new gl.DueDateSelectors(); + sidebar = new Sidebar(); diff --git a/config/application.rb b/config/application.rb index 946b632b0e80..318018bc374a 100644 --- a/config/application.rb +++ b/config/application.rb @@ -90,6 +90,7 @@ class Application < Rails::Application config.assets.precompile << "profile/profile_bundle.js" config.assets.precompile << "protected_branches/protected_branches_bundle.js" config.assets.precompile << "diff_notes/diff_notes_bundle.js" + config.assets.precompile << "issuable/issuable_bundle.js" config.assets.precompile << "boards/boards_bundle.js" config.assets.precompile << "cycle_analytics/cycle_analytics_bundle.js" config.assets.precompile << "merge_conflicts/merge_conflicts_bundle.js" -- GitLab From 7fe7bd98c6abea848c8a510f532a2594713419ca Mon Sep 17 00:00:00 2001 From: Bryce Johnson Date: Wed, 23 Nov 2016 01:14:33 +0100 Subject: [PATCH 7/9] Further break out issuable sidebar into components. --- .../assignee_select_bundle.js.es6 | 32 +++++++ .../labels_select/labels_select_bundle.js.es6 | 0 .../partcipants_display_bundle.js.es6 | 1 + .../project_refs/project_refs_bundle.js.es6 | 10 ++ .../subscription_toggle.js.es6 | 15 +++ .../components/labels_select.js.es6 | 0 .../todo_toggle/todo_toggle_bundle.js.es6 | 14 +++ app/views/shared/issuable/_sidebar.html.haml | 95 +++---------------- 8 files changed, 85 insertions(+), 82 deletions(-) delete mode 100644 app/assets/javascripts/issuable/labels_select/labels_select_bundle.js.es6 create mode 100644 app/assets/javascripts/issuable/participants_display/partcipants_display_bundle.js.es6 create mode 100644 app/assets/javascripts/issuable/project_refs/project_refs_bundle.js.es6 create mode 100644 app/assets/javascripts/issuable/subscription_toggle/subscription_toggle.js.es6 rename app/assets/javascripts/issuable/{labels_select => todo_toggle}/components/labels_select.js.es6 (100%) create mode 100644 app/assets/javascripts/issuable/todo_toggle/todo_toggle_bundle.js.es6 diff --git a/app/assets/javascripts/issuable/assignee_select/assignee_select_bundle.js.es6 b/app/assets/javascripts/issuable/assignee_select/assignee_select_bundle.js.es6 index 4a1ac4db0388..dc5d38ca4fa5 100644 --- a/app/assets/javascripts/issuable/assignee_select/assignee_select_bundle.js.es6 +++ b/app/assets/javascripts/issuable/assignee_select/assignee_select_bundle.js.es6 @@ -1,3 +1,35 @@ //= require vue //= require_directory ./components +/* + .block.assignee + .sidebar-collapsed-icon.sidebar-collapsed-user{data: {toggle: "tooltip", placement: "left", container: "body"}, title: (issuable.assignee.name if issuable.assignee)} + - if issuable.assignee + = link_to_member(@project, issuable.assignee, size: 24) + - else + = icon('user') + .title.hide-collapsed + Assignee + = icon('spinner spin', class: 'block-loading') + - if can_edit_issuable + = link_to 'Edit', '#', class: 'edit-link pull-right' + .value.hide-collapsed + - if issuable.assignee + = link_to_member(@project, issuable.assignee, size: 32, extra_class: 'bold') do + - if issuable.instance_of?(MergeRequest) && !issuable.can_be_merged_by?(issuable.assignee) + %span.pull-right.cannot-be-merged{ data: { toggle: 'tooltip', placement: 'left' }, title: 'Not allowed to merge' } + = icon('exclamation-triangle') + %span.username + = issuable.assignee.to_reference + - else + %span.assign-yourself.no-value + No assignee + - if can_edit_issuable + \- + %a.js-assign-yourself{ href: '#' } + assign yourself + .selectbox.hide-collapsed + = f.hidden_field 'assignee_id', value: issuable.assignee_id, id: 'issue_assignee_id' + = dropdown_tag('Select assignee', options: { toggle_class: 'js-user-search js-author-search', title: 'Assign to', filter: true, dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author', placeholder: 'Search users', data: { first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), author_id: issuable.author_id, field_name: "#{issuable.to_ability_name}[assignee_id]", issue_update: issuable_json_path(issuable), ability_name: issuable.to_ability_name, null_user: true } }) + +*/ diff --git a/app/assets/javascripts/issuable/labels_select/labels_select_bundle.js.es6 b/app/assets/javascripts/issuable/labels_select/labels_select_bundle.js.es6 deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/app/assets/javascripts/issuable/participants_display/partcipants_display_bundle.js.es6 b/app/assets/javascripts/issuable/participants_display/partcipants_display_bundle.js.es6 new file mode 100644 index 000000000000..4bc5e93a735c --- /dev/null +++ b/app/assets/javascripts/issuable/participants_display/partcipants_display_bundle.js.es6 @@ -0,0 +1 @@ + // = render "shared/issuable/participants", participants: issuable.participants(current_user) diff --git a/app/assets/javascripts/issuable/project_refs/project_refs_bundle.js.es6 b/app/assets/javascripts/issuable/project_refs/project_refs_bundle.js.es6 new file mode 100644 index 000000000000..083c33829208 --- /dev/null +++ b/app/assets/javascripts/issuable/project_refs/project_refs_bundle.js.es6 @@ -0,0 +1,10 @@ + // - project_ref = cross_project_reference(@project, issuable) + // .block.project-reference + // .sidebar-collapsed-icon.dont-change-state + // = clipboard_button(clipboard_text: project_ref) + // .cross-project-reference.hide-collapsed + // %span + // Reference: + // %cite{title: project_ref} + // = project_ref + // = clipboard_button(clipboard_text: project_ref) \ No newline at end of file diff --git a/app/assets/javascripts/issuable/subscription_toggle/subscription_toggle.js.es6 b/app/assets/javascripts/issuable/subscription_toggle/subscription_toggle.js.es6 new file mode 100644 index 000000000000..500973e496eb --- /dev/null +++ b/app/assets/javascripts/issuable/subscription_toggle/subscription_toggle.js.es6 @@ -0,0 +1,15 @@ +// - if current_user +// - subscribed = issuable.subscribed?(current_user, @project) +// .block.light.subscription{data: {url: toggle_subscription_path(issuable)}} +// .sidebar-collapsed-icon +// = icon('rss') +// .title.hide-collapsed +// Notifications +// - subscribtion_status = subscribed ? 'subscribed' : 'unsubscribed' +// %button.btn.btn-block.btn-default.js-subscribe-button.issuable-subscribe-button.hide-collapsed{ type: "button" } +// %span= subscribed ? 'Unsubscribe' : 'Subscribe' +// .subscription-status.hide-collapsed{data: {status: subscribtion_status}} +// .unsubscribed{class: ( 'hidden' if subscribed )} +// You're not receiving notifications from this thread. +// .subscribed{class: ( 'hidden' unless subscribed )} +// You're receiving notifications because you're subscribed to this thread. \ No newline at end of file diff --git a/app/assets/javascripts/issuable/labels_select/components/labels_select.js.es6 b/app/assets/javascripts/issuable/todo_toggle/components/labels_select.js.es6 similarity index 100% rename from app/assets/javascripts/issuable/labels_select/components/labels_select.js.es6 rename to app/assets/javascripts/issuable/todo_toggle/components/labels_select.js.es6 diff --git a/app/assets/javascripts/issuable/todo_toggle/todo_toggle_bundle.js.es6 b/app/assets/javascripts/issuable/todo_toggle/todo_toggle_bundle.js.es6 new file mode 100644 index 000000000000..62c34249ba88 --- /dev/null +++ b/app/assets/javascripts/issuable/todo_toggle/todo_toggle_bundle.js.es6 @@ -0,0 +1,14 @@ +// .block.issuable-sidebar-header +// - if current_user +// %span.issuable-header-text.hide-collapsed.pull-left +// Todo +// %a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", aria: { label: "Toggle sidebar" } } +// = sidebar_gutter_toggle_icon +// - if current_user +// %button.btn.btn-default.issuable-header-btn.pull-right.js-issuable-todo{ type: "button", aria: { label: (todo.nil? ? "Add Todo" : "Mark Done") }, data: { todo_text: "Add Todo", mark_text: "Mark Done", issuable_id: issuable.id, issuable_type: issuable.class.name.underscore, url: namespace_project_todos_path(@project.namespace, @project), delete_path: (dashboard_todo_path(todo) if todo) } } +// %span.js-issuable-todo-text +// - if todo +// Mark Done +// - else +// Add Todo +// = icon('spin spinner', class: 'hidden js-issuable-todo-loading') diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index ac7cd42d590d..7f8d5ba98cf4 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -1,90 +1,21 @@ - todo = issuable_todo(issuable) +- can_edit_issuable = can?(current_user, :"admin_#{issuable.to_ability_name}", @project) +- has_due_date = issuable.has_attribute?(:due_date) +- has_labels = @labels && @labels.any? + - content_for :page_specific_javascripts do = page_specific_javascript_tag('issuable/issuable_bundle.js') -- can_edit_issuable = can?(current_user, :"admin_#{issuable.to_ability_name}", @project) + %aside.right-sidebar{ class: sidebar_gutter_collapsed_class } #issuable-sidebar-app.issuable-sidebar{ 'data-issuable' => issuable.to_json, 'data-admin' => can_edit_issuable.to_s } - - can_edit_issuable = can?(current_user, :"admin_#{issuable.to_ability_name}", @project) - .block.issuable-sidebar-header - - if current_user - %span.issuable-header-text.hide-collapsed.pull-left - Todo - %a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", aria: { label: "Toggle sidebar" } } - = sidebar_gutter_toggle_icon - - if current_user - %button.btn.btn-default.issuable-header-btn.pull-right.js-issuable-todo{ type: "button", aria: { label: (todo.nil? ? "Add Todo" : "Mark Done") }, data: { todo_text: "Add Todo", mark_text: "Mark Done", issuable_id: issuable.id, issuable_type: issuable.class.name.underscore, url: namespace_project_todos_path(@project.namespace, @project), delete_path: (dashboard_todo_path(todo) if todo) } } - %span.js-issuable-todo-text - - if todo - Mark Done - - else - Add Todo - = icon('spin spinner', class: 'hidden js-issuable-todo-loading') - - = form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, format: :json, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f| - .block.assignee - .sidebar-collapsed-icon.sidebar-collapsed-user{data: {toggle: "tooltip", placement: "left", container: "body"}, title: (issuable.assignee.name if issuable.assignee)} - - if issuable.assignee - = link_to_member(@project, issuable.assignee, size: 24) - - else - = icon('user') - .title.hide-collapsed - Assignee - = icon('spinner spin', class: 'block-loading') - - if can_edit_issuable - = link_to 'Edit', '#', class: 'edit-link pull-right' - .value.hide-collapsed - - if issuable.assignee - = link_to_member(@project, issuable.assignee, size: 32, extra_class: 'bold') do - - if issuable.instance_of?(MergeRequest) && !issuable.can_be_merged_by?(issuable.assignee) - %span.pull-right.cannot-be-merged{ data: { toggle: 'tooltip', placement: 'left' }, title: 'Not allowed to merge' } - = icon('exclamation-triangle') - %span.username - = issuable.assignee.to_reference - - else - %span.assign-yourself.no-value - No assignee - - if can_edit_issuable - \- - %a.js-assign-yourself{ href: '#' } - assign yourself - - .selectbox.hide-collapsed - = f.hidden_field 'assignee_id', value: issuable.assignee_id, id: 'issue_assignee_id' - = dropdown_tag('Select assignee', options: { toggle_class: 'js-user-search js-author-search', title: 'Assign to', filter: true, dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author', placeholder: 'Search users', data: { first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), author_id: issuable.author_id, field_name: "#{issuable.to_ability_name}[assignee_id]", issue_update: issuable_json_path(issuable), ability_name: issuable.to_ability_name, null_user: true } }) - - %issuable-milestone-select{':milestone' => 'issuable.milestone', ':admin' => 'admin'} - - if issuable.has_attribute?(:due_date) - %issuable-due-date-select{':due_date' => 'issuable.due_date', ':admin' => 'admin'} - - if @labels && @labels.any? - %issuable-labels-select{':labels' => 'issuable.labels', 'admin' => 'admin' } - - = render "shared/issuable/participants", participants: issuable.participants(current_user) - - if current_user - - subscribed = issuable.subscribed?(current_user, @project) - .block.light.subscription{data: {url: toggle_subscription_path(issuable)}} - .sidebar-collapsed-icon - = icon('rss') - .title.hide-collapsed - Notifications - - subscribtion_status = subscribed ? 'subscribed' : 'unsubscribed' - %button.btn.btn-block.btn-default.js-subscribe-button.issuable-subscribe-button.hide-collapsed{ type: "button" } - %span= subscribed ? 'Unsubscribe' : 'Subscribe' - .subscription-status.hide-collapsed{data: {status: subscribtion_status}} - .unsubscribed{class: ( 'hidden' if subscribed )} - You're not receiving notifications from this thread. - .subscribed{class: ( 'hidden' unless subscribed )} - You're receiving notifications because you're subscribed to this thread. - - - project_ref = cross_project_reference(@project, issuable) - .block.project-reference - .sidebar-collapsed-icon.dont-change-state - = clipboard_button(clipboard_text: project_ref) - .cross-project-reference.hide-collapsed - %span - Reference: - %cite{title: project_ref} - = project_ref - = clipboard_button(clipboard_text: project_ref) + %issuable-todo-toggle{':todo' => 'issuable.todo', ':admin' => 'admin'} + %issuable-assignee-select{ ':assignee' => 'issuable.assignee', ':admin' => 'admin'} + %issuable-milestone-select{':milestone' => 'issuable.milestone', ':admin' => 'admin'} + %issuable-due-date-select{':due_date' => 'issuable.due_date', ':admin' => 'admin'} + %issuable-labels-select{':labels' => 'issuable.labels', 'admin' => 'admin' } + %issuable-participants-display{':participants' => 'issuable.participants', 'admin' => 'admin'} + %issuable-subscription-toggle{':subscription' => 'issuable.subscription' } + %issuable-project-refs{':refs' => 'issuable.refs', ':admin' => 'admin'} :javascript new MilestoneSelect('{"namespace":"#{@project.namespace.path}","path":"#{@project.path}"}'); -- GitLab From 3aa3b064fc9e56b27a1a97303fc84bcfe5605f8b Mon Sep 17 00:00:00 2001 From: Bryce Johnson Date: Wed, 23 Nov 2016 11:50:02 +0100 Subject: [PATCH 8/9] Revert sidebar view to pristine state. --- app/views/shared/issuable/_sidebar.html.haml | 206 ++++++++++++++++--- 1 file changed, 183 insertions(+), 23 deletions(-) diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 7f8d5ba98cf4..4235b4dd6a37 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -1,26 +1,186 @@ - todo = issuable_todo(issuable) -- can_edit_issuable = can?(current_user, :"admin_#{issuable.to_ability_name}", @project) -- has_due_date = issuable.has_attribute?(:due_date) -- has_labels = @labels && @labels.any? +%aside.right-sidebar{ class: sidebar_gutter_collapsed_class } + .issuable-sidebar + - can_edit_issuable = can?(current_user, :"admin_#{issuable.to_ability_name}", @project) + .block.issuable-sidebar-header + - if current_user + %span.issuable-header-text.hide-collapsed.pull-left + Todo + %a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", aria: { label: "Toggle sidebar" } } + = sidebar_gutter_toggle_icon + - if current_user + %button.btn.btn-default.issuable-header-btn.pull-right.js-issuable-todo{ type: "button", aria: { label: (todo.nil? ? "Add Todo" : "Mark Done") }, data: { todo_text: "Add Todo", mark_text: "Mark Done", issuable_id: issuable.id, issuable_type: issuable.class.name.underscore, url: namespace_project_todos_path(@project.namespace, @project), delete_path: (dashboard_todo_path(todo) if todo) } } + %span.js-issuable-todo-text + - if todo + Mark Done + - else + Add Todo + = icon('spin spinner', class: 'hidden js-issuable-todo-loading') -- content_for :page_specific_javascripts do - = page_specific_javascript_tag('issuable/issuable_bundle.js') + = form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, format: :json, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f| + .block.assignee + .sidebar-collapsed-icon.sidebar-collapsed-user{data: {toggle: "tooltip", placement: "left", container: "body"}, title: (issuable.assignee.name if issuable.assignee)} + - if issuable.assignee + = link_to_member(@project, issuable.assignee, size: 24) + - else + = icon('user') + .title.hide-collapsed + Assignee + = icon('spinner spin', class: 'block-loading') + - if can_edit_issuable + = link_to 'Edit', '#', class: 'edit-link pull-right' + .value.hide-collapsed + - if issuable.assignee + = link_to_member(@project, issuable.assignee, size: 32, extra_class: 'bold') do + - if issuable.instance_of?(MergeRequest) && !issuable.can_be_merged_by?(issuable.assignee) + %span.pull-right.cannot-be-merged{ data: { toggle: 'tooltip', placement: 'left' }, title: 'Not allowed to merge' } + = icon('exclamation-triangle') + %span.username + = issuable.assignee.to_reference + - else + %span.assign-yourself.no-value + No assignee + - if can_edit_issuable + \- + %a.js-assign-yourself{ href: '#' } + assign yourself -%aside.right-sidebar{ class: sidebar_gutter_collapsed_class } - #issuable-sidebar-app.issuable-sidebar{ 'data-issuable' => issuable.to_json, 'data-admin' => can_edit_issuable.to_s } - %issuable-todo-toggle{':todo' => 'issuable.todo', ':admin' => 'admin'} - %issuable-assignee-select{ ':assignee' => 'issuable.assignee', ':admin' => 'admin'} - %issuable-milestone-select{':milestone' => 'issuable.milestone', ':admin' => 'admin'} - %issuable-due-date-select{':due_date' => 'issuable.due_date', ':admin' => 'admin'} - %issuable-labels-select{':labels' => 'issuable.labels', 'admin' => 'admin' } - %issuable-participants-display{':participants' => 'issuable.participants', 'admin' => 'admin'} - %issuable-subscription-toggle{':subscription' => 'issuable.subscription' } - %issuable-project-refs{':refs' => 'issuable.refs', ':admin' => 'admin'} - - :javascript - new MilestoneSelect('{"namespace":"#{@project.namespace.path}","path":"#{@project.path}"}'); - new LabelsSelect(); - new IssuableContext('#{escape_javascript(current_user.to_json(only: [:username, :id, :name]))}'); - new Subscription('.subscription') - new gl.DueDateSelectors(); - sidebar = new Sidebar(); + .selectbox.hide-collapsed + = f.hidden_field 'assignee_id', value: issuable.assignee_id, id: 'issue_assignee_id' + = dropdown_tag('Select assignee', options: { toggle_class: 'js-user-search js-author-search', title: 'Assign to', filter: true, dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author', placeholder: 'Search users', data: { first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), author_id: issuable.author_id, field_name: "#{issuable.to_ability_name}[assignee_id]", issue_update: issuable_json_path(issuable), ability_name: issuable.to_ability_name, null_user: true } }) + + .block.milestone + .sidebar-collapsed-icon + = icon('clock-o') + %span + - if issuable.milestone + %span.has-tooltip{title: milestone_remaining_days(issuable.milestone), data: {container: 'body', html: 1, placement: 'left'}} + = issuable.milestone.title + - else + None + .title.hide-collapsed + Milestone + = icon('spinner spin', class: 'block-loading') + - if can_edit_issuable + = link_to 'Edit', '#', class: 'edit-link pull-right' + .value.hide-collapsed + - if issuable.milestone + = link_to issuable.milestone.title, namespace_project_milestone_path(@project.namespace, @project, issuable.milestone), class: "bold has-tooltip", title: milestone_remaining_days(issuable.milestone), data: { container: "body", html: 1 } + - else + %span.no-value None + + .selectbox.hide-collapsed + = f.hidden_field 'milestone_id', value: issuable.milestone_id, id: nil + = dropdown_tag('Milestone', options: { title: 'Assign milestone', toggle_class: 'js-milestone-select js-extra-options', filter: true, dropdown_class: 'dropdown-menu-selectable', placeholder: 'Search milestones', data: { show_no: true, field_name: "#{issuable.to_ability_name}[milestone_id]", project_id: @project.id, issuable_id: issuable.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable), use_id: true }}) + + - if issuable.has_attribute?(:due_date) + .block.due_date + .sidebar-collapsed-icon + = icon('calendar') + %span.js-due-date-sidebar-value + = issuable.due_date.try(:to_s, :medium) || 'None' + .title.hide-collapsed + Due date + = icon('spinner spin', class: 'block-loading') + - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) + = link_to 'Edit', '#', class: 'edit-link pull-right' + .value.hide-collapsed + %span.value-content + - if issuable.due_date + %span.bold= issuable.due_date.to_s(:medium) + - else + %span.no-value No due date + - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) + %span.no-value.js-remove-due-date-holder{ class: ("hidden" if issuable.due_date.nil?) } + \- + %a.js-remove-due-date{ href: "#", role: "button" } + remove due date + - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) + .selectbox.hide-collapsed + = f.hidden_field :due_date, value: issuable.due_date + .dropdown + %button.dropdown-menu-toggle.js-due-date-select{ type: 'button', data: { toggle: 'dropdown', field_name: "#{issuable.to_ability_name}[due_date]", ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable) } } + %span.dropdown-toggle-text Due date + = icon('chevron-down') + .dropdown-menu.dropdown-menu-due-date + = dropdown_title('Due date') + = dropdown_content do + .js-due-date-calendar + + - if @labels && @labels.any? + - selected_labels = issuable.labels + .block.labels + .sidebar-collapsed-icon.js-sidebar-labels-tooltip{ title: issuable_labels_tooltip(issuable.labels_array), data: { placement: "left", container: "body" } } + = icon('tags') + %span + = selected_labels.size + .title.hide-collapsed + Labels + = icon('spinner spin', class: 'block-loading') + - if can_edit_issuable + = link_to 'Edit', '#', class: 'edit-link pull-right' + .value.issuable-show-labels.hide-collapsed{ class: ("has-labels" if selected_labels.any?) } + - if selected_labels.any? + - selected_labels.each do |label| + = link_to_label(label, type: issuable.to_ability_name) + - else + %span.no-value None + .selectbox.hide-collapsed + - selected_labels.each do |label| + = hidden_field_tag "#{issuable.to_ability_name}[label_names][]", label.id, id: nil + .dropdown + %button.dropdown-menu-toggle.js-label-select.js-multiselect.js-label-sidebar-dropdown{type: "button", data: {toggle: "dropdown", default_label: "Labels", field_name: "#{issuable.to_ability_name}[label_names][]", ability_name: issuable.to_ability_name, show_no: "true", show_any: "true", namespace_path: @project.try(:namespace).try(:path), project_path: @project.try(:path), issue_update: issuable_json_path(issuable), labels: (namespace_project_labels_path(@project.namespace, @project, :json) if @project)}} + %span.dropdown-toggle-text{ class: ("is-default" if selected_labels.empty?)} + = multi_label_name(selected_labels, "Labels") + = icon('chevron-down') + .dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable + = render partial: "shared/issuable/label_page_default" + - if can? current_user, :admin_label, @project and @project + = render partial: "shared/issuable/label_page_create" + + = render "shared/issuable/participants", participants: issuable.participants(current_user) + - if current_user + - subscribed = issuable.subscribed?(current_user, @project) + .block.light.subscription{data: {url: toggle_subscription_path(issuable)}} + .sidebar-collapsed-icon + = icon('rss') + .title.hide-collapsed + Notifications + - subscribtion_status = subscribed ? 'subscribed' : 'unsubscribed' + %button.btn.btn-block.btn-default.js-subscribe-button.issuable-subscribe-button.hide-collapsed{ type: "button" } + %span= subscribed ? 'Unsubscribe' : 'Subscribe' + .subscription-status.hide-collapsed{data: {status: subscribtion_status}} + .unsubscribed{class: ( 'hidden' if subscribed )} + You're not receiving notifications from this thread. + .subscribed{class: ( 'hidden' unless subscribed )} + You're receiving notifications because you're subscribed to this thread. + + - project_ref = cross_project_reference(@project, issuable) + .block.project-reference + .sidebar-collapsed-icon.dont-change-state + = clipboard_button(clipboard_text: project_ref) + .cross-project-reference.hide-collapsed + %span + Reference: + %cite{title: project_ref} + = project_ref + = clipboard_button(clipboard_text: project_ref) + + :javascript + gl.IssuableResource = new gl.SubbableResource('#{issuable_json_path(issuable)}'); + function fetcher() { + return gl.IssuableResource.get.call(gl.IssuableResource, { type: 'GET', url: gl.IssuableResource.endpoint }); + } + new gl.SmartInterval({ + startingInterval: 5000, + maxInterval: 5000, + increaseByFactorOf: 2, + lazyStart: false, + callback: fetcher + }); + new MilestoneSelect('{"namespace":"#{@project.namespace.path}","path":"#{@project.path}"}'); + new LabelsSelect(); + new IssuableContext('#{escape_javascript(current_user.to_json(only: [:username, :id, :name]))}'); + new Subscription('.subscription') + new gl.DueDateSelectors(); + sidebar = new Sidebar(); -- GitLab From e0e0234e08bb6d6a890031d74befd5674687a0e1 Mon Sep 17 00:00:00 2001 From: Bryce Johnson Date: Wed, 23 Nov 2016 11:54:49 +0100 Subject: [PATCH 9/9] Re-add structure for sidebar-app to sidebar.html. --- app/views/shared/issuable/_sidebar.html.haml | 91 +++----------------- 1 file changed, 14 insertions(+), 77 deletions(-) diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 4235b4dd6a37..8ec255e0d1c6 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -1,6 +1,9 @@ - todo = issuable_todo(issuable) +- content_for :page_specific_javascripts do + = page_specific_javascript_tag('issuable/issuable_bundle.js') +- can_edit_issuable = can?(current_user, :"admin_#{issuable.to_ability_name}", @project) %aside.right-sidebar{ class: sidebar_gutter_collapsed_class } - .issuable-sidebar + #issuable-sidebar-app.issuable-sidebar{ 'data-issuable' => issuable.to_json, 'data-admin' => can_edit_issuable.to_s } - can_edit_issuable = can?(current_user, :"admin_#{issuable.to_ability_name}", @project) .block.issuable-sidebar-header - if current_user @@ -48,65 +51,10 @@ .selectbox.hide-collapsed = f.hidden_field 'assignee_id', value: issuable.assignee_id, id: 'issue_assignee_id' = dropdown_tag('Select assignee', options: { toggle_class: 'js-user-search js-author-search', title: 'Assign to', filter: true, dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author', placeholder: 'Search users', data: { first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), author_id: issuable.author_id, field_name: "#{issuable.to_ability_name}[assignee_id]", issue_update: issuable_json_path(issuable), ability_name: issuable.to_ability_name, null_user: true } }) - - .block.milestone - .sidebar-collapsed-icon - = icon('clock-o') - %span - - if issuable.milestone - %span.has-tooltip{title: milestone_remaining_days(issuable.milestone), data: {container: 'body', html: 1, placement: 'left'}} - = issuable.milestone.title - - else - None - .title.hide-collapsed - Milestone - = icon('spinner spin', class: 'block-loading') - - if can_edit_issuable - = link_to 'Edit', '#', class: 'edit-link pull-right' - .value.hide-collapsed - - if issuable.milestone - = link_to issuable.milestone.title, namespace_project_milestone_path(@project.namespace, @project, issuable.milestone), class: "bold has-tooltip", title: milestone_remaining_days(issuable.milestone), data: { container: "body", html: 1 } - - else - %span.no-value None - - .selectbox.hide-collapsed - = f.hidden_field 'milestone_id', value: issuable.milestone_id, id: nil - = dropdown_tag('Milestone', options: { title: 'Assign milestone', toggle_class: 'js-milestone-select js-extra-options', filter: true, dropdown_class: 'dropdown-menu-selectable', placeholder: 'Search milestones', data: { show_no: true, field_name: "#{issuable.to_ability_name}[milestone_id]", project_id: @project.id, issuable_id: issuable.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable), use_id: true }}) - + + %issuable-milestone-select{':milestone' => 'issuable.milestone', ':admin' => 'admin'} - if issuable.has_attribute?(:due_date) - .block.due_date - .sidebar-collapsed-icon - = icon('calendar') - %span.js-due-date-sidebar-value - = issuable.due_date.try(:to_s, :medium) || 'None' - .title.hide-collapsed - Due date - = icon('spinner spin', class: 'block-loading') - - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) - = link_to 'Edit', '#', class: 'edit-link pull-right' - .value.hide-collapsed - %span.value-content - - if issuable.due_date - %span.bold= issuable.due_date.to_s(:medium) - - else - %span.no-value No due date - - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) - %span.no-value.js-remove-due-date-holder{ class: ("hidden" if issuable.due_date.nil?) } - \- - %a.js-remove-due-date{ href: "#", role: "button" } - remove due date - - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) - .selectbox.hide-collapsed - = f.hidden_field :due_date, value: issuable.due_date - .dropdown - %button.dropdown-menu-toggle.js-due-date-select{ type: 'button', data: { toggle: 'dropdown', field_name: "#{issuable.to_ability_name}[due_date]", ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable) } } - %span.dropdown-toggle-text Due date - = icon('chevron-down') - .dropdown-menu.dropdown-menu-due-date - = dropdown_title('Due date') - = dropdown_content do - .js-due-date-calendar - + %issuable-due-date-select{':due_date' => 'issuable.due_date', ':admin' => 'admin'} - if @labels && @labels.any? - selected_labels = issuable.labels .block.labels @@ -166,21 +114,10 @@ = project_ref = clipboard_button(clipboard_text: project_ref) - :javascript - gl.IssuableResource = new gl.SubbableResource('#{issuable_json_path(issuable)}'); - function fetcher() { - return gl.IssuableResource.get.call(gl.IssuableResource, { type: 'GET', url: gl.IssuableResource.endpoint }); - } - new gl.SmartInterval({ - startingInterval: 5000, - maxInterval: 5000, - increaseByFactorOf: 2, - lazyStart: false, - callback: fetcher - }); - new MilestoneSelect('{"namespace":"#{@project.namespace.path}","path":"#{@project.path}"}'); - new LabelsSelect(); - new IssuableContext('#{escape_javascript(current_user.to_json(only: [:username, :id, :name]))}'); - new Subscription('.subscription') - new gl.DueDateSelectors(); - sidebar = new Sidebar(); + :javascript + new MilestoneSelect('{"namespace":"#{@project.namespace.path}","path":"#{@project.path}"}'); + new LabelsSelect(); + new IssuableContext('#{escape_javascript(current_user.to_json(only: [:username, :id, :name]))}'); + new Subscription('.subscription') + new gl.DueDateSelectors(); + sidebar = new Sidebar(); -- GitLab