0001-Show-selected-columns-on-gantt.patch

Mizuki ISHIKAWA, 2018-02-23 08:03

Download (12.1 KB)

View differences:

app/views/gantts/show.html.erb
28 28
    <legend onclick="toggleFieldset(this);"><%= l(:label_options) %></legend>
29 29
    <div style="display: none;">
30 30
      <table>
31
        <tr>
32
          <td>
33
            <fieldset>
34
              <legend>
35
                <%= l(:field_column_names) %>
36
              </legend>
37
              <%= render_query_columns_selection(@query) %>
38
            </fieldset>
39
          </td>
40
        </tr>
31 41
        <tr>
32 42
          <td>
33 43
            <fieldset>
......
92 102

  
93 103
  subject_width = 330
94 104
  header_height = 18
105
  selected_column_width = 50
95 106

  
96 107
  headers_height = header_height
97 108
  show_weeks = false
......
157 168
    <% end %>
158 169
  <% end %>
159 170
</td>
160

  
161
<td>
171
<% @query.columns.each do |column| %>
172
  <% next if Redmine::Helpers::Gantt::UNAVAILABLE_COLUMNS.include?(column.name) %>
173
  <td style="width:<%= selected_column_width %>px; padding:0px;" class="gantt_<%= column.name %>_column gantt_selected_column" id="<%= column.name %>">
174
    <%
175
      style = "position:relative;"
176
      style += "height: #{t_height + 24}px;"
177
      style += "width: #{selected_column_width + 1}px;"
178
    %>
179
    <%= content_tag(:div, :style => style, :class => "gantt_#{column.name}_container") do %>
180
      <%
181
        style = "width: #{selected_column_width}px;"
182
        style += "height: #{t_height}px;"
183
        style += 'border-left: 0px;'
184
        style += 'overflow: hidden;'
185
      %>
186
      <%= content_tag(:div, '', :style => style, :class => "gantt_hdr") %>
187
      <%
188
        style = "width: #{selected_column_width}px;"
189
        style += "height: #{headers_height}px;"
190
        style += 'background: #eee;'
191
        style += 'border-left: 0px !important;'
192
      %>
193
      <%= content_tag(:div, content_tag(:p, column.caption, :class => 'gantt_hdr_selected_column_name'), :style => style, :class => "gantt_hdr") %>
194
      <%= content_tag(:div, :class => "gantt_#{column.name} gantt_selected_column_content") do %>
195
        <%= @gantt.selected_column_content({:column => column, :top => headers_height + 8, :zoom => zoom, :g_width => g_width}).html_safe %>
196
      <% end %>
197
    <% end %>
198
  </td>
199
<% end %>
200
<td style='padding: 0px;'>
162 201
<div style="position:relative;height:<%= t_height + 24 %>px;overflow:auto;" id="gantt_area">
163 202
<%
164 203
  style  = ""
......
371 410
<%= javascript_tag do %>
372 411
  var issue_relation_type = <%= raw Redmine::Helpers::Gantt::DRAW_TYPES.to_json %>;
373 412
  $(function() {
413
    disable_unavailable_columns('<%= Redmine::Helpers::Gantt::UNAVAILABLE_COLUMNS.map{|column| column.to_s }.join(',') %>'.split(','));
374 414
    drawGanttHandler();
375 415
    resizableSubjectColumn();
376 416
    $("#draw_relations").change(drawGanttHandler);
lib/redmine/helpers/gantt.rb
32 32
        IssueRelation::TYPE_PRECEDES => { :landscape_margin => 20, :color => '#628FEA' }
33 33
      }.freeze
34 34

  
35
      UNAVAILABLE_COLUMNS = [:id, :subject]
36

  
35 37
      # :nodoc:
36 38
      # Some utility methods for the PDF export
37 39
      class PDF
......
76 78
        @date_to = (@date_from >> @months) - 1
77 79
        @subjects = ''
78 80
        @lines = ''
81
        @columns = {}
79 82
        @number_of_rows = nil
80 83
        @truncated = false
81 84
        if options.has_key?(:max_rows)
......
135 138
        @lines
136 139
      end
137 140

  
141
      # Renders the selected column of the Gantt chart, the right side of subjects.
142
      def selected_column_content(options={})
143
        render(options.merge(:only => :selected_columns)) unless @columns.has_key?(options[:column].name)
144
        @columns[options[:column].name]
145
      end
146

  
138 147
      # Returns issues that will be rendered
139 148
      def issues
140 149
        @issues ||= @query.issues(
......
196 205
                   :indent_increment => 20, :render => :subject,
197 206
                   :format => :html}.merge(options)
198 207
        indent = options[:indent] || 4
199
        @subjects = '' unless options[:only] == :lines
200
        @lines = '' unless options[:only] == :subjects
208
        @subjects = '' unless options[:only] == :lines || options[:only] == :selected_columns
209
        @lines = '' unless options[:only] == :subjects || options[:only] == :selected_columns
210
        @columns[options[:column].name] = '' if options[:only] == :selected_columns && @columns.has_key?(options[:column]) == false
201 211
        @number_of_rows = 0
202 212
        begin
203 213
          Project.project_tree(projects) do |project, level|
......
207 217
        rescue MaxLinesLimitReached
208 218
          @truncated = true
209 219
        end
210
        @subjects_rendered = true unless options[:only] == :lines
211
        @lines_rendered = true unless options[:only] == :subjects
220
        @subjects_rendered = true unless options[:only] == :lines || options[:only] == :selected_columns
221
        @lines_rendered = true unless options[:only] == :subjects || options[:only] == :selected_columns
212 222
        render_end(options)
213 223
      end
214 224

  
......
254 264

  
255 265
      def render_object_row(object, options)
256 266
        class_name = object.class.name.downcase
257
        send("subject_for_#{class_name}", object, options) unless options[:only] == :lines
258
        send("line_for_#{class_name}", object, options) unless options[:only] == :subjects
267
        send("subject_for_#{class_name}", object, options) unless options[:only] == :lines || options[:only] == :selected_columns
268
        send("line_for_#{class_name}", object, options) unless options[:only] == :subjects || options[:only] == :selected_columns
269
        column_content_for_issue(object, options) if options[:only] == :selected_columns && options[:column].present? && object.is_a?(Issue)
259 270
        options[:top] += options[:top_increment]
260 271
        @number_of_rows += 1
261 272
        if @max_rows && @number_of_rows >= @max_rows
......
323 334
        end
324 335
      end
325 336

  
337
      def column_content_for_issue(issue, options)
338
        if options[:format] == :html
339
          style = "position: absolute;top: #{options[:top]}px; font-size: 0.8em;"
340
          content = view.content_tag(:div, view.column_content(options[:column], issue), :style => style, :class => "issue_#{options[:column].name}", :id => "#{options[:column].name}_issue_#{issue.id}")
341
          @columns[options[:column].name] << content if @columns.has_key?(options[:column].name)
342
          content
343
        end
344
      end
345

  
326 346
      def subject(label, options, object=nil)
327 347
        send "#{options[:format]}_subject", options, label, object
328 348
      end
public/javascripts/gantt.js
161 161
  }
162 162
}
163 163

  
164
function drawSelectedColumns(){
165
  if(isMobile()) {
166
    $('td.gantt_selected_column').each(function(i) {
167
      $(this).hide();
168
    });
169
  }else{
170
    $('#content').addClass("gantt_content");
171
    $('td.gantt_selected_column').each(function() {
172
      $(this).show();
173
      var column_name = $(this).attr('id');
174
      $(this).resizable({
175
        alsoResize: `.gantt_${column_name}_container, .gantt_${column_name}_container > .gantt_hdr`,
176
        minWidth: 20,
177
        handles: "e",
178
        create: function( event, ui ) {
179
          $(".ui-resizable-e").css("cursor","ew-resize");
180
        }
181
      }).on('resize', function (e) {
182
          e.stopPropagation();
183
      });
184
    });
185
  }
186
}
187

  
164 188
function drawGanttHandler() {
165 189
  var folder = document.getElementById('gantt_draw_area');
166 190
  if(draw_gantt != null)
......
168 192
  else
169 193
    draw_gantt = Raphael(folder);
170 194
  setDrawArea();
195
  drawSelectedColumns();
171 196
  if ($("#draw_progress_line").prop('checked'))
172 197
    drawGanttProgressLines();
173 198
  if ($("#draw_relations").prop('checked'))
......
195 220
    $('td.gantt_subjects_column').resizable('enable');
196 221
  };
197 222
}
223

  
224
function disable_unavailable_columns(unavailable_columns){
225
  $.each(unavailable_columns, function(index, value) {
226
    $('#available_c, #selected_c').children("[value='" + value + "']").prop('disabled', true);
227
  });
228
}
public/stylesheets/application.css
1169 1169

  
1170 1170
.gantt_hdr.nwday {background-color:#f1f1f1; color:#999;}
1171 1171

  
1172
.gantt_subjects { font-size: 0.8em; }
1173
.gantt_subjects div { line-height:16px;height:16px;overflow:hidden;white-space:nowrap;text-overflow: ellipsis; }
1172
.gantt_subjects, .gantt_selected_column_content.gantt_hdr { font-size: 0.8em; }
1173
.gantt_subjects div, .gantt_selected_column_content div {
1174
  line-height: 16px;
1175
  height: 16px;
1176
  white-space: nowrap;
1177
  text-overflow: ellipsis;
1178
  overflow: hidden !important;
1179
  width: 100%;
1180
}
1174 1181
.gantt_subjects div.issue-subject:hover { background-color:#ffffdd; }
1182
.gantt_selected_column_content { padding-left: 3px; padding-right: 3px;}
1175 1183

  
1176 1184
.gantt_subjects .issue-subject img.icon-gravatar {
1177 1185
  margin: 2px 5px 0px 2px;
1178 1186
}
1187
.gantt_hdr_selected_column_name {
1188
  padding-top: 15px;
1189
  font-size: 0.8em;
1190
  white-space: nowrap;
1191
  text-overflow: ellipsis;
1192
  overflow: hidden;
1193
}
1194
.gantt_content {
1195
  overflow: scroll;
1196
}
1179 1197

  
1180 1198
.task {
1181 1199
  position: absolute;
test/unit/lib/redmine/helpers/gantt_test.rb
23 23

  
24 24
  include ProjectsHelper
25 25
  include IssuesHelper
26
  include QueriesHelper
26 27
  include ERB::Util
27 28
  include Rails.application.routes.url_helpers
28 29

  
......
237 238
    assert_select "div.tooltip", /#{@issue.subject}/
238 239
  end
239 240

  
241
  test "#selected_column_content" do
242
    create_gantt
243
    issue = Issue.generate!
244
    @gantt.query.column_names = [:assigned_to]
245
    issue.update(:assigned_to_id => issue.assignable_users.first.id)
246
    @project.issues << issue
247
    # :column => assigned_to
248
    @output_buffer = @gantt.selected_column_content({ :column => @gantt.query.columns.last })
249
    assert_select "div.issue_assigned_to#assigned_to_issue_#{issue.id}"
250
  end
251

  
240 252
  test "#subject_for_project" do
241 253
    create_gantt
242 254
    @output_buffer = @gantt.subject_for_project(@project, :format => :html)
......
418 430
    assert_select "div.label", :text => 'line'
419 431
  end
420 432

  
433
  test "#column_content_for_issue" do
434
    create_gantt
435
    @gantt.query.column_names = [:assigned_to]
436
    issue = Issue.generate!
437
    issue.update(:assigned_to_id => issue.assignable_users.first.id)
438
    @project.issues << issue
439
    # :column => assigned_to
440
    options = { :column => @gantt.query.columns.last, :top => 64, :format => :html }
441
    @output_buffer = @gantt.column_content_for_issue(issue, options)
442

  
443
    assert_select "div.issue_assigned_to#assigned_to_issue_#{issue.id}"
444
    assert_includes @output_buffer, column_content(options[:column], issue)
445
  end
446

  
421 447
  def test_sort_issues_no_date
422 448
    project = Project.generate!
423 449
    issue1 = Issue.generate!(:subject => "test", :project => project)
424
-