##// END OF EJS Templates
Use checkbox layout instead of actions dropdown
Jonathan Frederic -
Show More
@@ -87,6 +87,11 b' define(['
87 that.load_list();
87 that.load_list();
88 });
88 });
89 });
89 });
90
91 $('.rename-button').click($.proxy(this.rename_selected, this));
92 $('.shutdown-button').click($.proxy(this.shutdown_selected, this));
93 $('.duplicate-button').click($.proxy(this.duplicate_selected, this));
94 $('.delete-button').click($.proxy(this.delete_selected, this));
90 }
95 }
91 };
96 };
92
97
@@ -111,7 +116,7 b' define(['
111 // read non-notebook files as binary
116 // read non-notebook files as binary
112 reader.readAsArrayBuffer(f);
117 reader.readAsArrayBuffer(f);
113 }
118 }
114 var item = that.new_item(0);
119 var item = that.new_item(0, true);
115 item.addClass('new-file');
120 item.addClass('new-file');
116 that.add_name_input(f.name, item, file_ext == '.ipynb' ? 'notebook' : 'file');
121 that.add_name_input(f.name, item, file_ext == '.ipynb' ? 'notebook' : 'file');
117 // Store the list item in the reader so we can use it later
122 // Store the list item in the reader so we can use it later
@@ -218,7 +223,7 b' define(['
218 var path = this.notebook_path;
223 var path = this.notebook_path;
219 var offset = n_uploads;
224 var offset = n_uploads;
220 if (path !== '') {
225 if (path !== '') {
221 item = this.new_item(offset);
226 item = this.new_item(offset, false);
222 model = {
227 model = {
223 type: 'directory',
228 type: 'directory',
224 name: '..',
229 name: '..',
@@ -229,34 +234,84 b' define(['
229 }
234 }
230 for (var i=0; i<len; i++) {
235 for (var i=0; i<len; i++) {
231 model = list.content[i];
236 model = list.content[i];
232 item = this.new_item(i+offset);
237 item = this.new_item(i+offset, true);
233 this.add_link(model, item);
238 this.add_link(model, item);
234 }
239 }
235 // Trigger an event when we've finished drawing the notebook list.
240 // Trigger an event when we've finished drawing the notebook list.
236 events.trigger('draw_notebook_list.NotebookList');
241 events.trigger('draw_notebook_list.NotebookList');
242 this._selection_changed();
237 };
243 };
238
244
239
245
240 NotebookList.prototype.new_item = function (index) {
246 /**
241 var item = $('<div/>').addClass("list_item").addClass("row");
247 * Creates a new item.
242 // item.addClass('list_item ui-widget ui-widget-content ui-helper-clearfix');
248 * @param {integer} index
243 // item.css('border-top-style','none');
249 * @param {boolean} [selectable] - tristate, undefined: don't draw checkbox,
244 item.append($("<div/>").addClass("col-md-12").append(
250 * false: don't draw checkbox but pad
245 $('<i/>').addClass('item_icon')
251 * where it should be, true: draw checkbox.
246 ).append(
252 * @return {JQuery} row
247 $("<a/>").addClass("item_link").append(
253 */
248 $("<span/>").addClass("item_name")
254 NotebookList.prototype.new_item = function (index, selectable) {
249 )
255 var row = $('<div/>')
250 ).append(
256 .addClass("list_item")
251 $('<div/>').addClass("item_buttons pull-right")
257 .addClass("row");
252 ));
258
259 var item = $("<div/>")
260 .addClass("col-md-12")
261 .appendTo(row);
262
263 var checkbox;
264 if (selectable !== undefined) {
265 checkbox = $('<input/>')
266 .attr('type', 'checkbox')
267 .appendTo(item);
268 }
269
270 $('<i/>')
271 .addClass('item_icon')
272 .appendTo(item);
273
274 var link = $("<a/>")
275 .addClass("item_link")
276 .appendTo(item);
277
278 $("<span/>")
279 .addClass("item_name")
280 .appendTo(link);
281
282 if (selectable === false) {
283 checkbox.css('visibility', 'hidden');
284 } else if (selectable === true) {
285 var that = this;
286 link.click(function(e) {
287 e.stopPropagation();
288 });
289 checkbox.click(function(e) {
290 e.stopPropagation();
291 that._selection_changed();
292 });
293 row.click(function(e) {
294 e.stopPropagation();
295 checkbox.prop('checked', !checkbox.prop('checked'));
296 that._selection_changed();
297 });
298 }
299
300 var buttons = $('<div/>')
301 .addClass("item_buttons pull-right")
302 .appendTo(item);
303
304 $('<i/>')
305 .addClass('fa fa-power-off running-indicator')
306 .css('visible', 'hidden')
307 .appendTo(buttons);
253
308
254 if (index === -1) {
309 if (index === -1) {
255 this.element.append(item);
310 this.element.append(row);
256 } else {
311 } else {
257 this.element.children().eq(index).after(item);
312 this.element.children().eq(index).after(row);
258 }
313 }
259 return item;
314 return row;
260 };
315 };
261
316
262
317
@@ -272,12 +327,58 b' define(['
272 file: 'edit',
327 file: 'edit',
273 };
328 };
274
329
330 NotebookList.prototype._selection_changed = function() {
331 var selected = [];
332 var has_notebook = false;
333 var has_directory = false;
334 $('.list_item :checked').each(function(index, item) {
335 var parent = $(item).parent().parent();
336 selected.push({
337 name: parent.data('name'),
338 path: parent.data('path'),
339 type: parent.data('type')
340 });
341
342 has_notebook = has_notebook || parent.data('type') == 'notebook';
343 has_directory = has_directory || parent.data('type') == 'directory';
344 });
345 this.selected = selected;
346
347 // Rename is only visible when one item is selected.
348 if (selected.length==1) {
349 $('.rename-button').css('display', 'inline-block');
350 } else {
351 $('.rename-button').css('display', 'none');
352 }
353
354 // Shutdown is only visible when one or more notebooks are visible.
355 if (has_notebook) {
356 $('.shutdown-button').css('display', 'inline-block');
357 } else {
358 $('.shutdown-button').css('display', 'none');
359 }
360
361 // Duplicate isn't visible if a directory is selected.
362 if (selected.length > 0 && !has_directory) {
363 $('.duplicate-button').css('display', 'inline-block');
364 } else {
365 $('.duplicate-button').css('display', 'none');
366 }
367
368 // Delete is visible if one or more items are selected.
369 if (selected.length > 0) {
370 $('.delete-button').css('display', 'inline-block');
371 } else {
372 $('.delete-button').css('display', 'none');
373 }
374 };
275
375
276 NotebookList.prototype.add_link = function (model, item) {
376 NotebookList.prototype.add_link = function (model, item) {
277 var path = model.path,
377 var path = model.path,
278 name = model.name;
378 name = model.name;
279 item.data('name', name);
379 item.data('name', name);
280 item.data('path', path);
380 item.data('path', path);
381 item.data('type', model.type);
281 item.find(".item_name").text(name);
382 item.find(".item_name").text(name);
282 var icon = NotebookList.icons[model.type];
383 var icon = NotebookList.icons[model.type];
283 var uri_prefix = NotebookList.uri_prefixes[model.type];
384 var uri_prefix = NotebookList.uri_prefixes[model.type];
@@ -291,13 +392,8 b' define(['
291 )
392 )
292 );
393 );
293
394
294 var can_duplicate = (model.type != 'directory');
395 var running = (model.type == 'notebook' && this.sessions[path] !== undefined);
295 var can_rename = (model.type == 'directory');
396 item.find(".item_buttons i.running-indicator").css('visibility', running ? '' : 'hidden');
296 var can_delete = (model.type != 'notebook' || this.sessions[path] === undefined);
297 if (!can_delete) {
298 this.add_shutdown_button(item, this.sessions[path]);
299 }
300 this.add_actions_button(item, can_delete, can_duplicate, can_rename);
301
397
302 // directory nav doesn't open new tabs
398 // directory nav doesn't open new tabs
303 // files, notebooks do
399 // files, notebooks do
@@ -329,188 +425,156 b' define(['
329 };
425 };
330
426
331
427
332 NotebookList.prototype.add_shutdown_button = function (item, session) {
428 NotebookList.prototype.shutdown_selected = function() {
333 var that = this;
429 var that = this;
334 var shutdown_button = $("<button/>").text("Shutdown").addClass("btn btn-xs btn-warning").
430 var settings = {
335 click(function (e) {
431 processData : false,
336 var settings = {
432 cache : false,
337 processData : false,
433 type : "DELETE",
338 cache : false,
434 dataType : "json",
339 type : "DELETE",
435 success : function () {
340 dataType : "json",
436 that.load_sessions();
341 success : function () {
437 },
342 that.load_sessions();
438 error : utils.log_ajax_error,
343 },
439 };
344 error : utils.log_ajax_error,
440
345 };
441 this.selected.forEach(function(item) {
346 var url = utils.url_join_encode(
442 if (item.type == 'notebook') {
347 that.base_url,
443 var session = that.sessions[item.path];
348 'api/sessions',
444 if (session) {
349 session
445 var url = utils.url_join_encode(
350 );
446 that.base_url,
351 $.ajax(url, settings);
447 'api/sessions',
352 return false;
448 session
353 });
449 );
354 item.find(".item_buttons").append(shutdown_button);
450 $.ajax(url, settings);
451 }
452 }
453 });
355 };
454 };
356
455
357 NotebookList.prototype.add_actions_button = function (item, can_delete, can_duplicate, can_rename) {
456 NotebookList.prototype.rename_selected = function() {
358 var group = $("<div/>")
457 if (this.selected.length != 1) return;
359 .addClass('btn-group')
360 .css('float', 'none')
361 .appendTo(item.find(".item_buttons"));
362
363 var actions_button = $("<button/>")
364 .html("Actions <span class='caret'></span>")
365 .addClass("btn btn-default btn-xs dropdown-toggle")
366 .attr('data-toggle', 'dropdown')
367 .attr('aria-expanded', 'false')
368 .appendTo(group);
369
370 var actions_list = $('<ul/>')
371 .addClass('dropdown-menu')
372 .attr('role', 'menu')
373 .appendTo(group);
374
375 var create_action = function(label, callback) {
376 var item = $('<li/>')
377 .click(callback)
378 .appendTo(actions_list);
379
380 var link = $('<a/>')
381 .attr('href', '#')
382 .html(label)
383 .appendTo(item);
384 };
385
458
386 if (can_delete) create_action('Delete', this.make_delete_callback(item));
459 var that = this;
387 if (can_duplicate) create_action('Duplicate', this.make_duplicate_callback(item));
460 var path = this.selected[0].path;
388 if (can_rename) create_action('Rename', this.make_rename_callback(item));
461 var input = $('<input/>').attr('type','text').attr('size','25').addClass('form-control')
462 .val(path);
463 var dialog_body = $('<div/>').append(
464 $("<p/>").addClass("rename-message")
465 .text('Enter a new directory name:')
466 ).append(
467 $("<br/>")
468 ).append(input);
469 var d = dialog.modal({
470 title : "Rename directory",
471 body : dialog_body,
472 buttons : {
473 OK : {
474 class: "btn-primary",
475 click: function() {
476 that.contents.rename(path, input.val()).then(function() {
477 that.load_list();
478 }).catch(function(e) {
479 dialog.modal({
480 title : "Error",
481 body : $('<div/>')
482 .text("An error occurred while renaming \"" + path + "\" to \"" + input.val() + "\".")
483 .append($('<div/>').addClass('alert alert-danger').text(String(e))),
484 buttons : {
485 OK : {}
486 }
487 });
488 });
489 }
490 },
491 Cancel : {}
492 },
493 open : function () {
494 // Upon ENTER, click the OK button.
495 input.keydown(function (event) {
496 if (event.which === keyboard.keycodes.enter) {
497 d.find('.btn-primary').first().click();
498 return false;
499 }
500 });
501 input.focus().select();
502 }
503 });
389 };
504 };
390
505
391 NotebookList.prototype.make_rename_callback = function (item) {
506 NotebookList.prototype.delete_selected = function() {
392 var notebooklist = this;
507 var message;
393 return function (e) {
508 if (this.selected.length == 1) {
394 // $(this) is the button that was clicked.
509 message = 'Are you sure you want to permanently delete: ' + this.selected[0].name + '?';
395 var that = $(this);
510 } else {
396 // We use the filename from the parent list_item element's
511 message = 'Are you sure you want to permanently delete the ' + this.selected.length + ' files selected?';
397 // data because the outer scope's values change as we iterate through the loop.
512 }
398 var parent_item = that.parents('div.list_item');
513 var that = this;
399 var name = parent_item.data('name');
514 dialog.modal({
400 var path = parent_item.data('path');
515 title : "Delete",
401 var input = $('<input/>').attr('type','text').attr('size','25').addClass('form-control')
516 body : message,
402 .val(path);
517 buttons : {
403 var dialog_body = $('<div/>').append(
518 Delete : {
404 $("<p/>").addClass("rename-message")
519 class: "btn-danger",
405 .text('Enter a new directory name:')
520 click: function() {
406 ).append(
521 that.selected.forEach(function(item) {
407 $("<br/>")
522 that.contents.delete(item.path).then(function() {
408 ).append(input);
523 that.notebook_deleted(item.path);
409 var d = dialog.modal({
410 title : "Rename directory",
411 body : dialog_body,
412 buttons : {
413 OK : {
414 class: "btn-primary",
415 click: function() {
416 notebooklist.contents.rename(path, input.val()).then(function() {
417 notebooklist.load_list();
418 }).catch(function(e) {
524 }).catch(function(e) {
419 dialog.modal({
525 dialog.modal({
420 title : "Error",
526 title : "Error",
421 body : $('<div/>')
527 body : $('<div/>')
422 .text("An error occurred while renaming \"" + path + "\" to \"" + input.val() + "\".")
528 .text("An error occurred while deleting \"" + item.path + "\".")
423 .append($('<div/>').addClass('alert alert-danger').text(String(e))),
529 .append($('<div/>').addClass('alert alert-danger').text(String(e))),
424 buttons : {
530 buttons : {
425 OK : {}
531 OK : {}
426 }
532 }
427 });
533 });
428 });
534 });
429 }
535 });
430 },
536 }
431 Cancel : {}
432 },
537 },
433 open : function () {
538 Cancel : {}
434 // Upon ENTER, click the OK button.
539 }
435 input.keydown(function (event) {
540 });
436 if (event.which === keyboard.keycodes.enter) {
437 d.find('.btn-primary').first().click();
438 return false;
439 }
440 });
441 input.focus().select();
442 }
443 });
444 return false;
445 };
446 };
541 };
447
542
448 NotebookList.prototype.make_delete_callback = function (item) {
543 NotebookList.prototype.duplicate_selected = function() {
449 var notebooklist = this;
544 var message;
450 return function (e) {
545 if (this.selected.length == 1) {
451 // $(this) is the button that was clicked.
546 message = 'Are you sure you want to duplicate: ' + this.selected[0].name + '?';
452 var that = $(this);
547 } else {
453 // We use the filename from the parent list_item element's
548 message = 'Are you sure you want to duplicate the ' + this.selected.length + ' files selected?';
454 // data because the outer scope's values change as we iterate through the loop.
549 }
455 var parent_item = that.parents('div.list_item');
550 var that = this;
456 var name = parent_item.data('name');
551 dialog.modal({
457 var path = parent_item.data('path');
552 title : "Delete",
458 var message = 'Are you sure you want to permanently delete: ' + name + '?';
553 body : message,
459 dialog.modal({
554 buttons : {
460 title : "Delete",
555 Duplicate : {
461 body : message,
556 class: "btn-primary",
462 buttons : {
557 click: function() {
463 Delete : {
558 that.selected.forEach(function(item) {
464 class: "btn-danger",
559 that.contents.copy(item.path, that.notebook_path).then(function () {
465 click: function() {
560 that.load_list();
466 notebooklist.contents.delete(path).then(function() {
467 notebooklist.notebook_deleted(path);
468 }).catch(function(e) {
561 }).catch(function(e) {
469 dialog.modal({
562 dialog.modal({
470 title : "Error",
563 title : "Error",
471 body : $('<div/>')
564 body : $('<div/>')
472 .text("An error occurred while deleting \"" + path + "\".")
565 .text("An error occurred while copying \"" + item.path + "\".")
473 .append($('<div/>').addClass('alert alert-danger').text(String(e))),
566 .append($('<div/>').addClass('alert alert-danger').text(String(e))),
474 buttons : {
567 buttons : {
475 OK : {}
568 OK : {}
476 }
569 }
477 });
570 });
478 });
571 });
479 }
572 });
480 },
573 }
481 Cancel : {}
574 },
482 }
575 Cancel : {}
483 });
576 }
484 return false;
577 });
485 };
486 };
487
488 NotebookList.prototype.make_duplicate_callback = function (item) {
489 var notebooklist = this;
490 return function (e) {
491 // $(this) is the button that was clicked.
492 var that = $(this);
493 var name = item.data('name');
494 var path = item.data('path');
495 var message = 'Are you sure you want to duplicate ' + name + '?';
496 var copy_from = {copy_from : path};
497 IPython.dialog.modal({
498 title : "Duplicate " + name,
499 body : message,
500 buttons : {
501 Duplicate : {
502 class: "btn-primary",
503 click: function() {
504 notebooklist.contents.copy(path, notebooklist.notebook_path).then(function () {
505 notebooklist.load_list();
506 });
507 }
508 },
509 Cancel : {}
510 }
511 });
512 return false;
513 }
514 };
578 };
515
579
516 NotebookList.prototype.notebook_deleted = function(path) {
580 NotebookList.prototype.notebook_deleted = function(path) {
@@ -522,6 +586,7 b' define(['
522 if (element.data("path") == path) {
586 if (element.data("path") == path) {
523 element.remove();
587 element.remove();
524 events.trigger('notebook_deleted.NotebookList');
588 events.trigger('notebook_deleted.NotebookList');
589 this._selection_changed();
525 }
590 }
526 });
591 });
527 };
592 };
@@ -45,6 +45,10 b' ul.breadcrumb {'
45 }
45 }
46 }
46 }
47
47
48 .dynamic-buttons {
49 display: inline-block;
50 }
51
48 .list_toolbar [class*="span"] {
52 .list_toolbar [class*="span"] {
49 min-height: @btn_small_height;
53 min-height: @btn_small_height;
50 }
54 }
@@ -88,6 +92,10 b' ul.breadcrumb {'
88 padding-left: @dashboard_lr_pad;
92 padding-left: @dashboard_lr_pad;
89 padding-right: @dashboard_lr_pad;
93 padding-right: @dashboard_lr_pad;
90 line-height: @btn_mini_height;
94 line-height: @btn_mini_height;
95
96 input {
97 margin-right: @dashboard_lr_pad;
98 }
91 }
99 }
92
100
93 .item_name {
101 .item_name {
@@ -102,11 +110,15 b' ul.breadcrumb {'
102 }
110 }
103
111
104 .item_buttons {
112 .item_buttons {
113 padding-top: @dashboard_tb_pad;
105 line-height: 1em;
114 line-height: 1em;
115 .btn-toolbar();
106 .btn {
116 .btn {
107 min-width: 13ex;
117 min-width: 13ex;
108 }
118 }
109 .btn-toolbar();
119 .running-indicator {
120 color: @brand-success;
121 }
110 }
122 }
111
123
112 .toolbar_info {
124 .toolbar_info {
@@ -225,3 +237,19 b' ul#new-menu {'
225 }
237 }
226 }
238 }
227 }
239 }
240
241 .delete-button {
242 display: none;
243 }
244
245 .duplicate-button {
246 display: none;
247 }
248
249 .rename-button {
250 display: none;
251 }
252
253 .shutdown-button {
254 display: none;
255 }
@@ -37,6 +37,12 b' data-terminals-available="{{terminals_available}}"'
37 </div>
37 </div>
38 <div class="col-sm-4 no-padding tree-buttons">
38 <div class="col-sm-4 no-padding tree-buttons">
39 <div class="pull-right">
39 <div class="pull-right">
40 <div class="dynamic-buttons">
41 <button title="Duplicate selected" class="duplicate-button btn btn-default btn-xs">Duplicate</button>
42 <button title="Rename selected" class="rename-button btn btn-default btn-xs">Rename</button>
43 <button title="Shutdown selected notebook(s)" class="shutdown-button btn btn-default btn-xs btn-warning">Shutdown</button>
44 <button title="Deleted selected" class="delete-button btn btn-default btn-xs btn-danger">Delete</button>
45 </div>
40 <div id="new-buttons" class="btn-group">
46 <div id="new-buttons" class="btn-group">
41 <button class="dropdown-toggle btn btn-default btn-xs" data-toggle="dropdown">
47 <button class="dropdown-toggle btn btn-default btn-xs" data-toggle="dropdown">
42 <span>New</span>
48 <span>New</span>
General Comments 0
You need to be logged in to leave comments. Login now