widget_selectioncontainer.js
324 lines
| 11.7 KiB
| application/javascript
|
JavascriptLexer
|
r17198 | // Copyright (c) IPython Development Team. | ||
// Distributed under the terms of the Modified BSD License. | ||||
|
r14366 | |||
|
r17198 | define([ | ||
"widgets/js/widget", | ||||
|
r17199 | "base/js/utils", | ||
|
r17216 | "jquery", | ||
|
r17312 | "bootstrap", | ||
|
r17216 | ], function(widget, utils, $){ | ||
|
r14366 | |||
|
r17198 | var AccordionView = widget.DOMWidgetView.extend({ | ||
|
r19023 | initialize: function(){ | ||
AccordionView.__super__.initialize.apply(this, arguments); | ||||
this.containers = []; | ||||
this.model_containers = {}; | ||||
|
r19067 | this.children_views = new widget.ViewList(this.add_child_view, this.remove_child_view, this); | ||
|
r19023 | this.listenTo(this.model, 'change:children', function(model, value) { | ||
this.children_views.update(value); | ||||
}, this); | ||||
}, | ||||
|
r14284 | render: function(){ | ||
|
r19176 | /** | ||
* Called when view is rendered. | ||||
*/ | ||||
|
r17199 | var guid = 'panel-group' + utils.uuid(); | ||
|
r14455 | this.$el | ||
.attr('id', guid) | ||||
|
r16913 | .addClass('panel-group'); | ||
|
r15950 | this.model.on('change:selected_index', function(model, value, options) { | ||
|
r19773 | this.update_selected_index(options); | ||
|
r15950 | }, this); | ||
this.model.on('change:_titles', function(model, value, options) { | ||||
|
r19773 | this.update_titles(options); | ||
|
r15950 | }, this); | ||
|
r17179 | this.on('displayed', function() { | ||
|
r15955 | this.update_titles(); | ||
|
r17354 | }, this); | ||
|
r19023 | this.children_views.update(this.model.get('children')); | ||
|
r14503 | }, | ||
|
r14284 | |||
|
r19773 | /** | ||
* Update the contents of this view | ||||
* | ||||
* Called when the model is changed. The model may have been | ||||
* changed by another view or by a state update from the back-end. | ||||
*/ | ||||
update: function(options) { | ||||
this.update_titles(); | ||||
this.update_selected_index(options); | ||||
return TabView.__super__.update.apply(this); | ||||
}, | ||||
update_titles: function() { | ||||
|
r19176 | /** | ||
* Set tab titles | ||||
*/ | ||||
|
r19773 | var titles = this.model.get('_titles'); | ||
|
r15950 | var that = this; | ||
_.each(titles, function(title, page_index) { | ||||
var accordian = that.containers[page_index]; | ||||
if (accordian !== undefined) { | ||||
accordian | ||||
|
r16913 | .find('.panel-heading') | ||
|
r15950 | .find('.accordion-toggle') | ||
.text(title); | ||||
} | ||||
}); | ||||
}, | ||||
|
r19773 | update_selected_index: function(options) { | ||
|
r19176 | /** | ||
* Only update the selection if the selection wasn't triggered | ||||
* by the front-end. It must be triggered by the back-end. | ||||
*/ | ||||
|
r15950 | if (options === undefined || options.updated_view != this) { | ||
|
r19773 | var old_index = this.model.previous('selected_index'); | ||
var new_index = this.model.get('selected_index'); | ||||
|
r16913 | this.containers[old_index].find('.panel-collapse').collapse('hide'); | ||
|
r15950 | if (0 <= new_index && new_index < this.containers.length) { | ||
|
r16913 | this.containers[new_index].find('.panel-collapse').collapse('show'); | ||
|
r14370 | } | ||
} | ||||
|
r14284 | }, | ||
|
r19067 | remove_child_view: function(view) { | ||
|
r19176 | /** | ||
* Called when a child is removed from children list. | ||||
* TODO: does this handle two different views of the same model as children? | ||||
*/ | ||||
|
r19067 | var model = view.model; | ||
|
r14598 | var accordion_group = this.model_containers[model.id]; | ||
this.containers.splice(accordion_group.container_index, 1); | ||||
delete this.model_containers[model.id]; | ||||
accordion_group.remove(); | ||||
}, | ||||
|
r14284 | |||
|
r19067 | add_child_view: function(model) { | ||
|
r19176 | /** | ||
* Called when a child is added to children list. | ||||
*/ | ||||
|
r14284 | var index = this.containers.length; | ||
|
r17199 | var uuid = utils.uuid(); | ||
|
r14284 | var accordion_group = $('<div />') | ||
|
r16913 | .addClass('panel panel-default') | ||
|
r14284 | .appendTo(this.$el); | ||
var accordion_heading = $('<div />') | ||||
|
r16913 | .addClass('panel-heading') | ||
|
r14284 | .appendTo(accordion_group); | ||
|
r14370 | var that = this; | ||
|
r14284 | var accordion_toggle = $('<a />') | ||
.addClass('accordion-toggle') | ||||
.attr('data-toggle', 'collapse') | ||||
.attr('data-parent', '#' + this.$el.attr('id')) | ||||
.attr('href', '#' + uuid) | ||||
|
r14370 | .click(function(evt){ | ||
|
r14569 | |||
// Calling model.set will trigger all of the other views of the | ||||
// model to update. | ||||
|
r15950 | that.model.set("selected_index", index, {updated_view: that}); | ||
|
r14482 | that.touch(); | ||
|
r14370 | }) | ||
|
r14663 | .text('Page ' + index) | ||
|
r14284 | .appendTo(accordion_heading); | ||
var accordion_body = $('<div />', {id: uuid}) | ||||
|
r16913 | .addClass('panel-collapse collapse') | ||
|
r14284 | .appendTo(accordion_group); | ||
var accordion_inner = $('<div />') | ||||
|
r16913 | .addClass('panel-body') | ||
|
r14284 | .appendTo(accordion_body); | ||
|
r14598 | var container_index = this.containers.push(accordion_group) - 1; | ||
accordion_group.container_index = container_index; | ||||
this.model_containers[model.id] = accordion_group; | ||||
|
r18142 | |||
|
r18883 | var dummy = $('<div/>'); | ||
accordion_inner.append(dummy); | ||||
|
r18896 | return this.create_child_view(model).then(function(view) { | ||
|
r18883 | dummy.replaceWith(view.$el); | ||
|
r18142 | that.update(); | ||
that.update_titles(); | ||||
|
r16660 | |||
|
r18142 | // Trigger the displayed event of the child view. | ||
that.after_displayed(function() { | ||||
view.trigger('displayed'); | ||||
}); | ||||
|
r18896 | return view; | ||
|
r19080 | }).catch(utils.reject("Couldn't add child view to box", true)); | ||
|
r14284 | }, | ||
|
r19023 | |||
remove: function() { | ||||
|
r19176 | /** | ||
* We remove this widget before removing the children as an optimization | ||||
* we want to remove the entire container from the DOM first before | ||||
* removing each individual child separately. | ||||
*/ | ||||
|
r19023 | AccordionView.__super__.remove.apply(this, arguments); | ||
this.children_views.remove(); | ||||
}, | ||||
|
r14284 | }); | ||
|
r14290 | |||
|
r14609 | |||
|
r17198 | var TabView = widget.DOMWidgetView.extend({ | ||
|
r14507 | initialize: function() { | ||
|
r19176 | /** | ||
* Public constructor. | ||||
*/ | ||||
|
r14583 | TabView.__super__.initialize.apply(this, arguments); | ||
|
r19023 | |||
this.containers = []; | ||||
|
r19067 | this.children_views = new widget.ViewList(this.add_child_view, this.remove_child_view, this); | ||
|
r19023 | this.listenTo(this.model, 'change:children', function(model, value) { | ||
this.children_views.update(value); | ||||
}, this); | ||||
|
r14507 | }, | ||
|
r14486 | |||
|
r14290 | render: function(){ | ||
|
r19176 | /** | ||
* Called when view is rendered. | ||||
*/ | ||||
|
r17199 | var uuid = 'tabs'+utils.uuid(); | ||
|
r14290 | this.$tabs = $('<div />', {id: uuid}) | ||
.addClass('nav') | ||||
.addClass('nav-tabs') | ||||
.appendTo(this.$el); | ||||
this.$tab_contents = $('<div />', {id: uuid + 'Content'}) | ||||
.addClass('tab-content') | ||||
.appendTo(this.$el); | ||||
|
r19023 | this.children_views.update(this.model.get('children')); | ||
|
r14507 | }, | ||
|
r14609 | |||
|
r17724 | update_attr: function(name, value) { | ||
|
r19176 | /** | ||
* Set a css attr of the widget view. | ||||
*/ | ||||
|
r21464 | if (['padding', 'margin', 'height', 'width'].indexOf(name) !== -1) { | ||
|
r19366 | this.$el.css(name, value); | ||
} else { | ||||
this.$tabs.css(name, value); | ||||
} | ||||
|
r17724 | }, | ||
|
r19067 | remove_child_view: function(view) { | ||
|
r19176 | /** | ||
* Called when a child is removed from children list. | ||||
*/ | ||||
|
r14598 | this.containers.splice(view.parent_tab.tab_text_index, 1); | ||
view.parent_tab.remove(); | ||||
view.parent_container.remove(); | ||||
view.remove(); | ||||
|
r14290 | }, | ||
|
r19067 | add_child_view: function(model) { | ||
|
r19176 | /** | ||
* Called when a child is added to children list. | ||||
*/ | ||||
|
r14290 | var index = this.containers.length; | ||
|
r17199 | var uuid = utils.uuid(); | ||
|
r14290 | |||
var that = this; | ||||
var tab = $('<li />') | ||||
.css('list-style-type', 'none') | ||||
.appendTo(this.$tabs); | ||||
|
r18142 | |||
|
r18883 | var tab_text = $('<a />') | ||
.attr('href', '#' + uuid) | ||||
.attr('data-toggle', 'tab') | ||||
.text('Page ' + index) | ||||
.appendTo(tab) | ||||
.click(function (e) { | ||||
// Calling model.set will trigger all of the other views of the | ||||
// model to update. | ||||
that.model.set("selected_index", index, {updated_view: that}); | ||||
that.touch(); | ||||
that.select_page(index); | ||||
}); | ||||
tab.tab_text_index = that.containers.push(tab_text) - 1; | ||||
var dummy = $('<div />'); | ||||
var contents_div = $('<div />', {id: uuid}) | ||||
.addClass('tab-pane') | ||||
.addClass('fade') | ||||
.append(dummy) | ||||
.appendTo(that.$tab_contents); | ||||
|
r19773 | this.update(); | ||
|
r18896 | return this.create_child_view(model).then(function(view) { | ||
|
r18883 | dummy.replaceWith(view.$el); | ||
view.parent_tab = tab; | ||||
|
r18142 | view.parent_container = contents_div; | ||
// Trigger the displayed event of the child view. | ||||
that.after_displayed(function() { | ||||
view.trigger('displayed'); | ||||
|
r19773 | that.update(); | ||
|
r14290 | }); | ||
|
r18896 | return view; | ||
|
r19080 | }).catch(utils.reject("Couldn't add child view to box", true)); | ||
|
r14598 | }, | ||
update: function(options) { | ||||
|
r19176 | /** | ||
* Update the contents of this view | ||||
* | ||||
* Called when the model is changed. The model may have been | ||||
* changed by another view or by a state update from the back-end. | ||||
*/ | ||||
|
r19773 | this.update_titles(); | ||
this.update_selected_index(options); | ||||
return TabView.__super__.update.apply(this); | ||||
}, | ||||
|
r14598 | |||
|
r19773 | /** | ||
* Updates the tab page titles. | ||||
*/ | ||||
update_titles: function() { | ||||
var titles = this.model.get('_titles'); | ||||
var that = this; | ||||
_.each(titles, function(title, page_index) { | ||||
var tab_text = that.containers[page_index]; | ||||
if (tab_text !== undefined) { | ||||
tab_text.text(title); | ||||
} | ||||
}); | ||||
}, | ||||
/** | ||||
* Updates the tab page titles. | ||||
*/ | ||||
update_selected_index: function(options) { | ||||
if (options === undefined || options.updated_view != this) { | ||||
|
r14598 | var selected_index = this.model.get('selected_index'); | ||
if (0 <= selected_index && selected_index < this.containers.length) { | ||||
this.select_page(selected_index); | ||||
} | ||||
} | ||||
|
r14290 | }, | ||
|
r14307 | |||
select_page: function(index) { | ||||
|
r19176 | /** | ||
* Select a page. | ||||
*/ | ||||
|
r14307 | this.$tabs.find('li') | ||
.removeClass('active'); | ||||
this.containers[index].tab('show'); | ||||
}, | ||||
|
r19023 | |||
remove: function() { | ||||
|
r19176 | /** | ||
* We remove this widget before removing the children as an optimization | ||||
* we want to remove the entire container from the DOM first before | ||||
* removing each individual child separately. | ||||
*/ | ||||
|
r19023 | TabView.__super__.remove.apply(this, arguments); | ||
this.children_views.remove(); | ||||
}, | ||||
|
r14290 | }); | ||
|
r17198 | |||
return { | ||||
'AccordionView': AccordionView, | ||||
'TabView': TabView, | ||||
}; | ||||
|
r14290 | }); | ||