Initial support for @user and #label use in new cards.

When creating a new [mini]card, typing `@` or `#` brings up an
auto-complete box for board members and labels which will get applied to
the card upon creation. These textual tags are removed from the card
title before saving to maintain sanity. If a label doesn't have a name,
it's colour is used (i.e. `red`, `purple`, etc).

This was developed to ease the creation of new cards and allow users to
rapidly create cards without having to click numerous times just to
apply labels & members.
This commit is contained in:
Kenton Hamaluik 2015-10-03 15:58:36 -06:00
parent 57fa7af24c
commit bfcfd2ebda

View file

@ -26,7 +26,7 @@ BlazeComponent.extendComponent({
const firstCardDom = this.find('.js-minicard:first');
const lastCardDom = this.find('.js-minicard:last');
const textarea = $(evt.currentTarget).find('textarea');
const title = textarea.val();
let title = textarea.val();
const position = Blaze.getData(evt.currentTarget).position;
let sortIndex;
if (position === 'top') {
@ -36,10 +36,41 @@ BlazeComponent.extendComponent({
}
if ($.trim(title)) {
// Parse for @user and #label mentions, stripping them from the title
// and applying the appropriate users and labels to the card instead.
const currentBoard = Boards.findOne(Session.get('currentBoard'));
// Find all @-mentioned usernames, collect a list of their IDs
// and strip their mention out of the title.
let foundUserIds = [];
currentBoard.members.forEach(member => {
const username = Users.findOne(member.userId).username;
let nameNdx = title.indexOf('@' + username);
if(nameNdx !== -1) {
foundUserIds.push(member.userId);
title = title.substr(0, nameNdx) + title.substr(nameNdx + username.length + 1);
}
});
// Find all #-mentioned labels (based on their colour or name),
// collect a list of their IDs, and strip their mention out of
// the title.
let foundLabelIds = [];
currentBoard.labels.forEach(label => {
const labelName = (!label.name || label.name === "") ? label.color : label.name;
let labelNdx = title.indexOf('#' + labelName);
if(labelNdx !== -1) {
foundLabelIds.push(label._id);
title = title.substr(0, labelNdx) + title.substr(labelNdx + labelName.length + 1);
}
});
const _id = Cards.insert({
title,
listId: this.data()._id,
boardId: this.data().board()._id,
labelIds: foundLabelIds,
members: foundUserIds,
sort: sortIndex,
});
// In case the filter is active we need to add the newly inserted card in
@ -100,12 +131,18 @@ BlazeComponent.extendComponent({
},
}).register('listBody');
let dropdownMenuIsOpened = false;
BlazeComponent.extendComponent({
template() {
return 'addCardForm';
},
pressKey(evt) {
// don't do anything if the drop down is showing
if(dropDownIsOpened) {
return;
}
// Pressing Enter should submit the card
if (evt.keyCode === 13) {
evt.preventDefault();
@ -140,4 +177,63 @@ BlazeComponent.extendComponent({
keydown: this.pressKey,
}];
},
onCreated() {
dropDownIsOpened = false;
},
onRendered() {
const $textarea = this.$('textarea');
$textarea.textcomplete([
// User mentions
{
match: /\B@(\w*)$/,
search(term, callback) {
const currentBoard = Boards.findOne(Session.get('currentBoard'));
callback($.map(currentBoard.members, (member) => {
const username = Users.findOne(member.userId).username;
return username.indexOf(term) === 0 ? username : null;
}));
},
template(value) {
return value;
},
replace(username) {
return `@${username} `;
},
index: 1,
},
// Labels
{
match: /\B#(\w*)$/,
search(term, callback) {
const currentBoard = Boards.findOne(Session.get('currentBoard'));
callback($.map(currentBoard.labels, (label) => {
const labelName = (!label.name || label.name === "") ? label.color : label.name;
return labelName.indexOf(term) === 0 ? labelName : null;
}));
},
template(value) {
return value;
},
replace(label) {
return `#${label} `;
},
index: 1,
},
]);
// customize hooks for dealing with the dropdowns
$textarea.on({
'textComplete:show'() {
dropDownIsOpened = true;
},
'textComplete:hide'() {
Tracker.afterFlush(() => {
dropDownIsOpened = false;
});
},
});
},
}).register('addCardForm');