Skip to content

Commit

Permalink
re-made how "select" mode works so instead of the input being editabl…
Browse files Browse the repository at this point in the history
…e, the actual tag is now visible and goes into edit-mode and the input is hidden
  • Loading branch information
yairEO committed Jan 27, 2024
1 parent ab10852 commit bacaeb0
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 35 deletions.
1 change: 1 addition & 0 deletions src/parts/dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ export default {

_s.hooks.beforeKeyDown(e, {tagify:this})
.then(result => {
console.log(e.key);
switch( e.key ){
case 'ArrowDown' :
case 'ArrowUp' :
Expand Down
47 changes: 32 additions & 15 deletions src/parts/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export default {

if( !this.listeners || (!unbind && this.listeners.global) ) return; // do not re-bind

// these events are global event should never be unbinded, unless the instance is destroyed:
// these events are global and should never be unbinded, unless the instance is destroyed:
this.listeners.global = this.listeners.global || [
{
type: this.isIE ? 'keydown' : 'input', // IE cannot register "input" events on contenteditable elements, so the "keydown" should be used instead..
Expand Down Expand Up @@ -142,7 +142,6 @@ export default {
eventData = {relatedTarget:e.relatedTarget},
isTargetSelectOption = this.state.actions.selectOption && (ddEnabled || !_s.dropdown.closeOnSelect),
isTargetAddNewBtn = this.state.actions.addNew && ddEnabled,
isRelatedTargetX = e.relatedTarget && isNodeTag.call(this, e.relatedTarget) && this.DOM.scope.contains(e.relatedTarget),
shouldAddTags;

if( type == 'blur' ){
Expand Down Expand Up @@ -182,7 +181,7 @@ export default {
if( type == "focus" ){
this.trigger("focus", eventData)
// e.target.classList.remove('placeholder');
if( _s.dropdown.enabled === 0 || !_s.userInput ){ // && _s.mode != "select"
if( (_s.dropdown.enabled === 0 || !_s.userInput) && !this.state.dropdown.visible ){ // && _s.mode != "select"
this.dropdown.show(this.value.length ? '' : undefined)
}
return
Expand All @@ -195,11 +194,6 @@ export default {
// when clicking the X button of a selected tag, it is unwanted for it to be added back
// again in a few more lines of code (shouldAddTags && addTags)
if( _s.mode == 'select' ) {
if( isRelatedTargetX ) {
this.removeTags()
text = '';
}

// if nothing has changed (same display value), do not add a tag
if( currentDisplayValue === text )
text = ''
Expand Down Expand Up @@ -713,10 +707,10 @@ export default {
return
}

else if( tagElm ){
else if( tagElm && !this.state.editing ){
this.trigger("click", { tag:tagElm, index:this.getNodeIndex(tagElm), data:getSetTagData(tagElm), event:e })

if( _s.editTags === 1 || _s.editTags.clicks === 1 )
if( _s.editTags === 1 || _s.editTags.clicks === 1 || _s.mode == 'select' )
this.events.callbacks.onDoubleClickScope.call(this, e)

return
Expand Down Expand Up @@ -839,14 +833,28 @@ export default {
this.setRangeAtStartEnd(false, newNode)
},

onEditTagClick( tagElm, e) {
this.events.callbacks.onClickScope.call(this, e)
},

onEditTagFocus( tagElm ){
this.state.editing = {
scope: tagElm,
input: tagElm.querySelector("[contenteditable]")
}
},

onEditTagBlur( editableElm ){
onEditTagBlur( editableElm, e ){
// if "relatedTarget" is the tag then do not continue as this should not be considered a "blur" event
var isRelatedTargetNodeTag = isNodeTag.call(this, e.relatedTarget)

// in "select-mode" when editing the tag's template to include more nodes other than the editable "span",
// clicking those elements should not be considered a blur event
if( isRelatedTargetNodeTag && e.relatedTarget.contains(e.target) ) {
this.dropdown.hide()
return
}

// is "ESC" key was pressed then the "editing" state should be `false` and if so, logic should not continue
// because "ESC" reverts the edited tag back to how it was (replace the node) before editing
if( !this.state.editing )
Expand Down Expand Up @@ -954,9 +962,16 @@ export default {
break
}
case 'Enter' :
case 'Tab' :
case 'Tab' : {
e.preventDefault()
e.target.blur()

var EDITED_TAG_BLUR_DELAY = 100;

// a setTimeout is used so when editing (in "select" mode) while the dropdown is shown and a suggestion is highlighted
// and ENTER key is pressed down - the `dropdown.hide` method won't be invoked immediately and unbind the dropdown's
// KEYDOWN "ENTER" before it has time to call the handler and select the suggestion.
setTimeout(e.target.blur, EDITED_TAG_BLUR_DELAY)
}
}
},

Expand All @@ -972,11 +987,13 @@ export default {
isEditingTag = tagElm.classList.contains(this.settings.classNames.tagEditing)
isReadyOnlyTag = tagElm.hasAttribute('readonly')

if( _s.mode != 'select' && !_s.readonly && !isEditingTag && !isReadyOnlyTag && this.settings.editTags )
if( !_s.readonly && !isEditingTag && !isReadyOnlyTag && this.settings.editTags )
this.editTag(tagElm)

this.toggleFocusClass(true)
this.trigger('dblclick', { tag:tagElm, index:this.getNodeIndex(tagElm), data:getSetTagData(tagElm) })

if( _s.mode != 'select' )
this.trigger('dblclick', { tag:tagElm, index:this.getNodeIndex(tagElm), data:getSetTagData(tagElm) })
},

/**
Expand Down
26 changes: 14 additions & 12 deletions src/tagify.js
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ Tagify.prototype = {

toggleFocusClass( force ){
this.toggleClass(this.settings.classNames.focus, !!force)
this.state.hasFocus = !!force
},

triggerChangeEvent,
Expand Down Expand Up @@ -513,13 +514,14 @@ Tagify.prototype = {
editableElm.setAttribute('contenteditable', true)
tagElm.classList.add( _s.classNames.tagEditing )

editableElm.addEventListener('focus', _CB.onEditTagFocus.bind(this, tagElm))
editableElm.addEventListener('blur', _CB.onEditTagBlur.bind(this, this.getTagTextNode(tagElm)))
editableElm.addEventListener('input', _CB.onEditTagInput.bind(this, editableElm))
editableElm.addEventListener('paste', _CB.onEditTagPaste.bind(this, editableElm))
editableElm.addEventListener('keydown', e => _CB.onEditTagkeydown.call(this, e, tagElm))
editableElm.addEventListener('compositionstart', _CB.onCompositionStart.bind(this))
editableElm.addEventListener('compositionend', _CB.onCompositionEnd.bind(this))
editableElm.addEventListener('click' , _CB.onEditTagClick.bind(this, tagElm))
editableElm.addEventListener('focus' , _CB.onEditTagFocus.bind(this, tagElm))
editableElm.addEventListener('blur' , _CB.onEditTagBlur.bind(this, this.getTagTextNode(tagElm)))
editableElm.addEventListener('input' , _CB.onEditTagInput.bind(this, editableElm))
editableElm.addEventListener('paste' , _CB.onEditTagPaste.bind(this, editableElm))
editableElm.addEventListener('keydown' , e => _CB.onEditTagkeydown.call(this, e, tagElm))
editableElm.addEventListener('compositionstart' , _CB.onCompositionStart.bind(this))
editableElm.addEventListener('compositionend' , _CB.onCompositionEnd.bind(this))

if( !opts.skipValidation )
isValid = this.editTagToggleValidity(tagElm)
Expand Down Expand Up @@ -593,15 +595,15 @@ Tagify.prototype = {
function veryfyTagTextProp() {
var tagTextProp = tagData[_s.tagTextProp];

if( tagTextProp ) {
return tagTextProp.trim() ? tagTextProp : false;
if( tagTextProp !== '' ) {
return tagTextProp.trim?.()
}

if( !(_s.tagTextProp in tagData) )
return tagData.value
}

if( tagElm && veryfyTagTextProp() ){
if( tagElm && veryfyTagTextProp() !== '' ){
tagElm = this.replaceTag(tagElm, tagData)
this.editTagToggleValidity(tagElm, tagData)

Expand Down Expand Up @@ -629,7 +631,7 @@ Tagify.prototype = {
* @param {Object} tagData [data to create new tag from]
*/
replaceTag(tagElm, tagData){
if( !tagData || !tagData.value )
if( !tagData || tagData.value === '' || tagData.value === undefined )
tagData = tagElm.__tagifyTagData

// if tag is invalid, make the according changes in the newly created element
Expand Down Expand Up @@ -1185,7 +1187,7 @@ Tagify.prototype = {
if( _s.enforceWhitelist && !this.isTagWhitelisted(tagData.value) )
return

this.input.set.call(this, tagData[_s.tagTextProp] || tagData.value, true)
// this.input.set.call(this, tagData[_s.tagTextProp] || tagData.value, true)

// place the caret at the end of the input, only if a dropdown option was selected (and not by manually typing another value and clicking "TAB")
if( this.state.actions.selectOption )
Expand Down
25 changes: 17 additions & 8 deletions src/tagify.scss
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@
transition : .13s ease-out;

> div{ // :not([contenteditable])
flex : 1;
vertical-align : top;
box-sizing : border-box;
max-width : 100%;
Expand Down Expand Up @@ -248,6 +249,10 @@
padding: 2px;
max-width: 350px;
}

&:only-child{
width: 100%;
}
}

&::before{
Expand Down Expand Up @@ -544,6 +549,8 @@
}

&--select{
cursor: default;

&::after{
$size: 16px;

Expand All @@ -568,18 +575,20 @@
}

#{$self}__tag{
position: absolute;
top: 0;
right: 1.8em;
bottom: 0;
flex: 1;
max-width: none;
margin-inline-end: 2em;
cursor: text;

div{
display: none;
&::before {
display: none;
}
}
}

#{$self}__input{
width: 100%;
+ #{$self}__input{
display: none;
}
}
}

Expand Down

0 comments on commit bacaeb0

Please sign in to comment.