Merge pull request #2048 from vidartf/tags-bar

Simple implementation of cell tags
Thomas Kluyver 9 years ago committed by GitHub
commit 27cfa290b3

@ -0,0 +1,251 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'notebook/js/celltoolbar',
'base/js/dialog',
], function(celltoolbar, dialog) {
"use strict";
var CellToolbar = celltoolbar.CellToolbar;
var array_difference = function(a, b) {
return a.filter(function(n) {
return b.indexOf(n) === -1;
});
}
var write_tag = function(cell, name, add) {
if (add) {
// Add to metadata
if (cell.metadata.tags === undefined) {
cell.metadata.tags = [];
} else if (cell.metadata.tags.indexOf(name) !== -1) {
// Tag already exists
return false;
}
cell.metadata.tags.push(name);
} else {
// Remove from metadata
if (!cell.metadata || !cell.metadata.tags) {
// No tags to remove
return false;
}
// Remove tag from tags list
var index = cell.metadata.tags.indexOf(name);
if (index !== -1) {
cell.metadata.tags.splice(index, 1);
}
// If tags list is empty, remove it
if (cell.metadata.tags.length === 0) {
delete cell.metadata.tags;
}
}
cell.events.trigger('set_dirty.Notebook', {value: true});
return true;
}
var preprocess_input = function(input) {
// Split on whitespace:
return input.split(/\s/);
}
var add_tag = function(cell, tag_container, on_remove) {
return function(name) {
if (name === '') {
// Skip empty strings
return;
}
// Write tag to metadata
var changed = write_tag(cell, name, true);
if (changed) {
// Make tag UI
var tag = make_tag(name, on_remove);
tag_container.append(tag);
var tag_map = jQuery.data(tag_container, "tag_map") || {};
tag_map[name] = tag;
jQuery.data(tag_container, 'tag_map', tag_map);
}
};
};
var remove_tag = function(cell, tag_container) {
return function(name) {
var changed = write_tag(cell, name, false);
if (changed) {
// Remove tag UI
var tag_map = jQuery.data(tag_container, "tag_map") || {};
var tag_UI = tag_map[name];
delete tag_map[name];
tag_UI.remove();
}
};
};
var init_tag_container = function(cell, tag_container, on_remove) {
var tag_list = cell.metadata.tags || [];
if (!Array.isArray(tag_list)) {
// We cannot make tags UI for this cell!
// Maybe someone else used "tags" for something?
return false; // Fail gracefully
}
var tag_map = {};
for (var i=0; i < tag_list.length; ++i) {
var tag_name = tag_list[i];
if (typeof tag_name !== 'string') {
// Unexpected type, disable toolbar for safety
return false;
}
var tag = make_tag(tag_name, on_remove);
tag_container.append(tag);
tag_map[tag_name] = tag;
}
jQuery.data(tag_container, 'tag_map', tag_map);
return true;
};
var make_tag = function(name, on_remove) {
var tag_UI = $('<span/>')
.addClass('cell-tag')
.text(name);
var remove_button = $('<a/>')
.addClass('remove-tag-btn')
.text('X')
.click( function(ev) {
if (ev.button === 0) {
on_remove(name);
return false;
}
});
tag_UI.append(remove_button);
return tag_UI;
};
// Single edit with button to add tags
var add_tag_edit = function(div, cell, on_add, on_remove) {
var button_container = $(div);
var text = $('<input/>').attr('type', 'text');
var button = $('<button />')
.addClass('btn btn-default btn-xs')
.text('Add tag')
.click(function() {
var tags = preprocess_input(text[0].value);
for (var i=0; i < tags.length; ++i) {
on_add(tags[i]);
}
// Clear input after adding:
text[0].value = '';
return false;
});
// Wire enter in input to button click
text.keyup(function(event){
if(event.keyCode == 13){
button.click();
}
});
var input_container = $('<span/>')
.addClass('tags-input')
add_dialog_button(input_container, cell, on_add, on_remove);
button_container.append(input_container
.append(text)
.append(button)
);
IPython.keyboard_manager.register_events(text);
};
var tag_dialog = function(cell, on_add, on_remove) {
var tag_list = cell.metadata.tags || [];
var message =
"Edit the list of tags below. All whitespace " +
"is treated as tag separators.";
var textarea = $('<textarea/>')
.attr('rows', '13')
.attr('cols', '80')
.attr('name', 'tags')
.text(tag_list.join('\n'));
var dialogform = $('<div/>').attr('title', 'Edit the tags')
.append(
$('<form/>').append(
$('<fieldset/>').append(
$('<label/>')
.attr('for','tags')
.text(message)
)
.append($('<br/>'))
.append(textarea)
)
);
var modal_obj = dialog.modal({
title: "Edit Tags",
body: dialogform,
default_button: "Cancel",
buttons: {
Cancel: {},
Edit: { class : "btn-primary",
click: function() {
var old_tags = cell.metadata.tags || [];
var new_tags = preprocess_input(textarea[0].value);
var added_tags = array_difference(new_tags, old_tags);
var removed_tags = array_difference(old_tags, new_tags);
for (var i=0; i < added_tags.length; ++i) {
on_add(added_tags[i]);
}
for (var i=0; i < removed_tags.length; ++i) {
on_remove(removed_tags[i]);
}
}
}
},
notebook: cell.notebook,
keyboard_manager: cell.keyboard_manager,
});
};
var add_dialog_button = function(container, cell, on_add, on_remove) {
var button = $('<button />')
.addClass('btn btn-default btn-xs tags-dialog-btn')
.text('...')
.click( function() {
tag_dialog(cell, on_add, on_remove);
return false;
});
container.append(button);
};
var add_tags_cellbar = function(div, cell) {
var button_container = $(div);
button_container.addClass('tags_button_container');
var tag_container = $('<span/>').
addClass('tag-container');
var on_remove = remove_tag(cell, tag_container);
var ok = init_tag_container(cell, tag_container, on_remove);
if (!ok) {
return;
}
button_container.append(tag_container);
var on_add = add_tag(cell, tag_container, on_remove);
add_tag_edit(div, cell, on_add, on_remove);
};
var register = function(notebook) {
CellToolbar.register_callback('tags.edit', add_tags_cellbar);
var tags_preset = [];
tags_preset.push('tags.edit');
CellToolbar.register_preset('Tags', tags_preset, notebook);
};
return {'register' : register};
});

@ -28,6 +28,7 @@ define([
'./celltoolbarpresets/rawcell',
'./celltoolbarpresets/slideshow',
'./celltoolbarpresets/attachments',
'./celltoolbarpresets/tags',
'./scrollmanager',
'./commandpalette',
'./shortcuteditor',
@ -54,6 +55,7 @@ define([
rawcell_celltoolbar,
slideshow_celltoolbar,
attachments_celltoolbar,
tags_celltoolbar,
scrollmanager,
commandpalette,
shortcuteditor
@ -190,6 +192,7 @@ define([
rawcell_celltoolbar.register(this);
slideshow_celltoolbar.register(this);
attachments_celltoolbar.register(this);
tags_celltoolbar.register(this);
var that = this;

@ -7,6 +7,7 @@
*/
@import "notebook.less";
@import "celltoolbar.less";
@import "tagbar.less";
@import "completer.less";
@import "kernelselector.less";
@import "menubar.less";

@ -0,0 +1,40 @@
.tags_button_container {
width: 100%;
display: flex;
}
.tag-container {
display: flex;
flex-direction: row;
flex-grow: 1;
overflow: hidden;
position: relative;
}
.tag-container > * {
margin: 0 4px;
}
.remove-tag-btn {
margin-left: 4px;
}
.tags-input {
display: flex;
}
.cell-tag:last-child:after {
content: "";
position: absolute;
right: 0;
width: 40px;
height: 100%;
/* Fade to background color of cell toolbar */
background: linear-gradient(to right, rgba(0,0,0,0), #EEE);
}
.tags-dialog-btn {
margin-right: 4px;
}
Loading…
Cancel
Save