From bfcfd2ebda283bed1caa973f27e1af6b81fe621b Mon Sep 17 00:00:00 2001 From: Kenton Hamaluik Date: Sat, 3 Oct 2015 15:58:36 -0600 Subject: 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. --- client/components/lists/listBody.js | 98 ++++++++++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) (limited to 'client/components/lists/listBody.js') diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index 2e00cb4f..9d6aab88 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -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'); -- cgit v1.2.3-1-g7c22 From d105da5bc776446045a04bad83a0bb9d4a3fe50c Mon Sep 17 00:00:00 2001 From: Kenton Hamaluik Date: Sat, 3 Oct 2015 15:59:13 -0600 Subject: Conformed to the 80-character line length limit. --- client/components/lists/listBody.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'client/components/lists/listBody.js') diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index 9d6aab88..8bc828fb 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -40,28 +40,30 @@ BlazeComponent.extendComponent({ // 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. + // 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); + 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. + // 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; + 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); + title = title.substr(0, labelNdx) + + title.substr(labelNdx + labelName.length + 1); } }); @@ -138,7 +140,7 @@ BlazeComponent.extendComponent({ }, pressKey(evt) { - // don't do anything if the drop down is showing + // Don't do anything if the drop down is showing if(dropDownIsOpened) { return; } -- cgit v1.2.3-1-g7c22 From 429686ef480a56039e0d1a6da0a8668755b2bfa5 Mon Sep 17 00:00:00 2001 From: Kenton Hamaluik Date: Sat, 3 Oct 2015 16:53:45 -0600 Subject: Made eslinter happy. --- client/components/lists/listBody.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'client/components/lists/listBody.js') diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index 8bc828fb..a96e964c 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -42,10 +42,10 @@ BlazeComponent.extendComponent({ // Find all @-mentioned usernames, collect a list of their IDs and strip // their mention out of the title. - let foundUserIds = []; - currentBoard.members.forEach(member => { + let foundUserIds = []; // eslint-disable-line prefer-const + currentBoard.members.forEach((member) => { const username = Users.findOne(member.userId).username; - let nameNdx = title.indexOf('@' + username); + const nameNdx = title.indexOf(`@${username}!`); if(nameNdx !== -1) { foundUserIds.push(member.userId); title = title.substr(0, nameNdx) @@ -55,11 +55,11 @@ BlazeComponent.extendComponent({ // 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 === "") + let foundLabelIds = []; // eslint-disable-line prefer-const + currentBoard.labels.forEach((label) => { + const labelName = (!label.name || label.name === '') ? label.color : label.name; - let labelNdx = title.indexOf('#' + labelName); + const labelNdx = title.indexOf(`#${labelName}`); if(labelNdx !== -1) { foundLabelIds.push(label._id); title = title.substr(0, labelNdx) @@ -141,7 +141,7 @@ BlazeComponent.extendComponent({ pressKey(evt) { // Don't do anything if the drop down is showing - if(dropDownIsOpened) { + if(dropdownMenuIsOpened) { return; } @@ -181,7 +181,7 @@ BlazeComponent.extendComponent({ }, onCreated() { - dropDownIsOpened = false; + dropdownMenuIsOpened = false; }, onRendered() { @@ -212,7 +212,7 @@ BlazeComponent.extendComponent({ 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; + const labelName = (!label.name || label.name === '') ? label.color : label.name; return labelName.indexOf(term) === 0 ? labelName : null; })); }, @@ -229,11 +229,11 @@ BlazeComponent.extendComponent({ // customize hooks for dealing with the dropdowns $textarea.on({ 'textComplete:show'() { - dropDownIsOpened = true; + dropdownMenuIsOpened = true; }, 'textComplete:hide'() { Tracker.afterFlush(() => { - dropDownIsOpened = false; + dropdownMenuIsOpened = false; }); }, }); -- cgit v1.2.3-1-g7c22 From e4c5d2cbe64f42f1fc2e49aea6a9a25bc0d686cb Mon Sep 17 00:00:00 2001 From: Kenton Hamaluik Date: Sat, 3 Oct 2015 16:56:27 -0600 Subject: Fixed typo in template for quick-adding a user. --- client/components/lists/listBody.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'client/components/lists/listBody.js') diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index a96e964c..61e26975 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -45,7 +45,7 @@ BlazeComponent.extendComponent({ let foundUserIds = []; // eslint-disable-line prefer-const currentBoard.members.forEach((member) => { const username = Users.findOne(member.userId).username; - const nameNdx = title.indexOf(`@${username}!`); + const nameNdx = title.indexOf(`@${username}`); if(nameNdx !== -1) { foundUserIds.push(member.userId); title = title.substr(0, nameNdx) -- cgit v1.2.3-1-g7c22 From 77ca52d8c20211170a0f6d28e751768a4f9c3b8c Mon Sep 17 00:00:00 2001 From: Kenton Hamaluik Date: Thu, 8 Oct 2015 12:22:03 -0600 Subject: Fixed issue with possible race condition, suggested by @mquandalle --- client/components/lists/listBody.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'client/components/lists/listBody.js') diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index 61e26975..7c524b93 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -45,11 +45,9 @@ BlazeComponent.extendComponent({ let foundUserIds = []; // eslint-disable-line prefer-const currentBoard.members.forEach((member) => { const username = Users.findOne(member.userId).username; - const nameNdx = title.indexOf(`@${username}`); - if(nameNdx !== -1) { + if(title.indexOf(`@${username}`) !== -1) { foundUserIds.push(member.userId); - title = title.substr(0, nameNdx) - + title.substr(nameNdx + username.length + 1); + title = title.replace(`@${username}`, ''); } }); @@ -59,11 +57,9 @@ BlazeComponent.extendComponent({ currentBoard.labels.forEach((label) => { const labelName = (!label.name || label.name === '') ? label.color : label.name; - const labelNdx = title.indexOf(`#${labelName}`); - if(labelNdx !== -1) { + if(title.indexOf(`#${labelName}`) !== -1) { foundLabelIds.push(label._id); - title = title.substr(0, labelNdx) - + title.substr(labelNdx + labelName.length + 1); + title = title.replace(`#${labelName}`, ''); } }); -- cgit v1.2.3-1-g7c22 From a212b1310cf5c722a397e2c50af9a7b289e77e5a Mon Sep 17 00:00:00 2001 From: Kenton Hamaluik Date: Thu, 8 Oct 2015 12:22:55 -0600 Subject: Added space after if to conform to formatting --- client/components/lists/listBody.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'client/components/lists/listBody.js') diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index 7c524b93..ce095ed6 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -45,7 +45,7 @@ BlazeComponent.extendComponent({ let foundUserIds = []; // eslint-disable-line prefer-const currentBoard.members.forEach((member) => { const username = Users.findOne(member.userId).username; - if(title.indexOf(`@${username}`) !== -1) { + if (title.indexOf(`@${username}`) !== -1) { foundUserIds.push(member.userId); title = title.replace(`@${username}`, ''); } @@ -57,7 +57,7 @@ BlazeComponent.extendComponent({ currentBoard.labels.forEach((label) => { const labelName = (!label.name || label.name === '') ? label.color : label.name; - if(title.indexOf(`#${labelName}`) !== -1) { + if (title.indexOf(`#${labelName}`) !== -1) { foundLabelIds.push(label._id); title = title.replace(`#${labelName}`, ''); } @@ -137,7 +137,7 @@ BlazeComponent.extendComponent({ pressKey(evt) { // Don't do anything if the drop down is showing - if(dropdownMenuIsOpened) { + if (dropdownMenuIsOpened) { return; } -- cgit v1.2.3-1-g7c22 From c2cb17c5dff153fb5b11572036f8acc3155ea7e3 Mon Sep 17 00:00:00 2001 From: Kenton Hamaluik Date: Thu, 8 Oct 2015 12:25:58 -0600 Subject: Now cards with *only* metadata aren't created empty --- client/components/lists/listBody.js | 54 ++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 27 deletions(-) (limited to 'client/components/lists/listBody.js') diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index ce095ed6..6c191a71 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -34,35 +34,35 @@ BlazeComponent.extendComponent({ } else if (position === 'bottom') { 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}`, ''); + } + }); - 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 = []; // 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}`, ''); - } - }); + // 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 ($.trim(title)) { const _id = Cards.insert({ title, listId: this.data()._id, -- cgit v1.2.3-1-g7c22 From f5be121cf368415652ec3bb14aeaccecab6f663e Mon Sep 17 00:00:00 2001 From: Kenton Hamaluik Date: Thu, 8 Oct 2015 12:32:31 -0600 Subject: Pressing escape while autocomplete is open no longer closes the minicard --- client/components/lists/listBody.js | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'client/components/lists/listBody.js') diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index 6c191a71..e311ac76 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -233,5 +233,10 @@ BlazeComponent.extendComponent({ }); }, }); + + EscapeActions.register('textcomplete', + () => {}, + () => dropdownMenuIsOpened + ); }, }).register('addCardForm'); -- cgit v1.2.3-1-g7c22 From fde2a39ee3dbbdfd5a6b648dbe17541343ac3b2a Mon Sep 17 00:00:00 2001 From: Kenton Hamaluik Date: Thu, 8 Oct 2015 13:10:46 -0600 Subject: Added coloured label badges in autocomplete list --- client/components/lists/listBody.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'client/components/lists/listBody.js') diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index e311ac76..e248f203 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -34,7 +34,7 @@ BlazeComponent.extendComponent({ } else if (position === 'bottom') { 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')); @@ -182,12 +182,12 @@ BlazeComponent.extendComponent({ onRendered() { const $textarea = this.$('textarea'); + const currentBoard = Boards.findOne(Session.get('currentBoard')); $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; @@ -206,14 +206,23 @@ BlazeComponent.extendComponent({ { 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; + const labelName = (!label.name || label.name === '') + ? label.color + : label.name; return labelName.indexOf(term) === 0 ? labelName : null; })); }, template(value) { - return value; + // 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; + return (colorName && colorName !== '') + ? `
${value}` + : value; }, replace(label) { return `#${label} `; -- cgit v1.2.3-1-g7c22 From 3507c6565bb16b5f45c6f269f7376902f8b1ff37 Mon Sep 17 00:00:00 2001 From: Kenton Hamaluik Date: Sat, 10 Oct 2015 23:08:50 -0600 Subject: Made colours light grey in the labels dropdown --- client/components/lists/listBody.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'client/components/lists/listBody.js') diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index e248f203..a60ffe25 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -214,14 +214,19 @@ BlazeComponent.extendComponent({ })); }, 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) + ? `${value}` + : value; return (colorName && colorName !== '') ? `
${value}` + title="${value}"> ${valueSpan}` : value; }, replace(label) { -- cgit v1.2.3-1-g7c22 From 5d77ad4f6ba70038486d734e97844c547e664e88 Mon Sep 17 00:00:00 2001 From: Maxime Quandalle Date: Sat, 31 Oct 2015 09:26:55 -0700 Subject: Finish the minicard editor auto-completion feature This commit stands on the initial support implemented in #342. We now avoid error-prone parsing step by adding the member or the label directly to the card object. We also added support for `Tab` to completion on our textComplete component. Closes #342 --- client/components/lists/listBody.js | 156 ++++++++++++++++-------------------- 1 file changed, 71 insertions(+), 85 deletions(-) (limited to 'client/components/lists/listBody.js') diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index 2ed5d38a..36b60d06 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -11,7 +11,7 @@ BlazeComponent.extendComponent({ options = options || {}; options.position = options.position || 'top'; - const forms = this.childrenComponents('inlinedForm'); + const forms = this.childComponents('inlinedForm'); let form = forms.find((component) => { return component.data().position === options.position; }); @@ -27,7 +27,9 @@ BlazeComponent.extendComponent({ const lastCardDom = this.find('.js-minicard:last'); const textarea = $(evt.currentTarget).find('textarea'); const position = this.currentData().position; - let title = textarea.val().trim(); + const title = textarea.val().trim(); + + const formComponent = this.childComponents('addCardForm')[0]; let sortIndex; if (position === 'top') { sortIndex = Utils.calculateIndex(null, firstCardDom).base; @@ -35,40 +37,16 @@ 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}`, ''); - } - }); + const members = formComponent.members.get(); + const labelIds = formComponent.labels.get(); if (title) { const _id = Cards.insert({ title, + members, + labelIds, 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 @@ -82,6 +60,8 @@ BlazeComponent.extendComponent({ if (position === 'bottom') { this.scrollToBottom(); } + + formComponent.reset(); } }, @@ -129,18 +109,40 @@ BlazeComponent.extendComponent({ }, }).register('listBody'); -let dropdownMenuIsOpened = false; +function toggleValueInReactiveArray(reactiveValue, value) { + const array = reactiveValue.get(); + const valueIndex = array.indexOf(value); + if (valueIndex === -1) { + array.push(value); + } else { + array.splice(valueIndex, 1); + } + reactiveValue.set(array); +} + BlazeComponent.extendComponent({ template() { return 'addCardForm'; }, - pressKey(evt) { - // Don't do anything if the drop down is showing - if (dropdownMenuIsOpened) { - return; - } + onCreated() { + this.labels = new ReactiveVar([]); + this.members = new ReactiveVar([]); + }, + reset() { + this.labels.set([]); + this.members.set([]); + }, + + getLabels() { + const currentBoardId = Session.get('currentBoard'); + return Boards.findOne(currentBoardId).labels.filter((label) => { + return this.labels.get().indexOf(label._id) > -1; + }); + }, + + pressKey(evt) { // Pressing Enter should submit the card if (evt.keyCode === 13) { evt.preventDefault(); @@ -176,28 +178,25 @@ BlazeComponent.extendComponent({ }]; }, - onCreated() { - dropdownMenuIsOpened = false; - }, - onRendered() { - const $textarea = this.$('textarea'); - const currentBoard = Boards.findOne(Session.get('currentBoard')); - $textarea.textcomplete([ + const editor = this; + this.$('textarea').escapeableTextComplete([ // 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; + const user = Users.findOne(member.userId); + return user.username.indexOf(term) === 0 ? user : null; })); }, - template(value) { - return value; + template(user) { + return user.username; }, - replace(username) { - return `@${username} `; + replace(user) { + toggleValueInReactiveArray(editor.members, user._id); + return ''; }, index: 1, }, @@ -206,51 +205,38 @@ BlazeComponent.extendComponent({ { 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; + if (label.name.indexOf(term) > -1 || + label.color.indexOf(term) > -1) { + return label; + } })); }, - 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) - ? `${value}` - : value; - return (colorName && colorName !== '') - ? `
${valueSpan}` - : value; + template(label) { + return Blaze.toHTMLWithData(Template.autocompleteLabelLine, { + hasNoName: !Boolean(label.name), + colorName: label.color, + labelName: label.name || label.color, + }); }, replace(label) { - return `#${label} `; + toggleValueInReactiveArray(editor.labels, label._id); + return ''; }, index: 1, }, - ]); - - // customize hooks for dealing with the dropdowns - $textarea.on({ - 'textComplete:show'() { - dropdownMenuIsOpened = true; - }, - 'textComplete:hide'() { - Tracker.afterFlush(() => { - dropdownMenuIsOpened = false; - }); + ], { + // When the autocomplete menu is shown we want both a press of both `Tab` + // or `Enter` to validation the auto-completion. We also need to stop the + // event propagation to prevent the card from submitting (on `Enter`) or + // going on the next column (on `Tab`). + onKeydown(evt, commands) { + if (evt.keyCode === 9 || evt.keyCode === 13) { + evt.stopPropagation(); + return commands.KEY_ENTER; + } }, }); - - EscapeActions.register('textcomplete', - () => {}, - () => dropdownMenuIsOpened - ); }, }).register('addCardForm'); -- cgit v1.2.3-1-g7c22