mirror of
https://github.com/meineerde/redmine.git
synced 2025-12-20 15:31:12 +00:00
Allow collapse/expand in gantt chart (#6417).
Patch by Yuichi HARADA. git-svn-id: http://svn.redmine.org/redmine/trunk@17925 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
parent
49b1aeee59
commit
a925f9603d
@ -375,6 +375,7 @@
|
|||||||
resizableSubjectColumn();
|
resizableSubjectColumn();
|
||||||
$("#draw_relations").change(drawGanttHandler);
|
$("#draw_relations").change(drawGanttHandler);
|
||||||
$("#draw_progress_line").change(drawGanttHandler);
|
$("#draw_progress_line").change(drawGanttHandler);
|
||||||
|
$('div.gantt_subjects .expander').on('click', ganttEntryClick);
|
||||||
});
|
});
|
||||||
$(window).resize(function() {
|
$(window).resize(function() {
|
||||||
drawGanttHandler();
|
drawGanttHandler();
|
||||||
|
|||||||
@ -195,6 +195,7 @@ module Redmine
|
|||||||
options = {:top => 0, :top_increment => 20,
|
options = {:top => 0, :top_increment => 20,
|
||||||
:indent_increment => 20, :render => :subject,
|
:indent_increment => 20, :render => :subject,
|
||||||
:format => :html}.merge(options)
|
:format => :html}.merge(options)
|
||||||
|
options[:indent_increment] += 12 if options[:format] == :html
|
||||||
indent = options[:indent] || 4
|
indent = options[:indent] || 4
|
||||||
@subjects = '' unless options[:only] == :lines
|
@subjects = '' unless options[:only] == :lines
|
||||||
@lines = '' unless options[:only] == :subjects
|
@lines = '' unless options[:only] == :subjects
|
||||||
@ -221,7 +222,9 @@ module Redmine
|
|||||||
# then render project versions and their issues
|
# then render project versions and their issues
|
||||||
versions = project_versions(project)
|
versions = project_versions(project)
|
||||||
self.class.sort_versions!(versions)
|
self.class.sort_versions!(versions)
|
||||||
|
indent = options[:indent]
|
||||||
versions.each do |version|
|
versions.each do |version|
|
||||||
|
options[:indent] = indent
|
||||||
render_version(project, version, options)
|
render_version(project, version, options)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -698,21 +701,38 @@ module Redmine
|
|||||||
end
|
end
|
||||||
|
|
||||||
def html_subject(params, subject, object)
|
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
|
content = html_subject_content(object) || subject
|
||||||
tag_options = {:style => style}
|
tag_options = {}
|
||||||
case object
|
case object
|
||||||
when Issue
|
when Issue
|
||||||
tag_options[:id] = "issue-#{object.id}"
|
tag_options[:id] = "issue-#{object.id}"
|
||||||
tag_options[:class] = "issue-subject hascontextmenu"
|
tag_options[:class] = "issue-subject hascontextmenu"
|
||||||
tag_options[:title] = object.subject
|
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
|
when Version
|
||||||
tag_options[:id] = "version-#{object.id}"
|
tag_options[:id] = "version-#{object.id}"
|
||||||
tag_options[:class] = "version-name"
|
tag_options[:class] = "version-name"
|
||||||
|
has_children = object.fixed_issues.exists?
|
||||||
when Project
|
when Project
|
||||||
tag_options[:class] = "project-name"
|
tag_options[:class] = "project-name"
|
||||||
|
has_children = object.issues.exists? || object.versions.exists?
|
||||||
end
|
end
|
||||||
|
tag_options[:data] = {
|
||||||
|
:collapse_expand => {
|
||||||
|
:top_increment => params[:top_increment],
|
||||||
|
:obj_id => "#{object.class}-#{object.id}".downcase,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if has_children
|
||||||
|
content = view.content_tag(:span, nil, :class => :expander) + content
|
||||||
|
params = params.dup
|
||||||
|
params[:indent] -= 12 if params[:indent] >= 12
|
||||||
|
tag_options[:class] << ' open'
|
||||||
|
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)
|
output = view.content_tag(:div, content, tag_options)
|
||||||
@subjects << output
|
@subjects << output
|
||||||
output
|
output
|
||||||
@ -751,6 +771,9 @@ module Redmine
|
|||||||
|
|
||||||
def html_task(params, coords, markers, label, object)
|
def html_task(params, coords, markers, label, object)
|
||||||
output = ''
|
output = ''
|
||||||
|
data_options = {
|
||||||
|
:collapse_expand => "#{object.class}-#{object.id}".downcase,
|
||||||
|
}
|
||||||
|
|
||||||
css = "task " + case object
|
css = "task " + case object
|
||||||
when Project
|
when Project
|
||||||
@ -774,13 +797,15 @@ module Redmine
|
|||||||
html_id = "task-todo-version-#{object.id}" if object.is_a?(Version)
|
html_id = "task-todo-version-#{object.id}" if object.is_a?(Version)
|
||||||
content_opt = {:style => style,
|
content_opt = {:style => style,
|
||||||
:class => "#{css} task_todo",
|
:class => "#{css} task_todo",
|
||||||
:id => html_id}
|
:id => html_id,
|
||||||
|
:data => {}}
|
||||||
if object.is_a?(Issue)
|
if object.is_a?(Issue)
|
||||||
rels = issue_relations(object)
|
rels = issue_relations(object)
|
||||||
if rels.present?
|
if rels.present?
|
||||||
content_opt[:data] = {"rels" => rels.to_json}
|
content_opt[:data] = {"rels" => rels.to_json}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
content_opt[:data].merge!(data_options)
|
||||||
output << view.content_tag(:div, ' '.html_safe, content_opt)
|
output << view.content_tag(:div, ' '.html_safe, content_opt)
|
||||||
if coords[:bar_late_end]
|
if coords[:bar_late_end]
|
||||||
width = coords[:bar_late_end] - coords[:bar_start] - 2
|
width = coords[:bar_late_end] - coords[:bar_start] - 2
|
||||||
@ -790,7 +815,8 @@ module Redmine
|
|||||||
style << "width:#{width}px;"
|
style << "width:#{width}px;"
|
||||||
output << view.content_tag(:div, ' '.html_safe,
|
output << view.content_tag(:div, ' '.html_safe,
|
||||||
:style => style,
|
:style => style,
|
||||||
:class => "#{css} task_late")
|
:class => "#{css} task_late",
|
||||||
|
:data => data_options)
|
||||||
end
|
end
|
||||||
if coords[:bar_progress_end]
|
if coords[:bar_progress_end]
|
||||||
width = coords[:bar_progress_end] - coords[:bar_start] - 2
|
width = coords[:bar_progress_end] - coords[:bar_start] - 2
|
||||||
@ -803,7 +829,8 @@ module Redmine
|
|||||||
output << view.content_tag(:div, ' '.html_safe,
|
output << view.content_tag(:div, ' '.html_safe,
|
||||||
:style => style,
|
:style => style,
|
||||||
:class => "#{css} task_done",
|
:class => "#{css} task_done",
|
||||||
:id => html_id)
|
:id => html_id,
|
||||||
|
:data => data_options)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
# Renders the markers
|
# Renders the markers
|
||||||
@ -815,7 +842,8 @@ module Redmine
|
|||||||
style << "width:15px;"
|
style << "width:15px;"
|
||||||
output << view.content_tag(:div, ' '.html_safe,
|
output << view.content_tag(:div, ' '.html_safe,
|
||||||
:style => style,
|
:style => style,
|
||||||
:class => "#{css} marker starting")
|
:class => "#{css} marker starting",
|
||||||
|
:data => data_options)
|
||||||
end
|
end
|
||||||
if coords[:end]
|
if coords[:end]
|
||||||
style = ""
|
style = ""
|
||||||
@ -824,7 +852,8 @@ module Redmine
|
|||||||
style << "width:15px;"
|
style << "width:15px;"
|
||||||
output << view.content_tag(:div, ' '.html_safe,
|
output << view.content_tag(:div, ' '.html_safe,
|
||||||
:style => style,
|
:style => style,
|
||||||
:class => "#{css} marker ending")
|
:class => "#{css} marker ending",
|
||||||
|
:data => data_options)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
# Renders the label on the right
|
# Renders the label on the right
|
||||||
@ -835,7 +864,8 @@ module Redmine
|
|||||||
style << "width:15px;"
|
style << "width:15px;"
|
||||||
output << view.content_tag(:div, label,
|
output << view.content_tag(:div, label,
|
||||||
:style => style,
|
:style => style,
|
||||||
:class => "#{css} label")
|
:class => "#{css} label",
|
||||||
|
:data => data_options)
|
||||||
end
|
end
|
||||||
# Renders the tooltip
|
# Renders the tooltip
|
||||||
if object.is_a?(Issue) && coords[:bar_start] && coords[:bar_end]
|
if object.is_a?(Issue) && coords[:bar_start] && coords[:bar_end]
|
||||||
@ -851,7 +881,8 @@ module Redmine
|
|||||||
style << "height:12px;"
|
style << "height:12px;"
|
||||||
output << view.content_tag(:div, s.html_safe,
|
output << view.content_tag(:div, s.html_safe,
|
||||||
:style => style,
|
:style => style,
|
||||||
:class => "tooltip hascontextmenu")
|
:class => "tooltip hascontextmenu",
|
||||||
|
:data => data_options)
|
||||||
end
|
end
|
||||||
@lines << output
|
@lines << output
|
||||||
output
|
output
|
||||||
|
|||||||
@ -17,6 +17,7 @@ function setDrawArea() {
|
|||||||
function getRelationsArray() {
|
function getRelationsArray() {
|
||||||
var arr = new Array();
|
var arr = new Array();
|
||||||
$.each($('div.task_todo[data-rels]'), function(index_div, element) {
|
$.each($('div.task_todo[data-rels]'), function(index_div, element) {
|
||||||
|
if(!$(element).is(':visible')) return true;
|
||||||
var element_id = $(element).attr("id");
|
var element_id = $(element).attr("id");
|
||||||
if (element_id != null) {
|
if (element_id != null) {
|
||||||
var issue_id = element_id.replace("task-todo-issue-", "");
|
var issue_id = element_id.replace("task-todo-issue-", "");
|
||||||
@ -106,6 +107,7 @@ function getProgressLinesArray() {
|
|||||||
var today_left = $('#today_line').position().left;
|
var today_left = $('#today_line').position().left;
|
||||||
arr.push({left: today_left, top: 0});
|
arr.push({left: today_left, top: 0});
|
||||||
$.each($('div.issue-subject, div.version-name'), function(index, element) {
|
$.each($('div.issue-subject, div.version-name'), function(index, element) {
|
||||||
|
if(!$(element).is(':visible')) return true;
|
||||||
var t = $(element).position().top - draw_top ;
|
var t = $(element).position().top - draw_top ;
|
||||||
var h = ($(element).height() / 9);
|
var h = ($(element).height() / 9);
|
||||||
var element_top_upper = t - h;
|
var element_top_upper = t - h;
|
||||||
@ -169,7 +171,7 @@ function drawGanttHandler() {
|
|||||||
draw_gantt = Raphael(folder);
|
draw_gantt = Raphael(folder);
|
||||||
setDrawArea();
|
setDrawArea();
|
||||||
if ($("#draw_progress_line").prop('checked'))
|
if ($("#draw_progress_line").prop('checked'))
|
||||||
drawGanttProgressLines();
|
try{drawGanttProgressLines();}catch(e){}
|
||||||
if ($("#draw_relations").prop('checked'))
|
if ($("#draw_relations").prop('checked'))
|
||||||
drawRelations();
|
drawRelations();
|
||||||
}
|
}
|
||||||
@ -195,3 +197,59 @@ function resizableSubjectColumn(){
|
|||||||
$('td.gantt_subjects_column').resizable('enable');
|
$('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 a { margin-left: 16px; }
|
||||||
tr.entry.file td.filename_no_report 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 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 {background-image: url(../images/arrow_down.png);}
|
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 { height: 20px }
|
||||||
tr.changeset ul, ol { margin-top: 0px; margin-bottom: 0px; }
|
tr.changeset ul, ol { margin-top: 0px; margin-bottom: 0px; }
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user