##// END OF EJS Templates
Merge pull request #8550 from SylvainCorlay/Backport_ipython_ipywidets#22...
Min RK -
r21466:ded6199b merge
parent child Browse files
Show More
@@ -1,324 +1,324 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 define([
4 define([
5 "widgets/js/widget",
5 "widgets/js/widget",
6 "base/js/utils",
6 "base/js/utils",
7 "jquery",
7 "jquery",
8 "bootstrap",
8 "bootstrap",
9 ], function(widget, utils, $){
9 ], function(widget, utils, $){
10
10
11 var AccordionView = widget.DOMWidgetView.extend({
11 var AccordionView = widget.DOMWidgetView.extend({
12 initialize: function(){
12 initialize: function(){
13 AccordionView.__super__.initialize.apply(this, arguments);
13 AccordionView.__super__.initialize.apply(this, arguments);
14
14
15 this.containers = [];
15 this.containers = [];
16 this.model_containers = {};
16 this.model_containers = {};
17 this.children_views = new widget.ViewList(this.add_child_view, this.remove_child_view, this);
17 this.children_views = new widget.ViewList(this.add_child_view, this.remove_child_view, this);
18 this.listenTo(this.model, 'change:children', function(model, value) {
18 this.listenTo(this.model, 'change:children', function(model, value) {
19 this.children_views.update(value);
19 this.children_views.update(value);
20 }, this);
20 }, this);
21 },
21 },
22
22
23 render: function(){
23 render: function(){
24 /**
24 /**
25 * Called when view is rendered.
25 * Called when view is rendered.
26 */
26 */
27 var guid = 'panel-group' + utils.uuid();
27 var guid = 'panel-group' + utils.uuid();
28 this.$el
28 this.$el
29 .attr('id', guid)
29 .attr('id', guid)
30 .addClass('panel-group');
30 .addClass('panel-group');
31 this.model.on('change:selected_index', function(model, value, options) {
31 this.model.on('change:selected_index', function(model, value, options) {
32 this.update_selected_index(options);
32 this.update_selected_index(options);
33 }, this);
33 }, this);
34 this.model.on('change:_titles', function(model, value, options) {
34 this.model.on('change:_titles', function(model, value, options) {
35 this.update_titles(options);
35 this.update_titles(options);
36 }, this);
36 }, this);
37 this.on('displayed', function() {
37 this.on('displayed', function() {
38 this.update_titles();
38 this.update_titles();
39 }, this);
39 }, this);
40 this.children_views.update(this.model.get('children'));
40 this.children_views.update(this.model.get('children'));
41 },
41 },
42
42
43 /**
43 /**
44 * Update the contents of this view
44 * Update the contents of this view
45 *
45 *
46 * Called when the model is changed. The model may have been
46 * Called when the model is changed. The model may have been
47 * changed by another view or by a state update from the back-end.
47 * changed by another view or by a state update from the back-end.
48 */
48 */
49 update: function(options) {
49 update: function(options) {
50 this.update_titles();
50 this.update_titles();
51 this.update_selected_index(options);
51 this.update_selected_index(options);
52 return TabView.__super__.update.apply(this);
52 return TabView.__super__.update.apply(this);
53 },
53 },
54
54
55 update_titles: function() {
55 update_titles: function() {
56 /**
56 /**
57 * Set tab titles
57 * Set tab titles
58 */
58 */
59 var titles = this.model.get('_titles');
59 var titles = this.model.get('_titles');
60 var that = this;
60 var that = this;
61 _.each(titles, function(title, page_index) {
61 _.each(titles, function(title, page_index) {
62 var accordian = that.containers[page_index];
62 var accordian = that.containers[page_index];
63 if (accordian !== undefined) {
63 if (accordian !== undefined) {
64 accordian
64 accordian
65 .find('.panel-heading')
65 .find('.panel-heading')
66 .find('.accordion-toggle')
66 .find('.accordion-toggle')
67 .text(title);
67 .text(title);
68 }
68 }
69 });
69 });
70 },
70 },
71
71
72 update_selected_index: function(options) {
72 update_selected_index: function(options) {
73 /**
73 /**
74 * Only update the selection if the selection wasn't triggered
74 * Only update the selection if the selection wasn't triggered
75 * by the front-end. It must be triggered by the back-end.
75 * by the front-end. It must be triggered by the back-end.
76 */
76 */
77 if (options === undefined || options.updated_view != this) {
77 if (options === undefined || options.updated_view != this) {
78 var old_index = this.model.previous('selected_index');
78 var old_index = this.model.previous('selected_index');
79 var new_index = this.model.get('selected_index');
79 var new_index = this.model.get('selected_index');
80 this.containers[old_index].find('.panel-collapse').collapse('hide');
80 this.containers[old_index].find('.panel-collapse').collapse('hide');
81 if (0 <= new_index && new_index < this.containers.length) {
81 if (0 <= new_index && new_index < this.containers.length) {
82 this.containers[new_index].find('.panel-collapse').collapse('show');
82 this.containers[new_index].find('.panel-collapse').collapse('show');
83 }
83 }
84 }
84 }
85 },
85 },
86
86
87 remove_child_view: function(view) {
87 remove_child_view: function(view) {
88 /**
88 /**
89 * Called when a child is removed from children list.
89 * Called when a child is removed from children list.
90 * TODO: does this handle two different views of the same model as children?
90 * TODO: does this handle two different views of the same model as children?
91 */
91 */
92 var model = view.model;
92 var model = view.model;
93 var accordion_group = this.model_containers[model.id];
93 var accordion_group = this.model_containers[model.id];
94 this.containers.splice(accordion_group.container_index, 1);
94 this.containers.splice(accordion_group.container_index, 1);
95 delete this.model_containers[model.id];
95 delete this.model_containers[model.id];
96 accordion_group.remove();
96 accordion_group.remove();
97 },
97 },
98
98
99 add_child_view: function(model) {
99 add_child_view: function(model) {
100 /**
100 /**
101 * Called when a child is added to children list.
101 * Called when a child is added to children list.
102 */
102 */
103 var index = this.containers.length;
103 var index = this.containers.length;
104 var uuid = utils.uuid();
104 var uuid = utils.uuid();
105 var accordion_group = $('<div />')
105 var accordion_group = $('<div />')
106 .addClass('panel panel-default')
106 .addClass('panel panel-default')
107 .appendTo(this.$el);
107 .appendTo(this.$el);
108 var accordion_heading = $('<div />')
108 var accordion_heading = $('<div />')
109 .addClass('panel-heading')
109 .addClass('panel-heading')
110 .appendTo(accordion_group);
110 .appendTo(accordion_group);
111 var that = this;
111 var that = this;
112 var accordion_toggle = $('<a />')
112 var accordion_toggle = $('<a />')
113 .addClass('accordion-toggle')
113 .addClass('accordion-toggle')
114 .attr('data-toggle', 'collapse')
114 .attr('data-toggle', 'collapse')
115 .attr('data-parent', '#' + this.$el.attr('id'))
115 .attr('data-parent', '#' + this.$el.attr('id'))
116 .attr('href', '#' + uuid)
116 .attr('href', '#' + uuid)
117 .click(function(evt){
117 .click(function(evt){
118
118
119 // Calling model.set will trigger all of the other views of the
119 // Calling model.set will trigger all of the other views of the
120 // model to update.
120 // model to update.
121 that.model.set("selected_index", index, {updated_view: that});
121 that.model.set("selected_index", index, {updated_view: that});
122 that.touch();
122 that.touch();
123 })
123 })
124 .text('Page ' + index)
124 .text('Page ' + index)
125 .appendTo(accordion_heading);
125 .appendTo(accordion_heading);
126 var accordion_body = $('<div />', {id: uuid})
126 var accordion_body = $('<div />', {id: uuid})
127 .addClass('panel-collapse collapse')
127 .addClass('panel-collapse collapse')
128 .appendTo(accordion_group);
128 .appendTo(accordion_group);
129 var accordion_inner = $('<div />')
129 var accordion_inner = $('<div />')
130 .addClass('panel-body')
130 .addClass('panel-body')
131 .appendTo(accordion_body);
131 .appendTo(accordion_body);
132 var container_index = this.containers.push(accordion_group) - 1;
132 var container_index = this.containers.push(accordion_group) - 1;
133 accordion_group.container_index = container_index;
133 accordion_group.container_index = container_index;
134 this.model_containers[model.id] = accordion_group;
134 this.model_containers[model.id] = accordion_group;
135
135
136 var dummy = $('<div/>');
136 var dummy = $('<div/>');
137 accordion_inner.append(dummy);
137 accordion_inner.append(dummy);
138 return this.create_child_view(model).then(function(view) {
138 return this.create_child_view(model).then(function(view) {
139 dummy.replaceWith(view.$el);
139 dummy.replaceWith(view.$el);
140 that.update();
140 that.update();
141 that.update_titles();
141 that.update_titles();
142
142
143 // Trigger the displayed event of the child view.
143 // Trigger the displayed event of the child view.
144 that.after_displayed(function() {
144 that.after_displayed(function() {
145 view.trigger('displayed');
145 view.trigger('displayed');
146 });
146 });
147 return view;
147 return view;
148 }).catch(utils.reject("Couldn't add child view to box", true));
148 }).catch(utils.reject("Couldn't add child view to box", true));
149 },
149 },
150
150
151 remove: function() {
151 remove: function() {
152 /**
152 /**
153 * We remove this widget before removing the children as an optimization
153 * We remove this widget before removing the children as an optimization
154 * we want to remove the entire container from the DOM first before
154 * we want to remove the entire container from the DOM first before
155 * removing each individual child separately.
155 * removing each individual child separately.
156 */
156 */
157 AccordionView.__super__.remove.apply(this, arguments);
157 AccordionView.__super__.remove.apply(this, arguments);
158 this.children_views.remove();
158 this.children_views.remove();
159 },
159 },
160 });
160 });
161
161
162
162
163 var TabView = widget.DOMWidgetView.extend({
163 var TabView = widget.DOMWidgetView.extend({
164 initialize: function() {
164 initialize: function() {
165 /**
165 /**
166 * Public constructor.
166 * Public constructor.
167 */
167 */
168 TabView.__super__.initialize.apply(this, arguments);
168 TabView.__super__.initialize.apply(this, arguments);
169
169
170 this.containers = [];
170 this.containers = [];
171 this.children_views = new widget.ViewList(this.add_child_view, this.remove_child_view, this);
171 this.children_views = new widget.ViewList(this.add_child_view, this.remove_child_view, this);
172 this.listenTo(this.model, 'change:children', function(model, value) {
172 this.listenTo(this.model, 'change:children', function(model, value) {
173 this.children_views.update(value);
173 this.children_views.update(value);
174 }, this);
174 }, this);
175 },
175 },
176
176
177 render: function(){
177 render: function(){
178 /**
178 /**
179 * Called when view is rendered.
179 * Called when view is rendered.
180 */
180 */
181 var uuid = 'tabs'+utils.uuid();
181 var uuid = 'tabs'+utils.uuid();
182 this.$tabs = $('<div />', {id: uuid})
182 this.$tabs = $('<div />', {id: uuid})
183 .addClass('nav')
183 .addClass('nav')
184 .addClass('nav-tabs')
184 .addClass('nav-tabs')
185 .appendTo(this.$el);
185 .appendTo(this.$el);
186 this.$tab_contents = $('<div />', {id: uuid + 'Content'})
186 this.$tab_contents = $('<div />', {id: uuid + 'Content'})
187 .addClass('tab-content')
187 .addClass('tab-content')
188 .appendTo(this.$el);
188 .appendTo(this.$el);
189 this.children_views.update(this.model.get('children'));
189 this.children_views.update(this.model.get('children'));
190 },
190 },
191
191
192 update_attr: function(name, value) {
192 update_attr: function(name, value) {
193 /**
193 /**
194 * Set a css attr of the widget view.
194 * Set a css attr of the widget view.
195 */
195 */
196 if (name == 'padding' || name == 'margin') {
196 if (['padding', 'margin', 'height', 'width'].indexOf(name) !== -1) {
197 this.$el.css(name, value);
197 this.$el.css(name, value);
198 } else {
198 } else {
199 this.$tabs.css(name, value);
199 this.$tabs.css(name, value);
200 }
200 }
201 },
201 },
202
202
203 remove_child_view: function(view) {
203 remove_child_view: function(view) {
204 /**
204 /**
205 * Called when a child is removed from children list.
205 * Called when a child is removed from children list.
206 */
206 */
207 this.containers.splice(view.parent_tab.tab_text_index, 1);
207 this.containers.splice(view.parent_tab.tab_text_index, 1);
208 view.parent_tab.remove();
208 view.parent_tab.remove();
209 view.parent_container.remove();
209 view.parent_container.remove();
210 view.remove();
210 view.remove();
211 },
211 },
212
212
213 add_child_view: function(model) {
213 add_child_view: function(model) {
214 /**
214 /**
215 * Called when a child is added to children list.
215 * Called when a child is added to children list.
216 */
216 */
217 var index = this.containers.length;
217 var index = this.containers.length;
218 var uuid = utils.uuid();
218 var uuid = utils.uuid();
219
219
220 var that = this;
220 var that = this;
221 var tab = $('<li />')
221 var tab = $('<li />')
222 .css('list-style-type', 'none')
222 .css('list-style-type', 'none')
223 .appendTo(this.$tabs);
223 .appendTo(this.$tabs);
224
224
225 var tab_text = $('<a />')
225 var tab_text = $('<a />')
226 .attr('href', '#' + uuid)
226 .attr('href', '#' + uuid)
227 .attr('data-toggle', 'tab')
227 .attr('data-toggle', 'tab')
228 .text('Page ' + index)
228 .text('Page ' + index)
229 .appendTo(tab)
229 .appendTo(tab)
230 .click(function (e) {
230 .click(function (e) {
231
231
232 // Calling model.set will trigger all of the other views of the
232 // Calling model.set will trigger all of the other views of the
233 // model to update.
233 // model to update.
234 that.model.set("selected_index", index, {updated_view: that});
234 that.model.set("selected_index", index, {updated_view: that});
235 that.touch();
235 that.touch();
236 that.select_page(index);
236 that.select_page(index);
237 });
237 });
238 tab.tab_text_index = that.containers.push(tab_text) - 1;
238 tab.tab_text_index = that.containers.push(tab_text) - 1;
239
239
240 var dummy = $('<div />');
240 var dummy = $('<div />');
241 var contents_div = $('<div />', {id: uuid})
241 var contents_div = $('<div />', {id: uuid})
242 .addClass('tab-pane')
242 .addClass('tab-pane')
243 .addClass('fade')
243 .addClass('fade')
244 .append(dummy)
244 .append(dummy)
245 .appendTo(that.$tab_contents);
245 .appendTo(that.$tab_contents);
246
246
247 this.update();
247 this.update();
248 return this.create_child_view(model).then(function(view) {
248 return this.create_child_view(model).then(function(view) {
249 dummy.replaceWith(view.$el);
249 dummy.replaceWith(view.$el);
250 view.parent_tab = tab;
250 view.parent_tab = tab;
251 view.parent_container = contents_div;
251 view.parent_container = contents_div;
252
252
253 // Trigger the displayed event of the child view.
253 // Trigger the displayed event of the child view.
254 that.after_displayed(function() {
254 that.after_displayed(function() {
255 view.trigger('displayed');
255 view.trigger('displayed');
256 that.update();
256 that.update();
257 });
257 });
258 return view;
258 return view;
259 }).catch(utils.reject("Couldn't add child view to box", true));
259 }).catch(utils.reject("Couldn't add child view to box", true));
260 },
260 },
261
261
262 update: function(options) {
262 update: function(options) {
263 /**
263 /**
264 * Update the contents of this view
264 * Update the contents of this view
265 *
265 *
266 * Called when the model is changed. The model may have been
266 * Called when the model is changed. The model may have been
267 * changed by another view or by a state update from the back-end.
267 * changed by another view or by a state update from the back-end.
268 */
268 */
269 this.update_titles();
269 this.update_titles();
270 this.update_selected_index(options);
270 this.update_selected_index(options);
271 return TabView.__super__.update.apply(this);
271 return TabView.__super__.update.apply(this);
272 },
272 },
273
273
274 /**
274 /**
275 * Updates the tab page titles.
275 * Updates the tab page titles.
276 */
276 */
277 update_titles: function() {
277 update_titles: function() {
278 var titles = this.model.get('_titles');
278 var titles = this.model.get('_titles');
279 var that = this;
279 var that = this;
280 _.each(titles, function(title, page_index) {
280 _.each(titles, function(title, page_index) {
281 var tab_text = that.containers[page_index];
281 var tab_text = that.containers[page_index];
282 if (tab_text !== undefined) {
282 if (tab_text !== undefined) {
283 tab_text.text(title);
283 tab_text.text(title);
284 }
284 }
285 });
285 });
286 },
286 },
287
287
288 /**
288 /**
289 * Updates the tab page titles.
289 * Updates the tab page titles.
290 */
290 */
291 update_selected_index: function(options) {
291 update_selected_index: function(options) {
292 if (options === undefined || options.updated_view != this) {
292 if (options === undefined || options.updated_view != this) {
293 var selected_index = this.model.get('selected_index');
293 var selected_index = this.model.get('selected_index');
294 if (0 <= selected_index && selected_index < this.containers.length) {
294 if (0 <= selected_index && selected_index < this.containers.length) {
295 this.select_page(selected_index);
295 this.select_page(selected_index);
296 }
296 }
297 }
297 }
298 },
298 },
299
299
300 select_page: function(index) {
300 select_page: function(index) {
301 /**
301 /**
302 * Select a page.
302 * Select a page.
303 */
303 */
304 this.$tabs.find('li')
304 this.$tabs.find('li')
305 .removeClass('active');
305 .removeClass('active');
306 this.containers[index].tab('show');
306 this.containers[index].tab('show');
307 },
307 },
308
308
309 remove: function() {
309 remove: function() {
310 /**
310 /**
311 * We remove this widget before removing the children as an optimization
311 * We remove this widget before removing the children as an optimization
312 * we want to remove the entire container from the DOM first before
312 * we want to remove the entire container from the DOM first before
313 * removing each individual child separately.
313 * removing each individual child separately.
314 */
314 */
315 TabView.__super__.remove.apply(this, arguments);
315 TabView.__super__.remove.apply(this, arguments);
316 this.children_views.remove();
316 this.children_views.remove();
317 },
317 },
318 });
318 });
319
319
320 return {
320 return {
321 'AccordionView': AccordionView,
321 'AccordionView': AccordionView,
322 'TabView': TabView,
322 'TabView': TabView,
323 };
323 };
324 });
324 });
General Comments 0
You need to be logged in to leave comments. Login now