##// END OF EJS Templates
Make dropdown view DOM swap elements on update.
Jonathan Frederic -
Show More
@@ -1,371 +1,375
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2013 The IPython Development Team
2 // Copyright (C) 2013 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // SelectionWidget
9 // SelectionWidget
10 //============================================================================
10 //============================================================================
11
11
12 /**
12 /**
13 * @module IPython
13 * @module IPython
14 * @namespace IPython
14 * @namespace IPython
15 **/
15 **/
16
16
17 define(["notebook/js/widgets/widget"], function(widget_manager){
17 define(["notebook/js/widgets/widget"], function(widget_manager){
18
18
19 var DropdownView = IPython.DOMWidgetView.extend({
19 var DropdownView = IPython.DOMWidgetView.extend({
20 render : function(){
20 render : function(){
21 // Called when view is rendered.
21 // Called when view is rendered.
22 this.$el
22 this.$el
23 .addClass('widget-hbox-single')
23 .addClass('widget-hbox-single')
24 .html('');
24 .html('');
25 this.$label = $('<div />')
25 this.$label = $('<div />')
26 .appendTo(this.$el)
26 .appendTo(this.$el)
27 .addClass('widget-hlabel')
27 .addClass('widget-hlabel')
28 .hide();
28 .hide();
29 this.$buttongroup = $('<div />')
29 this.$buttongroup = $('<div />')
30 .addClass('widget_item')
30 .addClass('widget_item')
31 .addClass('btn-group')
31 .addClass('btn-group')
32 .appendTo(this.$el);
32 .appendTo(this.$el);
33 this.$el_to_style = this.$buttongroup; // Set default element to style
33 this.$el_to_style = this.$buttongroup; // Set default element to style
34 this.$droplabel = $('<button />')
34 this.$droplabel = $('<button />')
35 .addClass('btn')
35 .addClass('btn')
36 .addClass('widget-combo-btn')
36 .addClass('widget-combo-btn')
37 .html('&nbsp;')
37 .html('&nbsp;')
38 .appendTo(this.$buttongroup);
38 .appendTo(this.$buttongroup);
39 this.$dropbutton = $('<button />')
39 this.$dropbutton = $('<button />')
40 .addClass('btn')
40 .addClass('btn')
41 .addClass('dropdown-toggle')
41 .addClass('dropdown-toggle')
42 .addClass('widget-combo-carrot-btn')
42 .addClass('widget-combo-carrot-btn')
43 .attr('data-toggle', 'dropdown')
43 .attr('data-toggle', 'dropdown')
44 .html('<span class="caret"></span>')
44 .html('<span class="caret"></span>')
45 .appendTo(this.$buttongroup);
45 .appendTo(this.$buttongroup);
46 this.$droplist = $('<ul />')
46 this.$droplist = $('<ul />')
47 .addClass('dropdown-menu')
47 .addClass('dropdown-menu')
48 .appendTo(this.$buttongroup);
48 .appendTo(this.$buttongroup);
49
49
50 // Set defaults.
50 // Set defaults.
51 this.update();
51 this.update();
52 },
52 },
53
53
54 update : function(options){
54 update : function(options){
55 // Update the contents of this view
55 // Update the contents of this view
56 //
56 //
57 // Called when the model is changed. The model may have been
57 // Called when the model is changed. The model may have been
58 // changed by another view or by a state update from the back-end.
58 // changed by another view or by a state update from the back-end.
59
59
60 if (options === undefined || options.updated_view != this) {
60 if (options === undefined || options.updated_view != this) {
61 var selected_item_text = this.model.get('value');
61 var selected_item_text = this.model.get('value');
62 selected_item_text = selected_item_text.replace(/ /g, '&nbsp;');
62 selected_item_text = selected_item_text.replace(/ /g, '&nbsp;');
63 selected_item_text = selected_item_text.replace(/\n/g, '<br>\n');
63 selected_item_text = selected_item_text.replace(/\n/g, '<br>\n');
64 if (selected_item_text.length === 0) {
64 if (selected_item_text.length === 0) {
65 this.$droplabel.html('&nbsp;');
65 this.$droplabel.html('&nbsp;');
66 } else {
66 } else {
67 this.$droplabel.html(selected_item_text);
67 this.$droplabel.html(selected_item_text);
68 }
68 }
69
69
70 var items = this.model.get('values');
70 var items = this.model.get('values');
71 this.$droplist.html('');
71 var $replace_droplist = $('<ul />')
72 .addClass('dropdown-menu');
72 for (var index in items) {
73 for (var index in items) {
73 var that = this;
74 var that = this;
74 var item_button = $('<a href="#"/>')
75 var item_button = $('<a href="#"/>')
75 .html(items[index])
76 .html(items[index])
76 .on('click', $.proxy(this.handle_click, this));
77 .on('click', $.proxy(this.handle_click, this));
77 this.$droplist.append($('<li />').append(item_button));
78 $replace_droplist.append($('<li />').append(item_button));
78 }
79 }
80 this.$droplist.replaceWith($replace_droplist);
81 this.$droplist.remove();
82 this.$droplist = $replace_droplist;
79
83
80 if (this.model.get('disabled')) {
84 if (this.model.get('disabled')) {
81 this.$buttongroup.attr('disabled','disabled');
85 this.$buttongroup.attr('disabled','disabled');
82 this.$droplabel.attr('disabled','disabled');
86 this.$droplabel.attr('disabled','disabled');
83 this.$dropbutton.attr('disabled','disabled');
87 this.$dropbutton.attr('disabled','disabled');
84 this.$droplist.attr('disabled','disabled');
88 this.$droplist.attr('disabled','disabled');
85 } else {
89 } else {
86 this.$buttongroup.removeAttr('disabled');
90 this.$buttongroup.removeAttr('disabled');
87 this.$droplabel.removeAttr('disabled');
91 this.$droplabel.removeAttr('disabled');
88 this.$dropbutton.removeAttr('disabled');
92 this.$dropbutton.removeAttr('disabled');
89 this.$droplist.removeAttr('disabled');
93 this.$droplist.removeAttr('disabled');
90 }
94 }
91
95
92 var description = this.model.get('description');
96 var description = this.model.get('description');
93 if (description.length === 0) {
97 if (description.length === 0) {
94 this.$label.hide();
98 this.$label.hide();
95 } else {
99 } else {
96 this.$label.html(description);
100 this.$label.html(description);
97 this.$label.show();
101 this.$label.show();
98 }
102 }
99 }
103 }
100 return DropdownView.__super__.update.apply(this);
104 return DropdownView.__super__.update.apply(this);
101 },
105 },
102
106
103 handle_click: function (e) {
107 handle_click: function (e) {
104 // Handle when a value is clicked.
108 // Handle when a value is clicked.
105
109
106 // Calling model.set will trigger all of the other views of the
110 // Calling model.set will trigger all of the other views of the
107 // model to update.
111 // model to update.
108 this.model.set('value', $(e.target).html(), {updated_view: this});
112 this.model.set('value', $(e.target).html(), {updated_view: this});
109 this.touch();
113 this.touch();
110 },
114 },
111
115
112 });
116 });
113 widget_manager.register_widget_view('DropdownView', DropdownView);
117 widget_manager.register_widget_view('DropdownView', DropdownView);
114
118
115
119
116 var RadioButtonsView = IPython.DOMWidgetView.extend({
120 var RadioButtonsView = IPython.DOMWidgetView.extend({
117 render : function(){
121 render : function(){
118 // Called when view is rendered.
122 // Called when view is rendered.
119 this.$el
123 this.$el
120 .addClass('widget-hbox');
124 .addClass('widget-hbox');
121 this.$label = $('<div />')
125 this.$label = $('<div />')
122 .appendTo(this.$el)
126 .appendTo(this.$el)
123 .addClass('widget-hlabel')
127 .addClass('widget-hlabel')
124 .hide();
128 .hide();
125 this.$container = $('<div />')
129 this.$container = $('<div />')
126 .appendTo(this.$el)
130 .appendTo(this.$el)
127 .addClass('widget-container')
131 .addClass('widget-container')
128 .addClass('vbox');
132 .addClass('vbox');
129 this.$el_to_style = this.$container; // Set default element to style
133 this.$el_to_style = this.$container; // Set default element to style
130 this.update();
134 this.update();
131 },
135 },
132
136
133 update : function(options){
137 update : function(options){
134 // Update the contents of this view
138 // Update the contents of this view
135 //
139 //
136 // Called when the model is changed. The model may have been
140 // Called when the model is changed. The model may have been
137 // changed by another view or by a state update from the back-end.
141 // changed by another view or by a state update from the back-end.
138 if (options === undefined || options.updated_view != this) {
142 if (options === undefined || options.updated_view != this) {
139 // Add missing items to the DOM.
143 // Add missing items to the DOM.
140 var items = this.model.get('values');
144 var items = this.model.get('values');
141 var disabled = this.model.get('disabled');
145 var disabled = this.model.get('disabled');
142 for (var index in items) {
146 for (var index in items) {
143 var item_query = ' :input[value="' + items[index] + '"]';
147 var item_query = ' :input[value="' + items[index] + '"]';
144 if (this.$el.find(item_query).length === 0) {
148 if (this.$el.find(item_query).length === 0) {
145 var $label = $('<label />')
149 var $label = $('<label />')
146 .addClass('radio')
150 .addClass('radio')
147 .html(items[index])
151 .html(items[index])
148 .appendTo(this.$container);
152 .appendTo(this.$container);
149
153
150 $('<input />')
154 $('<input />')
151 .attr('type', 'radio')
155 .attr('type', 'radio')
152 .addClass(this.model)
156 .addClass(this.model)
153 .val(items[index])
157 .val(items[index])
154 .prependTo($label)
158 .prependTo($label)
155 .on('click', $.proxy(this.handle_click, this));
159 .on('click', $.proxy(this.handle_click, this));
156 }
160 }
157
161
158 var $item_element = this.$container.find(item_query);
162 var $item_element = this.$container.find(item_query);
159 if (this.model.get('value') == items[index]) {
163 if (this.model.get('value') == items[index]) {
160 $item_element.prop('checked', true);
164 $item_element.prop('checked', true);
161 } else {
165 } else {
162 $item_element.prop('checked', false);
166 $item_element.prop('checked', false);
163 }
167 }
164 $item_element.prop('disabled', disabled);
168 $item_element.prop('disabled', disabled);
165 }
169 }
166
170
167 // Remove items that no longer exist.
171 // Remove items that no longer exist.
168 this.$container.find('input').each(function(i, obj) {
172 this.$container.find('input').each(function(i, obj) {
169 var value = $(obj).val();
173 var value = $(obj).val();
170 var found = false;
174 var found = false;
171 for (var index in items) {
175 for (var index in items) {
172 if (items[index] == value) {
176 if (items[index] == value) {
173 found = true;
177 found = true;
174 break;
178 break;
175 }
179 }
176 }
180 }
177
181
178 if (!found) {
182 if (!found) {
179 $(obj).parent().remove();
183 $(obj).parent().remove();
180 }
184 }
181 });
185 });
182
186
183 var description = this.model.get('description');
187 var description = this.model.get('description');
184 if (description.length === 0) {
188 if (description.length === 0) {
185 this.$label.hide();
189 this.$label.hide();
186 } else {
190 } else {
187 this.$label.html(description);
191 this.$label.html(description);
188 this.$label.show();
192 this.$label.show();
189 }
193 }
190 }
194 }
191 return RadioButtonsView.__super__.update.apply(this);
195 return RadioButtonsView.__super__.update.apply(this);
192 },
196 },
193
197
194 handle_click: function (e) {
198 handle_click: function (e) {
195 // Handle when a value is clicked.
199 // Handle when a value is clicked.
196
200
197 // Calling model.set will trigger all of the other views of the
201 // Calling model.set will trigger all of the other views of the
198 // model to update.
202 // model to update.
199 this.model.set('value', $(e.target).val(), {updated_view: this});
203 this.model.set('value', $(e.target).val(), {updated_view: this});
200 this.touch();
204 this.touch();
201 },
205 },
202 });
206 });
203 widget_manager.register_widget_view('RadioButtonsView', RadioButtonsView);
207 widget_manager.register_widget_view('RadioButtonsView', RadioButtonsView);
204
208
205
209
206 var ToggleButtonsView = IPython.DOMWidgetView.extend({
210 var ToggleButtonsView = IPython.DOMWidgetView.extend({
207 render : function(){
211 render : function(){
208 // Called when view is rendered.
212 // Called when view is rendered.
209 this.$el
213 this.$el
210 .addClass('widget-hbox-single');
214 .addClass('widget-hbox-single');
211 this.$label = $('<div />')
215 this.$label = $('<div />')
212 .appendTo(this.$el)
216 .appendTo(this.$el)
213 .addClass('widget-hlabel')
217 .addClass('widget-hlabel')
214 .hide();
218 .hide();
215 this.$buttongroup = $('<div />')
219 this.$buttongroup = $('<div />')
216 .addClass('btn-group')
220 .addClass('btn-group')
217 .attr('data-toggle', 'buttons-radio')
221 .attr('data-toggle', 'buttons-radio')
218 .appendTo(this.$el);
222 .appendTo(this.$el);
219 this.$el_to_style = this.$buttongroup; // Set default element to style
223 this.$el_to_style = this.$buttongroup; // Set default element to style
220 this.update();
224 this.update();
221 },
225 },
222
226
223 update : function(options){
227 update : function(options){
224 // Update the contents of this view
228 // Update the contents of this view
225 //
229 //
226 // Called when the model is changed. The model may have been
230 // Called when the model is changed. The model may have been
227 // changed by another view or by a state update from the back-end.
231 // changed by another view or by a state update from the back-end.
228 if (options === undefined || options.updated_view != this) {
232 if (options === undefined || options.updated_view != this) {
229 // Add missing items to the DOM.
233 // Add missing items to the DOM.
230 var items = this.model.get('values');
234 var items = this.model.get('values');
231 var disabled = this.model.get('disabled');
235 var disabled = this.model.get('disabled');
232 for (var index in items) {
236 for (var index in items) {
233 var item_query = ' :contains("' + items[index] + '")';
237 var item_query = ' :contains("' + items[index] + '")';
234 if (this.$buttongroup.find(item_query).length === 0) {
238 if (this.$buttongroup.find(item_query).length === 0) {
235 $('<button />')
239 $('<button />')
236 .attr('type', 'button')
240 .attr('type', 'button')
237 .addClass('btn')
241 .addClass('btn')
238 .html(items[index])
242 .html(items[index])
239 .appendTo(this.$buttongroup)
243 .appendTo(this.$buttongroup)
240 .on('click', $.proxy(this.handle_click, this));
244 .on('click', $.proxy(this.handle_click, this));
241 }
245 }
242
246
243 var $item_element = this.$buttongroup.find(item_query);
247 var $item_element = this.$buttongroup.find(item_query);
244 if (this.model.get('value') == items[index]) {
248 if (this.model.get('value') == items[index]) {
245 $item_element.addClass('active');
249 $item_element.addClass('active');
246 } else {
250 } else {
247 $item_element.removeClass('active');
251 $item_element.removeClass('active');
248 }
252 }
249 $item_element.prop('disabled', disabled);
253 $item_element.prop('disabled', disabled);
250 }
254 }
251
255
252 // Remove items that no longer exist.
256 // Remove items that no longer exist.
253 this.$buttongroup.find('button').each(function(i, obj) {
257 this.$buttongroup.find('button').each(function(i, obj) {
254 var value = $(obj).html();
258 var value = $(obj).html();
255 var found = false;
259 var found = false;
256 for (var index in items) {
260 for (var index in items) {
257 if (items[index] == value) {
261 if (items[index] == value) {
258 found = true;
262 found = true;
259 break;
263 break;
260 }
264 }
261 }
265 }
262
266
263 if (!found) {
267 if (!found) {
264 $(obj).remove();
268 $(obj).remove();
265 }
269 }
266 });
270 });
267
271
268 var description = this.model.get('description');
272 var description = this.model.get('description');
269 if (description.length === 0) {
273 if (description.length === 0) {
270 this.$label.hide();
274 this.$label.hide();
271 } else {
275 } else {
272 this.$label.html(description);
276 this.$label.html(description);
273 this.$label.show();
277 this.$label.show();
274 }
278 }
275 }
279 }
276 return ToggleButtonsView.__super__.update.apply(this);
280 return ToggleButtonsView.__super__.update.apply(this);
277 },
281 },
278
282
279 handle_click: function (e) {
283 handle_click: function (e) {
280 // Handle when a value is clicked.
284 // Handle when a value is clicked.
281
285
282 // Calling model.set will trigger all of the other views of the
286 // Calling model.set will trigger all of the other views of the
283 // model to update.
287 // model to update.
284 this.model.set('value', $(e.target).html(), {updated_view: this});
288 this.model.set('value', $(e.target).html(), {updated_view: this});
285 this.touch();
289 this.touch();
286 },
290 },
287 });
291 });
288 widget_manager.register_widget_view('ToggleButtonsView', ToggleButtonsView);
292 widget_manager.register_widget_view('ToggleButtonsView', ToggleButtonsView);
289
293
290
294
291 var ListBoxView = IPython.DOMWidgetView.extend({
295 var ListBoxView = IPython.DOMWidgetView.extend({
292 render : function(){
296 render : function(){
293 // Called when view is rendered.
297 // Called when view is rendered.
294 this.$el
298 this.$el
295 .addClass('widget-hbox');
299 .addClass('widget-hbox');
296 this.$label = $('<div />')
300 this.$label = $('<div />')
297 .appendTo(this.$el)
301 .appendTo(this.$el)
298 .addClass('widget-hlabel')
302 .addClass('widget-hlabel')
299 .hide();
303 .hide();
300 this.$listbox = $('<select />')
304 this.$listbox = $('<select />')
301 .addClass('widget-listbox')
305 .addClass('widget-listbox')
302 .attr('size', 6)
306 .attr('size', 6)
303 .appendTo(this.$el);
307 .appendTo(this.$el);
304 this.$el_to_style = this.$listbox; // Set default element to style
308 this.$el_to_style = this.$listbox; // Set default element to style
305 this.update();
309 this.update();
306 },
310 },
307
311
308 update : function(options){
312 update : function(options){
309 // Update the contents of this view
313 // Update the contents of this view
310 //
314 //
311 // Called when the model is changed. The model may have been
315 // Called when the model is changed. The model may have been
312 // changed by another view or by a state update from the back-end.
316 // changed by another view or by a state update from the back-end.
313 if (options === undefined || options.updated_view != this) {
317 if (options === undefined || options.updated_view != this) {
314 // Add missing items to the DOM.
318 // Add missing items to the DOM.
315 var items = this.model.get('values');
319 var items = this.model.get('values');
316 for (var index in items) {
320 for (var index in items) {
317 var item_query = ' :contains("' + items[index] + '")';
321 var item_query = ' :contains("' + items[index] + '")';
318 if (this.$listbox.find(item_query).length === 0) {
322 if (this.$listbox.find(item_query).length === 0) {
319 $('<option />')
323 $('<option />')
320 .html(items[index])
324 .html(items[index])
321 .attr('value', items[index])
325 .attr('value', items[index])
322 .appendTo(this.$listbox)
326 .appendTo(this.$listbox)
323 .on('click', $.proxy(this.handle_click, this));
327 .on('click', $.proxy(this.handle_click, this));
324 }
328 }
325 }
329 }
326
330
327 // Select the correct element
331 // Select the correct element
328 this.$listbox.val(this.model.get('value'));
332 this.$listbox.val(this.model.get('value'));
329
333
330 // Disable listbox if needed
334 // Disable listbox if needed
331 var disabled = this.model.get('disabled');
335 var disabled = this.model.get('disabled');
332 this.$listbox.prop('disabled', disabled);
336 this.$listbox.prop('disabled', disabled);
333
337
334 // Remove items that no longer exist.
338 // Remove items that no longer exist.
335 this.$listbox.find('option').each(function(i, obj) {
339 this.$listbox.find('option').each(function(i, obj) {
336 var value = $(obj).html();
340 var value = $(obj).html();
337 var found = false;
341 var found = false;
338 for (var index in items) {
342 for (var index in items) {
339 if (items[index] == value) {
343 if (items[index] == value) {
340 found = true;
344 found = true;
341 break;
345 break;
342 }
346 }
343 }
347 }
344
348
345 if (!found) {
349 if (!found) {
346 $(obj).remove();
350 $(obj).remove();
347 }
351 }
348 });
352 });
349
353
350 var description = this.model.get('description');
354 var description = this.model.get('description');
351 if (description.length === 0) {
355 if (description.length === 0) {
352 this.$label.hide();
356 this.$label.hide();
353 } else {
357 } else {
354 this.$label.html(description);
358 this.$label.html(description);
355 this.$label.show();
359 this.$label.show();
356 }
360 }
357 }
361 }
358 return ListBoxView.__super__.update.apply(this);
362 return ListBoxView.__super__.update.apply(this);
359 },
363 },
360
364
361 handle_click: function (e) {
365 handle_click: function (e) {
362 // Handle when a value is clicked.
366 // Handle when a value is clicked.
363
367
364 // Calling model.set will trigger all of the other views of the
368 // Calling model.set will trigger all of the other views of the
365 // model to update.
369 // model to update.
366 this.model.set('value', $(e.target).html(), {updated_view: this});
370 this.model.set('value', $(e.target).html(), {updated_view: this});
367 this.touch();
371 this.touch();
368 },
372 },
369 });
373 });
370 widget_manager.register_widget_view('ListBoxView', ListBoxView);
374 widget_manager.register_widget_view('ListBoxView', ListBoxView);
371 });
375 });
General Comments 0
You need to be logged in to leave comments. Login now