Feature #6417 » 6417-collapse-expand-gantt-v2.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 | ||
---|---|---|
195 | 195 |
options = {:top => 0, :top_increment => 20, |
196 | 196 |
:indent_increment => 20, :render => :subject, |
197 | 197 |
:format => :html}.merge(options) |
198 |
options[:indent_increment ] += 12 if options[:format] == :html |
|
198 | 199 |
indent = options[:indent] || 4 |
199 | 200 |
@subjects = '' unless options[:only] == :lines |
200 | 201 |
@lines = '' unless options[:only] == :subjects |
... | ... | |
221 | 222 |
# then render project versions and their issues |
222 | 223 |
versions = project_versions(project) |
223 | 224 |
self.class.sort_versions!(versions) |
225 |
indent = options[:indent] |
|
224 | 226 |
versions.each do |version| |
227 |
options[:indent] = indent |
|
225 | 228 |
render_version(project, version, options) |
226 | 229 |
end |
227 | 230 |
end |
... | ... | |
698 | 701 |
end |
699 | 702 | |
700 | 703 |
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 | 704 |
content = html_subject_content(object) || subject |
704 |
tag_options = {:style => style}
|
|
705 |
tag_options = {} |
|
705 | 706 |
case object |
706 | 707 |
when Issue |
707 | 708 |
tag_options[:id] = "issue-#{object.id}" |
708 | 709 |
tag_options[:class] = "issue-subject hascontextmenu" |
709 | 710 |
tag_options[:title] = object.subject |
711 |
children = object.children & project_issues(object.project) |
|
712 |
has_children = children.present? && (children.collect(&:fixed_version).uniq & [object.fixed_version]).present? |
|
710 | 713 |
when Version |
711 | 714 |
tag_options[:id] = "version-#{object.id}" |
712 | 715 |
tag_options[:class] = "version-name" |
716 |
has_children = object.fixed_issues.exists? |
|
713 | 717 |
when Project |
714 | 718 |
tag_options[:class] = "project-name" |
719 |
has_children = object.issues.exists? || object.versions.exists? |
|
720 |
end |
|
721 |
tag_options[:data] = { |
|
722 |
:collapse_expand => { |
|
723 |
:top_increment => params[:top_increment], |
|
724 |
:obj_id => "#{object.class}-#{object.id}".downcase, |
|
725 |
}, |
|
726 |
} |
|
727 |
if has_children |
|
728 |
content = view.content_tag(:span, nil, :class => :expander) + content |
|
729 |
params = params.dup |
|
730 |
params[:indent] -= 12 if params[:indent] >= 12 |
|
731 |
tag_options[:class] << ' open' |
|
715 | 732 |
end |
733 |
style = "position: absolute;top:#{params[:top]}px;left:#{params[:indent]}px;" |
|
734 |
style << "width:#{params[:subject_width] - params[:indent]}px;" if params[:subject_width] |
|
735 |
tag_options[:style] = style |
|
716 | 736 |
output = view.content_tag(:div, content, tag_options) |
717 | 737 |
@subjects << output |
718 | 738 |
output |
... | ... | |
751 | 771 | |
752 | 772 |
def html_task(params, coords, markers, label, object) |
753 | 773 |
output = '' |
774 |
data_options = { |
|
775 |
:collapse_expand => "#{object.class}-#{object.id}".downcase, |
|
776 |
} |
|
754 | 777 | |
755 | 778 |
css = "task " + case object |
756 | 779 |
when Project |
... | ... | |
774 | 797 |
html_id = "task-todo-version-#{object.id}" if object.is_a?(Version) |
775 | 798 |
content_opt = {:style => style, |
776 | 799 |
:class => "#{css} task_todo", |
777 |
:id => html_id} |
|
800 |
:id => html_id, |
|
801 |
:data => {}} |
|
778 | 802 |
if object.is_a?(Issue) |
779 | 803 |
rels = issue_relations(object) |
780 | 804 |
if rels.present? |
781 | 805 |
content_opt[:data] = {"rels" => rels.to_json} |
782 | 806 |
end |
783 | 807 |
end |
808 |
content_opt[:data].merge!(data_options) |
|
784 | 809 |
output << view.content_tag(:div, ' '.html_safe, content_opt) |
785 | 810 |
if coords[:bar_late_end] |
786 | 811 |
width = coords[:bar_late_end] - coords[:bar_start] - 2 |
... | ... | |
790 | 815 |
style << "width:#{width}px;" |
791 | 816 |
output << view.content_tag(:div, ' '.html_safe, |
792 | 817 |
:style => style, |
793 |
:class => "#{css} task_late") |
|
818 |
:class => "#{css} task_late", |
|
819 |
:data => data_options) |
|
794 | 820 |
end |
795 | 821 |
if coords[:bar_progress_end] |
796 | 822 |
width = coords[:bar_progress_end] - coords[:bar_start] - 2 |
... | ... | |
803 | 829 |
output << view.content_tag(:div, ' '.html_safe, |
804 | 830 |
:style => style, |
805 | 831 |
:class => "#{css} task_done", |
806 |
:id => html_id) |
|
832 |
:id => html_id, |
|
833 |
:data => data_options) |
|
807 | 834 |
end |
808 | 835 |
end |
809 | 836 |
# Renders the markers |
... | ... | |
815 | 842 |
style << "width:15px;" |
816 | 843 |
output << view.content_tag(:div, ' '.html_safe, |
817 | 844 |
:style => style, |
818 |
:class => "#{css} marker starting") |
|
845 |
:class => "#{css} marker starting", |
|
846 |
:data => data_options) |
|
819 | 847 |
end |
820 | 848 |
if coords[:end] |
821 | 849 |
style = "" |
... | ... | |
824 | 852 |
style << "width:15px;" |
825 | 853 |
output << view.content_tag(:div, ' '.html_safe, |
826 | 854 |
:style => style, |
827 |
:class => "#{css} marker ending") |
|
855 |
:class => "#{css} marker ending", |
|
856 |
:data => data_options) |
|
828 | 857 |
end |
829 | 858 |
end |
830 | 859 |
# Renders the label on the right |
... | ... | |
835 | 864 |
style << "width:15px;" |
836 | 865 |
output << view.content_tag(:div, label, |
837 | 866 |
:style => style, |
838 |
:class => "#{css} label") |
|
867 |
:class => "#{css} label", |
|
868 |
:data => data_options) |
|
839 | 869 |
end |
840 | 870 |
# Renders the tooltip |
841 | 871 |
if object.is_a?(Issue) && coords[:bar_start] && coords[:bar_end] |
... | ... | |
851 | 881 |
style << "height:12px;" |
852 | 882 |
output << view.content_tag(:div, s.html_safe, |
853 | 883 |
:style => style, |
854 |
:class => "tooltip hascontextmenu") |
|
884 |
:class => "tooltip hascontextmenu", |
|
885 |
:data => data_options) |
|
855 | 886 |
end |
856 | 887 |
@lines << output |
857 | 888 |
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; } |