summaryrefslogtreecommitdiffstats
path: root/client
diff options
context:
space:
mode:
Diffstat (limited to 'client')
-rw-r--r--client/components/forms/forms.styl7
-rw-r--r--client/components/lists/listBody.js115
2 files changed, 121 insertions, 1 deletions
diff --git a/client/components/forms/forms.styl b/client/components/forms/forms.styl
index 83d25370..2d92aca9 100644
--- a/client/components/forms/forms.styl
+++ b/client/components/forms/forms.styl
@@ -617,6 +617,13 @@ button
margin-right: 5px
vertical-align: middle
+ .minicard-label
+ width: 11px
+ height: @width
+ border-radius: 2px
+ margin-right: 3px
+ display: inline-block
+
&.active
background: #005377
diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js
index 25aeffcc..2ed5d38a 100644
--- a/client/components/lists/listBody.js
+++ b/client/components/lists/listBody.js
@@ -26,8 +26,8 @@ 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().trim();
const position = this.currentData().position;
+ let title = textarea.val().trim();
let sortIndex;
if (position === 'top') {
sortIndex = Utils.calculateIndex(null, firstCardDom).base;
@@ -35,11 +35,40 @@ BlazeComponent.extendComponent({
sortIndex = Utils.calculateIndex(lastCardDom, null).base;
}
+ // 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 = []; // eslint-disable-line prefer-const
+ currentBoard.members.forEach((member) => {
+ const username = Users.findOne(member.userId).username;
+ if (title.indexOf(`@${username}`) !== -1) {
+ foundUserIds.push(member.userId);
+ title = title.replace(`@${username}`, '');
+ }
+ });
+
+ // 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 = []; // eslint-disable-line prefer-const
+ currentBoard.labels.forEach((label) => {
+ const labelName = (!label.name || label.name === '')
+ ? label.color : label.name;
+ if (title.indexOf(`#${labelName}`) !== -1) {
+ foundLabelIds.push(label._id);
+ title = title.replace(`#${labelName}`, '');
+ }
+ });
+
if (title) {
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 +129,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 (dropdownMenuIsOpened) {
+ return;
+ }
+
// Pressing Enter should submit the card
if (evt.keyCode === 13) {
evt.preventDefault();
@@ -140,4 +175,82 @@ BlazeComponent.extendComponent({
keydown: this.pressKey,
}];
},
+
+ onCreated() {
+ dropdownMenuIsOpened = false;
+ },
+
+ onRendered() {
+ const $textarea = this.$('textarea');
+ const currentBoard = Boards.findOne(Session.get('currentBoard'));
+ $textarea.textcomplete([
+ // User mentions
+ {
+ match: /\B@(\w*)$/,
+ search(term, callback) {
+ 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) {
+ callback($.map(currentBoard.labels, (label) => {
+ const labelName = (!label.name || label.name === '')
+ ? label.color
+ : label.name;
+ return labelName.indexOf(term) === 0 ? labelName : null;
+ }));
+ },
+ template(value) {
+ // XXX the following is duplicated from editor.js and should be
+ // abstracted to keep things DRY
+ // add a "colour badge" in front of the label name
+ // but first, get the colour's name from its value
+ const colorName = currentBoard.labels.find((label) => {
+ return value === label.name || value === label.color;
+ }).color;
+ const valueSpan = (colorName === value)
+ ? `<span class="quiet">${value}</span>`
+ : value;
+ return (colorName && colorName !== '')
+ ? `<div class="minicard-label card-label-${colorName}"
+ title="${value}"></div> ${valueSpan}`
+ : value;
+ },
+ replace(label) {
+ return `#${label} `;
+ },
+ index: 1,
+ },
+ ]);
+
+ // customize hooks for dealing with the dropdowns
+ $textarea.on({
+ 'textComplete:show'() {
+ dropdownMenuIsOpened = true;
+ },
+ 'textComplete:hide'() {
+ Tracker.afterFlush(() => {
+ dropdownMenuIsOpened = false;
+ });
+ },
+ });
+
+ EscapeActions.register('textcomplete',
+ () => {},
+ () => dropdownMenuIsOpened
+ );
+ },
}).register('addCardForm');