diff options
Diffstat (limited to 'client')
-rw-r--r-- | client/components/forms/forms.styl | 7 | ||||
-rw-r--r-- | client/components/lists/listBody.js | 115 |
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'); |