Feature #6417 » 6417-collapse-expand-gantt-v3.patch
| app/views/gantts/show.html.erb | ||
|---|---|---|
| 375 | 375 |
resizableSubjectColumn(); |
| 376 | 376 |
$("#draw_relations").change(drawGanttHandler);
|
| 377 | 377 |
$("#draw_progress_line").change(drawGanttHandler);
|
| 378 |
$('div.gantt_subjects .expander').on('click', ganttEntryClick);
|
|
| 378 | 379 |
}); |
| 379 | 380 |
$(window).resize(function() {
|
| 380 | 381 |
drawGanttHandler(); |
| lib/redmine/helpers/gantt.rb | ||
|---|---|---|
| 698 | 698 |
end |
| 699 | 699 | |
| 700 | 700 |
def html_subject(params, subject, object) |
| 701 |
style = "position: absolute;top:#{params[:top]}px;left:#{params[:indent]}px;"
|
|
| 702 |
style << "width:#{params[:subject_width] - params[:indent]}px;" if params[:subject_width]
|
|
| 703 | 701 |
content = html_subject_content(object) || subject |
| 704 |
tag_options = {:style => style}
|
|
| 702 |
tag_options = {}
|
|
| 705 | 703 |
case object |
| 706 | 704 |
when Issue |
| 707 | 705 |
tag_options[:id] = "issue-#{object.id}"
|
| 708 | 706 |
tag_options[:class] = "issue-subject hascontextmenu" |
| 709 | 707 |
tag_options[:title] = object.subject |
| 708 |
children = object.children & project_issues(object.project) |
|
| 709 |
has_children = children.present? && (children.collect(&:fixed_version).uniq & [object.fixed_version]).present? |
|
| 710 | 710 |
when Version |
| 711 | 711 |
tag_options[:id] = "version-#{object.id}"
|
| 712 | 712 |
tag_options[:class] = "version-name" |
| 713 |
has_children = object.fixed_issues.exists? |
|
| 713 | 714 |
when Project |
| 714 | 715 |
tag_options[:class] = "project-name" |
| 716 |
has_children = object.issues.exists? || object.versions.exists? |
|
| 717 |
end |
|
| 718 |
if object |
|
| 719 |
tag_options[:data] = {
|
|
| 720 |
:collapse_expand => {
|
|
| 721 |
:top_increment => params[:top_increment], |
|
| 722 |
:obj_id => "#{object.class}-#{object.id}".downcase,
|
|
| 723 |
}, |
|
| 724 |
} |
|
| 725 |
end |
|
| 726 |
if has_children |
|
| 727 |
content = view.content_tag(:span, nil, :class => :expander) + content |
|
| 728 |
tag_options[:class] << ' open' |
|
| 729 |
else |
|
| 730 |
if params[:indent] |
|
| 731 |
params = params.dup |
|
| 732 |
params[:indent] += 12 |
|
| 733 |
end |
|
| 715 | 734 |
end |
| 735 |
style = "position: absolute;top:#{params[:top]}px;left:#{params[:indent]}px;"
|
|
| 736 |
style << "width:#{params[:subject_width] - params[:indent]}px;" if params[:subject_width]
|
|
| 737 |
tag_options[:style] = style |
|
| 716 | 738 |
output = view.content_tag(:div, content, tag_options) |
| 717 | 739 |
@subjects << output |
| 718 | 740 |
output |
| ... | ... | |
| 751 | 773 | |
| 752 | 774 |
def html_task(params, coords, markers, label, object) |
| 753 | 775 |
output = '' |
| 776 |
data_options = {}
|
|
| 777 |
data_options[:collapse_expand] = "#{object.class}-#{object.id}".downcase if object
|
|
| 754 | 778 | |
| 755 | 779 |
css = "task " + case object |
| 756 | 780 |
when Project |
| ... | ... | |
| 774 | 798 |
html_id = "task-todo-version-#{object.id}" if object.is_a?(Version)
|
| 775 | 799 |
content_opt = {:style => style,
|
| 776 | 800 |
:class => "#{css} task_todo",
|
| 777 |
:id => html_id} |
|
| 801 |
:id => html_id, |
|
| 802 |
:data => {}}
|
|
| 778 | 803 |
if object.is_a?(Issue) |
| 779 | 804 |
rels = issue_relations(object) |
| 780 | 805 |
if rels.present? |
| 781 | 806 |
content_opt[:data] = {"rels" => rels.to_json}
|
| 782 | 807 |
end |
| 783 | 808 |
end |
| 809 |
content_opt[:data].merge!(data_options) |
|
| 784 | 810 |
output << view.content_tag(:div, ' '.html_safe, content_opt) |
| 785 | 811 |
if coords[:bar_late_end] |
| 786 | 812 |
width = coords[:bar_late_end] - coords[:bar_start] - 2 |
| ... | ... | |
| 790 | 816 |
style << "width:#{width}px;"
|
| 791 | 817 |
output << view.content_tag(:div, ' '.html_safe, |
| 792 | 818 |
:style => style, |
| 793 |
:class => "#{css} task_late")
|
|
| 819 |
:class => "#{css} task_late",
|
|
| 820 |
:data => data_options) |
|
| 794 | 821 |
end |
| 795 | 822 |
if coords[:bar_progress_end] |
| 796 | 823 |
width = coords[:bar_progress_end] - coords[:bar_start] - 2 |
| ... | ... | |
| 803 | 830 |
output << view.content_tag(:div, ' '.html_safe, |
| 804 | 831 |
:style => style, |
| 805 | 832 |
:class => "#{css} task_done",
|
| 806 |
:id => html_id) |
|
| 833 |
:id => html_id, |
|
| 834 |
:data => data_options) |
|
| 807 | 835 |
end |
| 808 | 836 |
end |
| 809 | 837 |
# Renders the markers |
| ... | ... | |
| 815 | 843 |
style << "width:15px;" |
| 816 | 844 |
output << view.content_tag(:div, ' '.html_safe, |
| 817 | 845 |
:style => style, |
| 818 |
:class => "#{css} marker starting")
|
|
| 846 |
:class => "#{css} marker starting",
|
|
| 847 |
:data => data_options) |
|
| 819 | 848 |
end |
| 820 | 849 |
if coords[:end] |
| 821 | 850 |
style = "" |
| ... | ... | |
| 824 | 853 |
style << "width:15px;" |
| 825 | 854 |
output << view.content_tag(:div, ' '.html_safe, |
| 826 | 855 |
:style => style, |
| 827 |
:class => "#{css} marker ending")
|
|
| 856 |
:class => "#{css} marker ending",
|
|
| 857 |
:data => data_options) |
|
| 828 | 858 |
end |
| 829 | 859 |
end |
| 830 | 860 |
# Renders the label on the right |
| ... | ... | |
| 835 | 865 |
style << "width:15px;" |
| 836 | 866 |
output << view.content_tag(:div, label, |
| 837 | 867 |
:style => style, |
| 838 |
:class => "#{css} label")
|
|
| 868 |
:class => "#{css} label",
|
|
| 869 |
:data => data_options) |
|
| 839 | 870 |
end |
| 840 | 871 |
# Renders the tooltip |
| 841 | 872 |
if object.is_a?(Issue) && coords[:bar_start] && coords[:bar_end] |
| ... | ... | |
| 851 | 882 |
style << "height:12px;" |
| 852 | 883 |
output << view.content_tag(:div, s.html_safe, |
| 853 | 884 |
:style => style, |
| 854 |
:class => "tooltip hascontextmenu") |
|
| 885 |
:class => "tooltip hascontextmenu", |
|
| 886 |
:data => data_options) |
|
| 855 | 887 |
end |
| 856 | 888 |
@lines << output |
| 857 | 889 |
output |
| public/javascripts/gantt.js | ||
|---|---|---|
| 17 | 17 |
function getRelationsArray() {
|
| 18 | 18 |
var arr = new Array(); |
| 19 | 19 |
$.each($('div.task_todo[data-rels]'), function(index_div, element) {
|
| 20 |
if(!$(element).is(':visible')) return true;
|
|
| 20 | 21 |
var element_id = $(element).attr("id");
|
| 21 | 22 |
if (element_id != null) {
|
| 22 | 23 |
var issue_id = element_id.replace("task-todo-issue-", "");
|
| ... | ... | |
| 106 | 107 |
var today_left = $('#today_line').position().left;
|
| 107 | 108 |
arr.push({left: today_left, top: 0});
|
| 108 | 109 |
$.each($('div.issue-subject, div.version-name'), function(index, element) {
|
| 110 |
if(!$(element).is(':visible')) return true;
|
|
| 109 | 111 |
var t = $(element).position().top - draw_top ; |
| 110 | 112 |
var h = ($(element).height() / 9); |
| 111 | 113 |
var element_top_upper = t - h; |
| ... | ... | |
| 169 | 171 |
draw_gantt = Raphael(folder); |
| 170 | 172 |
setDrawArea(); |
| 171 | 173 |
if ($("#draw_progress_line").prop('checked'))
|
| 172 |
drawGanttProgressLines();
|
|
| 174 |
try{drawGanttProgressLines();}catch(e){}
|
|
| 173 | 175 |
if ($("#draw_relations").prop('checked'))
|
| 174 | 176 |
drawRelations(); |
| 175 | 177 |
} |
| ... | ... | |
| 195 | 197 |
$('td.gantt_subjects_column').resizable('enable');
|
| 196 | 198 |
}; |
| 197 | 199 |
} |
| 200 | ||
| 201 |
ganttEntryClick = function(e){
|
|
| 202 |
var subject = $(e.target.parentElement); |
|
| 203 |
var subject_left = parseInt(subject.css('left'));
|
|
| 204 |
var target_shown = null; |
|
| 205 |
var target_top = 0; |
|
| 206 |
var total_height = 0; |
|
| 207 |
var out_of_hierarchy = false; |
|
| 208 |
var iconChange = null; |
|
| 209 |
if(subject.hasClass('open'))
|
|
| 210 |
iconChange = function(element){
|
|
| 211 |
$(element).removeClass('open');
|
|
| 212 |
}; |
|
| 213 |
else |
|
| 214 |
iconChange = function(element){
|
|
| 215 |
$(element).addClass('open');
|
|
| 216 |
}; |
|
| 217 |
iconChange(subject); |
|
| 218 |
subject.nextAll('div').each(function(_, element){
|
|
| 219 |
var el = $(element); |
|
| 220 |
var json = el.data('collapse-expand');
|
|
| 221 |
if(out_of_hierarchy || parseInt(el.css('left')) <= subject_left){
|
|
| 222 |
out_of_hierarchy = true; |
|
| 223 |
if(target_shown == null) return false; |
|
| 224 | ||
| 225 |
var new_top_val = parseInt(el.css('top')) + total_height * (target_shown ? -1 : 1);
|
|
| 226 |
el.css('top', new_top_val);
|
|
| 227 |
$('#gantt_area form > div[data-collapse-expand="' + json.obj_id + '"]').each(function(_, task){
|
|
| 228 |
$(task).css('top', new_top_val);
|
|
| 229 |
}); |
|
| 230 |
return true; |
|
| 231 |
} |
|
| 232 | ||
| 233 |
var is_shown = el.is(':visible');
|
|
| 234 |
if(target_shown == null){
|
|
| 235 |
target_shown = is_shown; |
|
| 236 |
target_top = parseInt(el.css('top'));
|
|
| 237 |
total_height = 0; |
|
| 238 |
} |
|
| 239 |
if(is_shown == target_shown){
|
|
| 240 |
$('#gantt_area form > div[data-collapse-expand="' + json.obj_id + '"]').each(function(_, task){
|
|
| 241 |
var el_task = $(task); |
|
| 242 |
if(!is_shown) |
|
| 243 |
el_task.css('top', target_top + total_height);
|
|
| 244 |
if(!el_task.hasClass('tooltip'))
|
|
| 245 |
el_task.toggle(!is_shown); |
|
| 246 |
}); |
|
| 247 |
if(!is_shown) |
|
| 248 |
el.css('top', target_top + total_height);
|
|
| 249 |
iconChange(el); |
|
| 250 |
el.toggle(!is_shown); |
|
| 251 |
total_height += parseInt(json.top_increment); |
|
| 252 |
} |
|
| 253 |
}); |
|
| 254 |
drawGanttHandler(); |
|
| 255 |
}; |
|
| public/stylesheets/application.css | ||
|---|---|---|
| 291 | 291 |
tr.entry.file td.filename a { margin-left: 16px; }
|
| 292 | 292 |
tr.entry.file td.filename_no_report a { margin-left: 16px; }
|
| 293 | 293 | |
| 294 |
tr span.expander {background: url(../images/arrow_right.png) no-repeat 2px 50%; padding-left: 8px; margin-left: 0; cursor: pointer;}
|
|
| 295 |
tr.open span.expander {background-image: url(../images/arrow_down.png);}
|
|
| 294 |
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;}
|
|
| 295 |
tr.open span.expander, .gantt_subjects div.open > span.expander {background-image: url(../images/arrow_down.png);}
|
|
| 296 |
.gantt_subjects div > span.expander {padding-left: 12px;}
|
|
| 297 |
.gantt_subjects div > span .icon-gravatar {float: none;}
|
|
| 296 | 298 | |
| 297 | 299 |
tr.changeset { height: 20px }
|
| 298 | 300 |
tr.changeset ul, ol { margin-top: 0px; margin-bottom: 0px; }
|
| test/unit/lib/redmine/helpers/gantt_test.rb | ||
|---|---|---|
| 152 | 152 |
setup_subjects |
| 153 | 153 |
@output_buffer = @gantt.subjects |
| 154 | 154 |
assert_select "div.issue-subject", /#{@issue.subject}/
|
| 155 |
assert_select 'div.issue-subject[style*="left:44px"]' |
|
| 155 |
# subject 56px: 44px + 12px(collapse/expand icon's width) |
|
| 156 |
assert_select 'div.issue-subject[style*="left:56px"]' |
|
| 156 | 157 |
end |
| 157 | 158 | |
| 158 | 159 |
test "#subjects issue assigned to a shared version of another project should be rendered" do |
| ... | ... | |
| 200 | 201 |
assert_select 'div.issue-subject[style*="left:44px"]', /#{@issue.subject}/
|
| 201 | 202 |
# children 64px |
| 202 | 203 |
assert_select 'div.issue-subject[style*="left:64px"]', /child1/ |
| 203 |
assert_select 'div.issue-subject[style*="left:64px"]', /child2/ |
|
| 204 |
# grandchild 84px |
|
| 205 |
assert_select 'div.issue-subject[style*="left:84px"]', /grandchild/, @output_buffer |
|
| 204 |
# children 76px: 64px + 12px(collapse/expand icon's width) |
|
| 205 |
assert_select 'div.issue-subject[style*="left:76px"]', /child2/ |
|
| 206 |
# grandchild 96px: 84px + 12px(collapse/expand icon's width) |
|
| 207 |
assert_select 'div.issue-subject[style*="left:96px"]', /grandchild/, @output_buffer |
|
| 206 | 208 |
end |
| 207 | 209 | |
| 208 | 210 |
test "#lines" do |
| ... | ... | |
| 298 | 300 |
test "#subject should use the indent option to move the div to the right" do |
| 299 | 301 |
create_gantt |
| 300 | 302 |
@output_buffer = @gantt.subject('subject', :format => :html, :indent => 40)
|
| 301 |
assert_select 'div[style*="left:40"]' |
|
| 303 |
# subject 52px: 40px(indent) + 12px(collapse/expand icon's width) |
|
| 304 |
assert_select 'div[style*="left:52px"]' |
|
| 302 | 305 |
end |
| 303 | 306 | |
| 304 | 307 |
test "#line_for_project" do |
- « Previous
- 1
- …
- 4
- 5
- 6
- Next »