Commit b30c6d85 authored by maeda's avatar maeda

Allow collapse/expand in gantt chart (#6417).

Patch by Yuichi HARADA.


git-svn-id: https://svn.redmine.org/redmine/trunk@17929 e93f8b46-1217-0410-a6f0-8f06a7374b81
parent 96308c53
......@@ -375,6 +375,7 @@
resizableSubjectColumn();
$("#draw_relations").change(drawGanttHandler);
$("#draw_progress_line").change(drawGanttHandler);
$('div.gantt_subjects .expander').on('click', ganttEntryClick);
});
$(window).resize(function() {
drawGanttHandler();
......
......@@ -698,21 +698,43 @@ module Redmine
end
def html_subject(params, subject, object)
style = "position: absolute;top:#{params[:top]}px;left:#{params[:indent]}px;"
style << "width:#{params[:subject_width] - params[:indent]}px;" if params[:subject_width]
content = html_subject_content(object) || subject
tag_options = {:style => style}
tag_options = {}
case object
when Issue
tag_options[:id] = "issue-#{object.id}"
tag_options[:class] = "issue-subject hascontextmenu"
tag_options[:title] = object.subject
children = object.children & project_issues(object.project)
has_children = children.present? && (children.collect(&:fixed_version).uniq & [object.fixed_version]).present?
when Version
tag_options[:id] = "version-#{object.id}"
tag_options[:class] = "version-name"
has_children = object.fixed_issues.exists?
when Project
tag_options[:class] = "project-name"
has_children = object.issues.exists? || object.versions.exists?
end
if object
tag_options[:data] = {
:collapse_expand => {
:top_increment => params[:top_increment],
:obj_id => "#{object.class}-#{object.id}".downcase,
},
}
end
if has_children
content = view.content_tag(:span, nil, :class => :expander) + content
tag_options[:class] << ' open'
else
if params[:indent]
params = params.dup
params[:indent] += 12
end
end
style = "position: absolute;top:#{params[:top]}px;left:#{params[:indent]}px;"
style << "width:#{params[:subject_width] - params[:indent]}px;" if params[:subject_width]
tag_options[:style] = style
output = view.content_tag(:div, content, tag_options)
@subjects << output
output
......@@ -751,6 +773,8 @@ module Redmine
def html_task(params, coords, markers, label, object)
output = ''
data_options = {}
data_options[:collapse_expand] = "#{object.class}-#{object.id}".downcase if object
css = "task " + case object
when Project
......@@ -774,13 +798,15 @@ module Redmine
html_id = "task-todo-version-#{object.id}" if object.is_a?(Version)
content_opt = {:style => style,
:class => "#{css} task_todo",
:id => html_id}
:id => html_id,
:data => {}}
if object.is_a?(Issue)
rels = issue_relations(object)
if rels.present?
content_opt[:data] = {"rels" => rels.to_json}
end
end
content_opt[:data].merge!(data_options)
output << view.content_tag(:div, '&nbsp;'.html_safe, content_opt)
if coords[:bar_late_end]
width = coords[:bar_late_end] - coords[:bar_start] - 2
......@@ -790,7 +816,8 @@ module Redmine
style << "width:#{width}px;"
output << view.content_tag(:div, '&nbsp;'.html_safe,
:style => style,
:class => "#{css} task_late")
:class => "#{css} task_late",
:data => data_options)
end
if coords[:bar_progress_end]
width = coords[:bar_progress_end] - coords[:bar_start] - 2
......@@ -803,7 +830,8 @@ module Redmine
output << view.content_tag(:div, '&nbsp;'.html_safe,
:style => style,
:class => "#{css} task_done",
:id => html_id)
:id => html_id,
:data => data_options)
end
end
# Renders the markers
......@@ -815,7 +843,8 @@ module Redmine
style << "width:15px;"
output << view.content_tag(:div, '&nbsp;'.html_safe,
:style => style,
:class => "#{css} marker starting")
:class => "#{css} marker starting",
:data => data_options)
end
if coords[:end]
style = ""
......@@ -824,7 +853,8 @@ module Redmine
style << "width:15px;"
output << view.content_tag(:div, '&nbsp;'.html_safe,
:style => style,
:class => "#{css} marker ending")
:class => "#{css} marker ending",
:data => data_options)
end
end
# Renders the label on the right
......@@ -835,7 +865,8 @@ module Redmine
style << "width:15px;"
output << view.content_tag(:div, label,
:style => style,
:class => "#{css} label")
:class => "#{css} label",
:data => data_options)
end
# Renders the tooltip
if object.is_a?(Issue) && coords[:bar_start] && coords[:bar_end]
......@@ -851,7 +882,8 @@ module Redmine
style << "height:12px;"
output << view.content_tag(:div, s.html_safe,
:style => style,
:class => "tooltip hascontextmenu")
:class => "tooltip hascontextmenu",
:data => data_options)
end
@lines << output
output
......
......@@ -17,6 +17,7 @@ function setDrawArea() {
function getRelationsArray() {
var arr = new Array();
$.each($('div.task_todo[data-rels]'), function(index_div, element) {
if(!$(element).is(':visible')) return true;
var element_id = $(element).attr("id");
if (element_id != null) {
var issue_id = element_id.replace("task-todo-issue-", "");
......@@ -106,6 +107,7 @@ function getProgressLinesArray() {
var today_left = $('#today_line').position().left;
arr.push({left: today_left, top: 0});
$.each($('div.issue-subject, div.version-name'), function(index, element) {
if(!$(element).is(':visible')) return true;
var t = $(element).position().top - draw_top ;
var h = ($(element).height() / 9);
var element_top_upper = t - h;
......@@ -169,7 +171,7 @@ function drawGanttHandler() {
draw_gantt = Raphael(folder);
setDrawArea();
if ($("#draw_progress_line").prop('checked'))
drawGanttProgressLines();
try{drawGanttProgressLines();}catch(e){}
if ($("#draw_relations").prop('checked'))
drawRelations();
}
......@@ -195,3 +197,59 @@ function resizableSubjectColumn(){
$('td.gantt_subjects_column').resizable('enable');
};
}
ganttEntryClick = function(e){
var subject = $(e.target.parentElement);
var subject_left = parseInt(subject.css('left'));
var target_shown = null;
var target_top = 0;
var total_height = 0;
var out_of_hierarchy = false;
var iconChange = null;
if(subject.hasClass('open'))
iconChange = function(element){
$(element).removeClass('open');
};
else
iconChange = function(element){
$(element).addClass('open');
};
iconChange(subject);
subject.nextAll('div').each(function(_, element){
var el = $(element);
var json = el.data('collapse-expand');
if(out_of_hierarchy || parseInt(el.css('left')) <= subject_left){
out_of_hierarchy = true;
if(target_shown == null) return false;
var new_top_val = parseInt(el.css('top')) + total_height * (target_shown ? -1 : 1);
el.css('top', new_top_val);
$('#gantt_area form > div[data-collapse-expand="' + json.obj_id + '"]').each(function(_, task){
$(task).css('top', new_top_val);
});
return true;
}
var is_shown = el.is(':visible');
if(target_shown == null){
target_shown = is_shown;
target_top = parseInt(el.css('top'));
total_height = 0;
}
if(is_shown == target_shown){
$('#gantt_area form > div[data-collapse-expand="' + json.obj_id + '"]').each(function(_, task){
var el_task = $(task);
if(!is_shown)
el_task.css('top', target_top + total_height);
if(!el_task.hasClass('tooltip'))
el_task.toggle(!is_shown);
});
if(!is_shown)
el.css('top', target_top + total_height);
iconChange(el);
el.toggle(!is_shown);
total_height += parseInt(json.top_increment);
}
});
drawGanttHandler();
};
......@@ -291,8 +291,10 @@ tr.entry td.age { text-align: right; }
tr.entry.file td.filename a { margin-left: 16px; }
tr.entry.file td.filename_no_report a { margin-left: 16px; }
tr span.expander {background: url(../images/arrow_right.png) no-repeat 2px 50%; padding-left: 8px; margin-left: 0; cursor: pointer;}
tr.open span.expander {background-image: url(../images/arrow_down.png);}
tr span.expander, .gantt_subjects div > span.expander {background: url(../images/arrow_right.png) no-repeat 2px 50%; padding-left: 8px; margin-left: 0; cursor: pointer;}
tr.open span.expander, .gantt_subjects div.open > span.expander {background-image: url(../images/arrow_down.png);}
.gantt_subjects div > span.expander {padding-left: 12px;}
.gantt_subjects div > span .icon-gravatar {float: none;}
tr.changeset { height: 20px }
tr.changeset ul, ol { margin-top: 0px; margin-bottom: 0px; }
......
......@@ -152,7 +152,8 @@ class Redmine::Helpers::GanttHelperTest < Redmine::HelperTest
setup_subjects
@output_buffer = @gantt.subjects
assert_select "div.issue-subject", /#{@issue.subject}/
assert_select 'div.issue-subject[style*="left:44px"]'
# subject 56px: 44px + 12px(collapse/expand icon's width)
assert_select 'div.issue-subject[style*="left:56px"]'
end
test "#subjects issue assigned to a shared version of another project should be rendered" do
......@@ -200,9 +201,10 @@ class Redmine::Helpers::GanttHelperTest < Redmine::HelperTest
assert_select 'div.issue-subject[style*="left:44px"]', /#{@issue.subject}/
# children 64px
assert_select 'div.issue-subject[style*="left:64px"]', /child1/
assert_select 'div.issue-subject[style*="left:64px"]', /child2/
# grandchild 84px
assert_select 'div.issue-subject[style*="left:84px"]', /grandchild/, @output_buffer
# children 76px: 64px + 12px(collapse/expand icon's width)
assert_select 'div.issue-subject[style*="left:76px"]', /child2/
# grandchild 96px: 84px + 12px(collapse/expand icon's width)
assert_select 'div.issue-subject[style*="left:96px"]', /grandchild/, @output_buffer
end
test "#lines" do
......@@ -298,7 +300,8 @@ class Redmine::Helpers::GanttHelperTest < Redmine::HelperTest
test "#subject should use the indent option to move the div to the right" do
create_gantt
@output_buffer = @gantt.subject('subject', :format => :html, :indent => 40)
assert_select 'div[style*="left:40"]'
# subject 52px: 40px(indent) + 12px(collapse/expand icon's width)
assert_select 'div[style*="left:52px"]'
end
test "#line_for_project" do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment