##// END OF EJS Templates
listening for change on SelectView and SelectMultipleView
Nicholas Bollweg -
Show More
@@ -1,551 +1,565 b''
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 define([
5 5 "widgets/js/widget",
6 6 "base/js/utils",
7 7 "jquery",
8 8 "underscore",
9 9 "bootstrap",
10 10 ], function(widget, utils, $, _){
11 11
12 12 var DropdownView = widget.DOMWidgetView.extend({
13 13 render : function(){
14 14 /**
15 15 * Called when view is rendered.
16 16 */
17 17 this.$el
18 18 .addClass('widget-hbox widget-dropdown');
19 19 this.$label = $('<div />')
20 20 .appendTo(this.$el)
21 21 .addClass('widget-label')
22 22 .hide();
23 23 this.$buttongroup = $('<div />')
24 24 .addClass('widget_item')
25 25 .addClass('btn-group')
26 26 .appendTo(this.$el);
27 27 this.$droplabel = $('<button />')
28 28 .addClass('btn btn-default')
29 29 .addClass('widget-combo-btn')
30 30 .html("&nbsp;")
31 31 .appendTo(this.$buttongroup);
32 32 this.$dropbutton = $('<button />')
33 33 .addClass('btn btn-default')
34 34 .addClass('dropdown-toggle')
35 35 .addClass('widget-combo-carrot-btn')
36 36 .attr('data-toggle', 'dropdown')
37 37 .append($('<span />').addClass("caret"))
38 38 .appendTo(this.$buttongroup);
39 39 this.$droplist = $('<ul />')
40 40 .addClass('dropdown-menu')
41 41 .appendTo(this.$buttongroup);
42 42
43 43 this.model.on('change:button_style', function(model, value) {
44 44 this.update_button_style();
45 45 }, this);
46 46 this.update_button_style('');
47 47
48 48 // Set defaults.
49 49 this.update();
50 50 },
51 51
52 52 update : function(options){
53 53 /**
54 54 * Update the contents of this view
55 55 *
56 56 * Called when the model is changed. The model may have been
57 57 * changed by another view or by a state update from the back-end.
58 58 */
59 59
60 60 if (options === undefined || options.updated_view != this) {
61 61 var selected_item_text = this.model.get('selected_label');
62 62 if (selected_item_text.trim().length === 0) {
63 63 this.$droplabel.html("&nbsp;");
64 64 } else {
65 65 this.$droplabel.text(selected_item_text);
66 66 }
67 67
68 68 var items = this.model.get('_options_labels');
69 69 var $replace_droplist = $('<ul />')
70 70 .addClass('dropdown-menu');
71 71 // Copy the style
72 72 $replace_droplist.attr('style', this.$droplist.attr('style'));
73 73 var that = this;
74 74 _.each(items, function(item, i) {
75 75 var item_button = $('<a href="#"/>')
76 76 .text(item)
77 77 .on('click', $.proxy(that.handle_click, that));
78 78 $replace_droplist.append($('<li />').append(item_button));
79 79 });
80 80
81 81 this.$droplist.replaceWith($replace_droplist);
82 82 this.$droplist.remove();
83 83 this.$droplist = $replace_droplist;
84 84
85 85 if (this.model.get('disabled')) {
86 86 this.$buttongroup.attr('disabled','disabled');
87 87 this.$droplabel.attr('disabled','disabled');
88 88 this.$dropbutton.attr('disabled','disabled');
89 89 this.$droplist.attr('disabled','disabled');
90 90 } else {
91 91 this.$buttongroup.removeAttr('disabled');
92 92 this.$droplabel.removeAttr('disabled');
93 93 this.$dropbutton.removeAttr('disabled');
94 94 this.$droplist.removeAttr('disabled');
95 95 }
96 96
97 97 var description = this.model.get('description');
98 98 if (description.length === 0) {
99 99 this.$label.hide();
100 100 } else {
101 101 this.typeset(this.$label, description);
102 102 this.$label.show();
103 103 }
104 104 }
105 105 return DropdownView.__super__.update.apply(this);
106 106 },
107 107
108 108 update_button_style: function(previous_trait_value) {
109 109 var class_map = {
110 110 primary: ['btn-primary'],
111 111 success: ['btn-success'],
112 112 info: ['btn-info'],
113 113 warning: ['btn-warning'],
114 114 danger: ['btn-danger']
115 115 };
116 116 this.update_mapped_classes(class_map, 'button_style', previous_trait_value, this.$droplabel);
117 117 this.update_mapped_classes(class_map, 'button_style', previous_trait_value, this.$dropbutton);
118 118 },
119 119
120 120 update_attr: function(name, value) {
121 121 /**
122 122 * Set a css attr of the widget view.
123 123 */
124 124 if (name.substring(0, 6) == 'border' || name == 'background' || name == 'color') {
125 125 this.$droplabel.css(name, value);
126 126 this.$dropbutton.css(name, value);
127 127 this.$droplist.css(name, value);
128 128 } else if (name == 'width') {
129 129 this.$droplist.css(name, value);
130 130 this.$droplabel.css(name, value);
131 131 } else if (name == 'padding') {
132 132 this.$droplist.css(name, value);
133 133 this.$buttongroup.css(name, value);
134 134 } else if (name == 'margin') {
135 135 this.$buttongroup.css(name, value);
136 136 } else if (name == 'height') {
137 137 this.$droplabel.css(name, value);
138 138 this.$dropbutton.css(name, value);
139 139 } else if (name == 'padding' || name == 'margin') {
140 140 this.$el.css(name, value);
141 141 } else {
142 142 this.$droplist.css(name, value);
143 143 this.$droplabel.css(name, value);
144 144 }
145 145 },
146 146
147 147 handle_click: function (e) {
148 148 /**
149 149 * Handle when a value is clicked.
150 150 *
151 151 * Calling model.set will trigger all of the other views of the
152 152 * model to update.
153 153 */
154 154 this.model.set('selected_label', $(e.target).text(), {updated_view: this});
155 155 this.touch();
156 156
157 157 // Manually hide the droplist.
158 158 e.stopPropagation();
159 159 e.preventDefault();
160 160 this.$buttongroup.removeClass('open');
161 161 },
162 162
163 163 });
164 164
165 165
166 166 var RadioButtonsView = widget.DOMWidgetView.extend({
167 167 render : function(){
168 168 /**
169 169 * Called when view is rendered.
170 170 */
171 171 this.$el
172 172 .addClass('widget-hbox widget-radio');
173 173 this.$label = $('<div />')
174 174 .appendTo(this.$el)
175 175 .addClass('widget-label')
176 176 .hide();
177 177 this.$container = $('<div />')
178 178 .appendTo(this.$el)
179 179 .addClass('widget-radio-box');
180 180 this.update();
181 181 },
182 182
183 183 update : function(options){
184 184 /**
185 185 * Update the contents of this view
186 186 *
187 187 * Called when the model is changed. The model may have been
188 188 * changed by another view or by a state update from the back-end.
189 189 */
190 190 if (options === undefined || options.updated_view != this) {
191 191 // Add missing items to the DOM.
192 192 var items = this.model.get('_options_labels');
193 193 var disabled = this.model.get('disabled');
194 194 var that = this;
195 195 _.each(items, function(item, index) {
196 196 var item_query = ' :input[data-value="' + encodeURIComponent(item) + '"]';
197 197 if (that.$el.find(item_query).length === 0) {
198 198 var $label = $('<label />')
199 199 .addClass('radio')
200 200 .text(item)
201 201 .appendTo(that.$container);
202 202
203 203 $('<input />')
204 204 .attr('type', 'radio')
205 205 .addClass(that.model)
206 206 .val(item)
207 207 .attr('data-value', encodeURIComponent(item))
208 208 .prependTo($label)
209 209 .on('click', $.proxy(that.handle_click, that));
210 210 }
211 211
212 212 var $item_element = that.$container.find(item_query);
213 213 if (that.model.get('selected_label') == item) {
214 214 $item_element.prop('checked', true);
215 215 } else {
216 216 $item_element.prop('checked', false);
217 217 }
218 218 $item_element.prop('disabled', disabled);
219 219 });
220 220
221 221 // Remove items that no longer exist.
222 222 this.$container.find('input').each(function(i, obj) {
223 223 var value = $(obj).val();
224 224 var found = false;
225 225 _.each(items, function(item, index) {
226 226 if (item == value) {
227 227 found = true;
228 228 return false;
229 229 }
230 230 });
231 231
232 232 if (!found) {
233 233 $(obj).parent().remove();
234 234 }
235 235 });
236 236
237 237 var description = this.model.get('description');
238 238 if (description.length === 0) {
239 239 this.$label.hide();
240 240 } else {
241 241 this.$label.text(description);
242 242 this.typeset(this.$label, description);
243 243 this.$label.show();
244 244 }
245 245 }
246 246 return RadioButtonsView.__super__.update.apply(this);
247 247 },
248 248
249 249 update_attr: function(name, value) {
250 250 /**
251 251 * Set a css attr of the widget view.
252 252 */
253 253 if (name == 'padding' || name == 'margin') {
254 254 this.$el.css(name, value);
255 255 } else {
256 256 this.$container.css(name, value);
257 257 }
258 258 },
259 259
260 260 handle_click: function (e) {
261 261 /**
262 262 * Handle when a value is clicked.
263 263 *
264 264 * Calling model.set will trigger all of the other views of the
265 265 * model to update.
266 266 */
267 267 this.model.set('selected_label', $(e.target).val(), {updated_view: this});
268 268 this.touch();
269 269 },
270 270 });
271 271
272 272
273 273 var ToggleButtonsView = widget.DOMWidgetView.extend({
274 274 initialize: function() {
275 275 this._css_state = {};
276 276 ToggleButtonsView.__super__.initialize.apply(this, arguments);
277 277 },
278 278
279 279 render: function() {
280 280 /**
281 281 * Called when view is rendered.
282 282 */
283 283 this.$el
284 284 .addClass('widget-hbox widget-toggle-buttons');
285 285 this.$label = $('<div />')
286 286 .appendTo(this.$el)
287 287 .addClass('widget-label')
288 288 .hide();
289 289 this.$buttongroup = $('<div />')
290 290 .addClass('btn-group')
291 291 .appendTo(this.$el);
292 292
293 293 this.model.on('change:button_style', function(model, value) {
294 294 this.update_button_style();
295 295 }, this);
296 296 this.update_button_style('');
297 297 this.update();
298 298 },
299 299
300 300 update : function(options){
301 301 /**
302 302 * Update the contents of this view
303 303 *
304 304 * Called when the model is changed. The model may have been
305 305 * changed by another view or by a state update from the back-end.
306 306 */
307 307 if (options === undefined || options.updated_view != this) {
308 308 // Add missing items to the DOM.
309 309 var items = this.model.get('_options_labels');
310 310 var disabled = this.model.get('disabled');
311 311 var that = this;
312 312 var item_html;
313 313 _.each(items, function(item, index) {
314 314 if (item.trim().length === 0) {
315 315 item_html = "&nbsp;";
316 316 } else {
317 317 item_html = utils.escape_html(item);
318 318 }
319 319 var item_query = '[data-value="' + encodeURIComponent(item) + '"]';
320 320 var $item_element = that.$buttongroup.find(item_query);
321 321 if (!$item_element.length) {
322 322 $item_element = $('<button/>')
323 323 .attr('type', 'button')
324 324 .addClass('btn btn-default')
325 325 .html(item_html)
326 326 .appendTo(that.$buttongroup)
327 327 .attr('data-value', encodeURIComponent(item))
328 328 .attr('value', item)
329 329 .on('click', $.proxy(that.handle_click, that));
330 330 that.update_style_traits($item_element);
331 331 }
332 332 if (that.model.get('selected_label') == item) {
333 333 $item_element.addClass('active');
334 334 } else {
335 335 $item_element.removeClass('active');
336 336 }
337 337 $item_element.prop('disabled', disabled);
338 338 });
339 339
340 340 // Remove items that no longer exist.
341 341 this.$buttongroup.find('button').each(function(i, obj) {
342 342 var value = $(obj).attr('value');
343 343 var found = false;
344 344 _.each(items, function(item, index) {
345 345 if (item == value) {
346 346 found = true;
347 347 return false;
348 348 }
349 349 });
350 350
351 351 if (!found) {
352 352 $(obj).remove();
353 353 }
354 354 });
355 355
356 356 var description = this.model.get('description');
357 357 if (description.length === 0) {
358 358 this.$label.hide();
359 359 } else {
360 360 this.$label.text();
361 361 this.typeset(this.$label, description);
362 362 this.$label.show();
363 363 }
364 364 }
365 365 return ToggleButtonsView.__super__.update.apply(this);
366 366 },
367 367
368 368 update_attr: function(name, value) {
369 369 /**
370 370 * Set a css attr of the widget view.
371 371 */
372 372 if (name == 'padding' || name == 'margin') {
373 373 this.$el.css(name, value);
374 374 } else {
375 375 this._css_state[name] = value;
376 376 this.update_style_traits();
377 377 }
378 378 },
379 379
380 380 update_style_traits: function(button) {
381 381 for (var name in this._css_state) {
382 382 if (this._css_state.hasOwnProperty(name)) {
383 383 if (name == 'margin') {
384 384 this.$buttongroup.css(name, this._css_state[name]);
385 385 } else if (name != 'width') {
386 386 if (button) {
387 387 button.css(name, this._css_state[name]);
388 388 } else {
389 389 this.$buttongroup.find('button').css(name, this._css_state[name]);
390 390 }
391 391 }
392 392 }
393 393 }
394 394 },
395 395
396 396 update_button_style: function(previous_trait_value) {
397 397 var class_map = {
398 398 primary: ['btn-primary'],
399 399 success: ['btn-success'],
400 400 info: ['btn-info'],
401 401 warning: ['btn-warning'],
402 402 danger: ['btn-danger']
403 403 };
404 404 this.update_mapped_classes(class_map, 'button_style', previous_trait_value, this.$buttongroup.find('button'));
405 405 },
406 406
407 407 handle_click: function (e) {
408 408 /**
409 409 * Handle when a value is clicked.
410 410 *
411 411 * Calling model.set will trigger all of the other views of the
412 412 * model to update.
413 413 */
414 414 this.model.set('selected_label', $(e.target).attr('value'), {updated_view: this});
415 415 this.touch();
416 416 },
417 417 });
418 418
419 419
420 420 var SelectView = widget.DOMWidgetView.extend({
421 421 render : function(){
422 422 /**
423 423 * Called when view is rendered.
424 424 */
425 425 this.$el
426 426 .addClass('widget-hbox widget-select');
427 427 this.$label = $('<div />')
428 428 .appendTo(this.$el)
429 429 .addClass('widget-label')
430 430 .hide();
431 431 this.$listbox = $('<select />')
432 432 .addClass('widget-listbox form-control')
433 433 .attr('size', 6)
434 .appendTo(this.$el);
434 .appendTo(this.$el)
435 .on('change', $.proxy(this.handle_change, this));
435 436 this.update();
436 437 },
437 438
438 439 update : function(options){
439 440 /**
440 441 * Update the contents of this view
441 442 *
442 443 * Called when the model is changed. The model may have been
443 444 * changed by another view or by a state update from the back-end.
444 445 */
445 446 if (options === undefined || options.updated_view != this) {
446 447 // Add missing items to the DOM.
447 448 var items = this.model.get('_options_labels');
448 449 var that = this;
449 450 _.each(items, function(item, index) {
450 451 var item_query = 'option[data-value="' + encodeURIComponent(item) + '"]';
451 452 if (that.$listbox.find(item_query).length === 0) {
452 453 $('<option />')
453 454 .text(item)
454 455 .attr('data-value', encodeURIComponent(item))
455 456 .attr('selected_label', item)
456 .appendTo(that.$listbox)
457 .on('click', $.proxy(that.handle_click, that));
457 .appendTo(that.$listbox);
458 458 }
459 459 });
460 460
461 461 // Select the correct element
462 462 this.$listbox.val(this.model.get('selected_label'));
463 463
464 464 // Disable listbox if needed
465 465 var disabled = this.model.get('disabled');
466 466 this.$listbox.prop('disabled', disabled);
467 467
468 468 // Remove items that no longer exist.
469 469 this.$listbox.find('option').each(function(i, obj) {
470 470 var value = $(obj).text();
471 471 var found = false;
472 472 _.each(items, function(item, index) {
473 473 if (item == value) {
474 474 found = true;
475 475 return false;
476 476 }
477 477 });
478 478
479 479 if (!found) {
480 480 $(obj).remove();
481 481 }
482 482 });
483 483
484 484 var description = this.model.get('description');
485 485 if (description.length === 0) {
486 486 this.$label.hide();
487 487 } else {
488 488 this.typeset(this.$label, description);
489 489 this.$label.show();
490 490 }
491 491 }
492 492 return SelectView.__super__.update.apply(this);
493 493 },
494 494
495 495 update_attr: function(name, value) {
496 496 /**
497 497 * Set a css attr of the widget view.
498 498 */
499 499 if (name == 'padding' || name == 'margin') {
500 500 this.$el.css(name, value);
501 501 } else {
502 502 this.$listbox.css(name, value);
503 503 }
504 504 },
505 505
506 handle_click: function (e) {
506 handle_change: function (e) {
507 507 /**
508 * Handle when a value is clicked.
508 * Handle when a new value is selected.
509 509 *
510 510 * Calling model.set will trigger all of the other views of the
511 511 * model to update.
512 512 */
513 this.model.set('selected_label', $(e.target).text(), {updated_view: this});
513 this.model.set('selected_label', this.$listbox.val(), {updated_view: this});
514 514 this.touch();
515 515 },
516 516 });
517
518
517 519 var SelectMultipleView = SelectView.extend({
518 520 render: function(){
521 /**
522 * Called when view is rendered.
523 */
519 524 SelectMultipleView.__super__.render.apply(this);
520 525 this.$el.removeClass('widget-select')
521 526 .addClass('widget-select-multiple');
522 527 this.$listbox.attr('multiple', true)
523 .on('input', $.proxy(this.handle_click, this));
528 .on('change', $.proxy(this.handle_change, this));
524 529 return this;
525 530 },
526 531
527 532 update: function(){
533 /**
534 * Update the contents of this view
535 *
536 * Called when the model is changed. The model may have been
537 * changed by another view or by a state update from the back-end.
538 */
528 539 SelectMultipleView.__super__.update.apply(this, arguments);
529 540 this.$listbox.val(this.model.get('selected_labels'));
530 541 },
531 542
532 handle_click: function (e) {
533 // Handle when a value is clicked.
534
535 // Calling model.set will trigger all of the other views of the
536 // model to update.
543 handle_change: function (e) {
544 /**
545 * Handle when a new value is selected.
546 *
547 * Calling model.set will trigger all of the other views of the
548 * model to update.
549 */
537 550 this.model.set('selected_labels',
538 551 (this.$listbox.val() || []).slice(),
539 552 {updated_view: this});
540 553 this.touch();
541 554 },
542 555 });
543 556
557
544 558 return {
545 559 'DropdownView': DropdownView,
546 560 'RadioButtonsView': RadioButtonsView,
547 561 'ToggleButtonsView': ToggleButtonsView,
548 562 'SelectView': SelectView,
549 563 'SelectMultipleView': SelectMultipleView,
550 564 };
551 565 });
General Comments 0
You need to be logged in to leave comments. Login now