diff --git a/customize.dist/src/less2/include/alertify.less b/customize.dist/src/less2/include/alertify.less
index e40c1aa14..f668f1735 100644
--- a/customize.dist/src/less2/include/alertify.less
+++ b/customize.dist/src/less2/include/alertify.less
@@ -293,6 +293,9 @@
&:not(:first-child) {
margin-left: @alertify_padding-base !important;
}
+ &.left {
+ float: left;
+ }
}
}
}
diff --git a/www/kanban/app-kanban.less b/www/kanban/app-kanban.less
index 8014f0f6a..2d10ca6ce 100644
--- a/www/kanban/app-kanban.less
+++ b/www/kanban/app-kanban.less
@@ -219,7 +219,6 @@
}
}
#cp-kanban-controls {
- height: 40px;
padding: 10px;
display: flex;
position: relative;
@@ -228,9 +227,36 @@
position: relative;
.cp-kanban-filterTags {
display: inline-flex;
- width: 250px;
- height: 40px;
- align-items: center;
+ align-items: baseline;
+ flex: 1;
+ max-width: 80%;
+ min-width: 150px;
+
+ &> i {
+ margin-left: 10px;
+ }
+ .cp-kanban-filterTags-name {
+ flex-shrink: 0;
+ }
+ .cp-kanban-filterTags-list {
+ margin-left: 10px;
+ display: flex;
+ flex-wrap: wrap;
+ span {
+ .tools_unselectable();
+ padding: 0 5px;
+ margin-right: 5px;
+ margin-top: 5px;
+ background-color: rgba(0,0,0,0.1);
+ display: inline-block;
+ font-size: 14px;
+ cursor: pointer;
+ &.active {
+ background-color: @cryptpad_text_col;
+ color: #fff;
+ }
+ }
+ }
&> .tokenfield {
border-radius: 0px;
diff --git a/www/kanban/inner.js b/www/kanban/inner.js
index a0f4e100e..3fdb09cd2 100644
--- a/www/kanban/inner.js
+++ b/www/kanban/inner.js
@@ -51,6 +51,7 @@ define([
var verbose = function (x) { console.log(x); };
verbose = function () {}; // comment out to enable verbose logging
+ var onRedraw = Util.mkEvent();
Messages.kanban_title = "Title"; // XXX
Messages.kanban_body = "Body"; // XXX
@@ -60,9 +61,9 @@ define([
Messages.kanban_tags = "Filter tags"; // XXX
// XXX
-// Tag filter:
-// remember tags in padAttribute
-// click on a tag ==> add it to the list
+// Conflicts
+// use cursor channel to tell others what you are editing
+// add outline + warning inside the modal?
var setValueAndCursor = function (input, val, _cursor) {
if (!input) { return; }
@@ -259,7 +260,7 @@ define([
};
var button = [{
- className: 'danger', // XXX align left
+ className: 'danger left',
name: Messages.kanban_delete,
onClick: function () {
var boards = kanban.options.boards || {};
@@ -432,6 +433,9 @@ define([
widthBoard: '300px',
buttonContent: '❌',
readOnly: framework.isReadOnly(),
+ refresh: function () {
+ onRedraw.fire();
+ },
onChange: function () {
verbose("Board object has changed");
framework.localChange();
@@ -607,7 +611,7 @@ define([
}
kanban.inEditMode = "new";
// create a form to enter element
- var boardId = $(el.parentNode.parentNode).attr("data-id");
+ var boardId = $(el).closest('.kanban-board').attr("data-id");
var $item = $('
', {'class': 'kanban-item new-item'});
var $input = getInput().val(name).appendTo($item);
kanban.addForm(boardId, $item[0]);
@@ -620,10 +624,14 @@ define([
kanban.inEditMode = false;
if (!$input.val()) { return; }
var id = Util.createRandomInteger();
- kanban.addElement(boardId, {
+ var item = {
"id": id,
"title": $input.val(),
- });
+ };
+ if (kanban.options.tags && kanban.options.tags.length) {
+ item.tags = kanban.options.tags;
+ }
+ kanban.addElement(boardId, item);
};
$input.blur(save);
$input.keydown(function (e) {
@@ -673,6 +681,7 @@ define([
var $container = $('#cp-app-kanban-content');
var addControls = function () {
+ // Quick or normal mode
var small = h('span.cp-kanban-view-small.fa.fa-minus');
var big = h('span.cp-kanban-view.fa.fa-bars');
$(small).click(function () {
@@ -686,42 +695,108 @@ define([
framework._.sfCommon.setPadAttribute('quickMode', false);
});
+ // Tags filter
var existing = getExistingTags(kanban.options.boards);
- var input = UI.dialog.textInput();
+ var list = h('div.cp-kanban-filterTags-list');
+ var reset = h('i.cp-kanban-filterTags-reset.fa.fa-times');
var tags = h('div.cp-kanban-filterTags', [
- Messages.kanban_tags,
- input
+ h('span.cp-kanban-filterTags-name', Messages.kanban_tags),
+ reset,
+ list
]);
- var field = UI.tokenField(input, existing).preventDuplicates(function (val) {
- UI.warn(Messages._getKey('tags_duplicate', [val]));
- });
- field.setTokens([]);
+ var $reset = $(reset);
+ var $list = $(list);
+
+ var getTags = function () {
+ return $list.find('span.active').map(function () {
+ return $(this).data('tag');
+ }).get();
+ };
var commitTags = function () {
- var t = field.getTokens();
+ var t = getTags();
+ if (t.length) {
+ $reset.show();
+ } else {
+ $reset.hide();
+ }
+ framework._.sfCommon.setPadAttribute('tagsFilter', t);
kanban.options.tags = t;
kanban.setBoards(kanban.options.boards);
addEditItemButton(framework, kanban);
};
- field.tokenfield.on('tokenfield:createdtoken', commitTags);
- field.tokenfield.on('tokenfield:editedoken', commitTags);
- field.tokenfield.on('tokenfield:removedtoken', commitTags);
+
+ var redrawList = function (allTags) {
+ if (!Array.isArray(allTags)) { return; }
+ $list.empty();
+ allTags.forEach(function (t) {
+ var tag;
+ $list.append(tag = h('span', {
+ 'data-tag': t
+ }, t));
+ var $tag = $(tag).click(function () {
+ if ($tag.hasClass('active')) {
+ $tag.removeClass('active');
+ } else {
+ $tag.addClass('active');
+ }
+ commitTags();
+ });
+ });
+ };
+ redrawList(existing);
+
+ var setTags = function (tags) {
+ $list.find('span').removeClass('active');
+ if (!Array.isArray(tags)) { return; }
+ tags.forEach(function (t, i) {
+ if (existing.indexOf(t) === -1) {
+ // This tag doesn't exist anymore
+ tags.splice(i, 1);
+ return;
+ }
+ $list.find('span').filter(function () {
+ return $(this).data('tag') === t;
+ }).addClass('active');
+ });
+ framework._.sfCommon.setPadAttribute('tagsFilter', tags);
+ };
+ $reset.hide().click(function () {
+ setTags([]);
+ commitTags();
+ });
var container = h('div#cp-kanban-controls', [
- tags, // XXX
+ tags,
h('div.cp-kanban-changeView', [
small,
big
])
]);
$container.prepend(container);
- return container;
+
+ onRedraw.reg(function () {
+ // Redraw if new tags have been added to items
+ var old = Sortify(existing);
+ var t = getTags();
+ existing = getExistingTags(kanban.options.boards);
+ if (old === Sortify(existing)) { return; } // No change
+ // New tags:
+ redrawList(existing);
+ setTags(t);
+ });
+ framework._.sfCommon.getPadAttribute('tagsFilter', function (err, res) {
+ if (!err && Array.isArray(res)) {
+ setTags(res);
+ commitTags();
+ }
+ });
+ framework._.sfCommon.getPadAttribute('quickMode', function (err, res) {
+ if (!err && res) {
+ $container.addClass('cp-kanban-quick');
+ }
+ });
};
addControls();
- framework._.sfCommon.getPadAttribute('quickMode', function (err, res) {
- if (!err && res) {
- $container.addClass('cp-kanban-quick');
- }
- });
return kanban;
};
@@ -795,7 +870,6 @@ define([
var end = input.selectionEnd;
var json = kanban.getBoardJSON(id) || kanban.getItemJSON(id);
- // XXX only title for now...
var oldVal = json && json.title;
return {
diff --git a/www/kanban/jkanban.js b/www/kanban/jkanban.js
index 1678328fb..38ceb99bd 100644
--- a/www/kanban/jkanban.js
+++ b/www/kanban/jkanban.js
@@ -73,6 +73,7 @@
colorClick: function (el, type) {},
addItemClick: function (el, boardId) {},
renderMd: function (md) {},
+ refresh: function () {},
onChange: function () {}
};
@@ -526,6 +527,7 @@
}
this.options.boards = boards;
this.addBoards();
+ self.options.refresh();
}
this.findBoard = function (id) {