Skip to content

Commit

Permalink
- fixes #1420: when selecting a suggestion with the keyboard, keep th…
Browse files Browse the repository at this point in the history
…e same highlighted suggestion if `includeSelectedTags` setting is `true` or else, highlight the next/prev suggestion

- rehydrate suggestions list when selecting a suggestion with the mouse
  • Loading branch information
yairEO committed Dec 21, 2024
1 parent ca64ac7 commit 5746d7a
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 30 deletions.
58 changes: 40 additions & 18 deletions src/parts/suggestions.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export default {

// get the "active" element, and if there was none (yet) active, use first child
var _s = this.settings,
includeSelectedTags = _s.dropdown.includeSelectedTags,
selectedElm = this.DOM.dropdown.querySelector(_s.classNames.dropdownItemActiveSelector),
selectedElmData = this.dropdown.getSuggestionDataByNode(selectedElm),
isMixMode = _s.mode == 'mix',
Expand Down Expand Up @@ -138,10 +139,15 @@ export default {
_s.hooks.suggestionClick(e, {tagify:this, tagData:selectedElmData, suggestionElm:selectedElm})
.then(() => {
if( selectedElm ){
this.dropdown.selectOption(selectedElm)
// highlight next option
selectedElm = this.dropdown.getNextOrPrevOption(selectedElm, !actionUp)
this.dropdown.highlightOption(selectedElm)
var nextOrPrevOption = includeSelectedTags ? selectedElm : this.dropdown.getNextOrPrevOption(selectedElm, !actionUp),
nextOrPrevOptionValue = nextOrPrevOption.getAttribute('value')

this.dropdown.selectOption(selectedElm, e, () => {
// highlight next option
nextOrPrevOption = this.dropdown.getSuggestionNodeByValue(nextOrPrevOptionValue)
this.dropdown.highlightOption(nextOrPrevOption)
})

return
}
else
Expand Down Expand Up @@ -243,6 +249,11 @@ export default {
}
},

getSuggestionNodeByValue( value ){
var dropdownItems = this.dropdown.getAllSuggestionsRefs()
return dropdownItems.find(item => item.getAttribute('value') === value);
},

getNextOrPrevOption(selected, next = true) {
var dropdownItems = this.dropdown.getAllSuggestionsRefs(),
selectedIdx = dropdownItems.findIndex(item => item === selected);
Expand All @@ -253,7 +264,7 @@ export default {
/**
* mark the currently active suggestion option
* @param {Object} elm option DOM node
* @param {Boolean} adjustScroll when navigation with keyboard arrows (up/down), aut-scroll to always show the highlighted element
* @param {Boolean} adjustScroll when navigation with keyboard arrows (up/down), auto-scroll to always show the highlighted element
*/
highlightOption( elm, adjustScroll ){
var className = this.settings.classNames.dropdownItemActive,
Expand Down Expand Up @@ -298,8 +309,9 @@ export default {
* @param {Object} elm DOM node to select
* @param {Object} event The original Click event, if available (since keyboard ENTER key also triggers this method)
*/
selectOption( elm, event ){
selectOption( elm, event, onSelect ){
var _s = this.settings,
includeSelectedTags = _s.dropdown.includeSelectedTags,
{clearOnSelect, closeOnSelect} = _s.dropdown;

if( !elm ) {
Expand Down Expand Up @@ -349,16 +361,25 @@ export default {
closeOnSelect && setTimeout(this.dropdown.hide.bind(this))

// execute these tasks once a suggestion has been selected
elm.addEventListener('transitionend', () => {
this.dropdown.fillHeaderFooter()
setTimeout(() => {
elm.remove()
this.dropdown.refilter()
}, 100)
}, {once: true})

// hide selected suggestion
elm.classList.add(this.settings.classNames.dropdownItemHidden)
if(includeSelectedTags) {
onSelect && onSelect()
}

// if the selected suggestion is removed after being selected, more things things needs to be done:
else {
elm.addEventListener('transitionend', () => {
this.dropdown.fillHeaderFooter()

setTimeout(() => {
elm.remove()
this.dropdown.refilter()
onSelect && onSelect()
}, 100)
}, {once: true})

// hide selected suggestion
elm.classList.add(this.settings.classNames.dropdownItemHidden)
}
},

// adds all the suggested items, including the ones which are not currently rendered,
Expand All @@ -384,7 +405,8 @@ export default {

/**
* returns an HTML string of the suggestions' list items
* @param {String} value string to filter the whitelist by
* @param {String} value string t
* o filter the whitelist by
* @param {Object} options "exact" - for exact complete match
* @return {Array} list of filtered whitelist items according to the settings provided and current value
*/
Expand All @@ -396,7 +418,7 @@ export default {
exactMatchesList = [],
whitelist = _s.whitelist,
suggestionsCount = _sd.maxItems >= 0 ? _sd.maxItems : Infinity,
includeSelectedTags = _sd.includeSelectedTags || _s.mode == 'select',
includeSelectedTags = _sd.includeSelectedTags,
hasCustomSort = typeof _sd.sortby == 'function',
searchKeys = _sd.searchKeys,
whitelistItem,
Expand Down
28 changes: 16 additions & 12 deletions src/tagify.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,6 @@ Tagify.prototype = {

this.generateClassSelectors(_s.classNames)

if ( _s.dropdown.includeSelectedTags === undefined )
_s.dropdown.includeSelectedTags = _s.duplicates;

if( this.isIE )
_s.autoComplete = false; // IE goes crazy if this isn't false

Expand Down Expand Up @@ -184,13 +181,20 @@ Tagify.prototype = {

this.TEXTS = {...TEXTS, ...(_s.texts || {})}

// it makes sense to enable "includeSelectedTags" in "select-mode"
if( _s.mode == 'select' ){
_s.dropdown.includeSelectedTags = true
}

// make sure the dropdown will be shown on "focus" and not only after typing something (in "select" mode)
if( (_s.mode == 'select' && !settings.dropdown?.enabled) || !_s.userInput ){
_s.dropdown.enabled = 0
}

_s.dropdown.appendTarget = settings.dropdown?.appendTarget || document.body;

if ( _s.dropdown.includeSelectedTags === undefined )
_s.dropdown.includeSelectedTags = _s.duplicates;

// get & merge persisted data with current data
let persistedWhitelist = this.getPersistedData('whitelist');
Expand Down Expand Up @@ -601,14 +605,13 @@ Tagify.prototype = {
tagElm = tagElm || this.state.editing.scope
tagData = tagData || {}

var eventData = {
tag : tagElm,
index : this.getNodeIndex(tagElm),
previousData: getSetTagData(tagElm),
data : tagData
}

var _s = this.settings
var _s = this.settings,
eventData = {
tag : tagElm,
index : this.getNodeIndex(tagElm),
previousData: getSetTagData(tagElm),
data : tagData
}

this.trigger("edit:beforeUpdate", eventData, {cloneData:false})

Expand Down Expand Up @@ -694,6 +697,7 @@ Tagify.prototype = {
})

this.update()
this.dropdown.refilter()
},

/**
Expand Down Expand Up @@ -1428,7 +1432,7 @@ Tagify.prototype = {
this.setRangeAtStartEnd(false, this.DOM.input)
}

// refilter hydrate the list
// hydrate the suggestions list
this.dropdown.refilter()
return tagElems
},
Expand Down

0 comments on commit 5746d7a

Please sign in to comment.