Skip to content

Commit

Permalink
4.15.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Yair Even Or authored and Yair Even Or committed Jul 30, 2022
1 parent 37c4dc1 commit 9bc961b
Show file tree
Hide file tree
Showing 8 changed files with 207 additions and 77 deletions.
6 changes: 3 additions & 3 deletions dist/jQuery.tagify.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/react.tagify.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/tagify.css

Large diffs are not rendered by default.

133 changes: 99 additions & 34 deletions dist/tagify.esm.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Tagify (v 4.14.1) - tags input component
* Tagify (v 4.15.0) - tags input component
* By Yair Even-Or
* https://github.com/yairEO/tagify
* Permission is hereby granted, free of charge, to any person obtaining a copy
Expand Down Expand Up @@ -248,7 +248,7 @@ var DEFAULTS = {
callbacks: {},
// Exposed callbacks object to be triggered on certain events
addTagOnBlur: true,
// Flag - automatically adds the text which was inputed as a tag when blur event happens
// automatically adds the text which was inputed as a tag when blur event happens
onChangeAfterBlur: true,
// By default, the native way of inputs' onChange events is kept, and it only fires when the field is blured.
duplicates: false,
Expand All @@ -258,11 +258,13 @@ var DEFAULTS = {
blacklist: [],
// A list of non-allowed tags
enforceWhitelist: false,
// Flag - Only allow tags from the whitelist
// Only allow tags from the whitelist
userInput: true,
// Flag - disable manually typing/pasting/editing tags (tags may only be added from the whitelist)
// disable manually typing/pasting/editing tags (tags may only be added from the whitelist)
keepInvalidTags: false,
// Flag - if true, do not remove tags which did not pass validation
// if true, do not remove tags which did not pass validation
createInvalidTags: true,
// if false, do not create invalid tags from invalid user input
mixTagsAllowedAfter: /,|\.|\:|\s/,
// RegEx - Define conditions in which mix-tags content allows a tag to be added after
mixTagsInterpolator: ['[[', ']]'],
Expand Down Expand Up @@ -317,6 +319,7 @@ var DEFAULTS = {
dropdownFooter: 'tagify__dropdown__footer',
dropdownItem: 'tagify__dropdown__item',
dropdownItemActive: 'tagify__dropdown__item--active',
dropdownItemHidden: 'tagify__dropdown__item--hidden',
dropdownInital: 'tagify__dropdown--initial',
tag: 'tagify__tag',
tagText: 'tagify__tag-text',
Expand Down Expand Up @@ -363,7 +366,15 @@ function initDropdown() {
var _dropdown = {
refs() {
this.DOM.dropdown = this.parseTemplate('dropdown', [this.settings]);
this.DOM.dropdown.content = this.DOM.dropdown.querySelector("[data-selector='tagify-dropdown-wrapper']");
this.DOM.dropdown.content = this.DOM.dropdown.querySelector("[data-selector='tagify-suggestions-wrapper']");
},

getHeaderRef() {
return this.DOM.dropdown.querySelector("[data-selector='tagify-suggestions-header']");
},

getFooterRef() {
return this.DOM.dropdown.querySelector("[data-selector='tagify-suggestions-footer']");
},

/**
Expand Down Expand Up @@ -527,7 +538,7 @@ var _dropdown = {
},

/**
*
* re-renders the dropdown content element (see "dropdownContent" in templates file)
* @param {String/Array} HTMLContent - optional
*/
fill(HTMLContent) {
Expand All @@ -536,9 +547,26 @@ var _dropdown = {
this.DOM.dropdown.content.innerHTML = minify(dropdownContent);
},

/**
* Re-renders only the header & footer.
* Used when selecting a suggestion and it is wanted that the suggestions dropdown stays open.
* Since the list of sugegstions is not being re-rendered completely every time a suggestion is selected (the item is transitioned-out)
* then the header & footer should be kept in sync with the suggestions data change
*/
fillHeaderFooter() {
this.settings.templates;
var suggestions = this.dropdown.filterListItems(this.state.dropdown.query),
newHeaderElem = this.parseTemplate('dropdownHeader', [suggestions]),
newFooterElem = this.parseTemplate('dropdownFooter', [suggestions]),
headerRef = this.dropdown.getHeaderRef(),
footerRef = this.dropdown.getFooterRef();
newHeaderElem && headerRef?.parentNode.replaceChild(newHeaderElem, headerRef);
newFooterElem && footerRef?.parentNode.replaceChild(newFooterElem, footerRef);
},

/**
* fill data into the suggestions list
* (mainly used to update the list when removing tags, so they will be re-added to the list. not efficient)
* (mainly used to update the list when removing tags while the suggestions dropdown is visible, so they will be re-added to the list. not efficient)
*/
refilter(value) {
value = value || this.state.dropdown.query || '';
Expand Down Expand Up @@ -742,7 +770,7 @@ var _dropdown = {
tagData: selectedElmData,
suggestionElm: selectedElm
}).then(() => {
if (selectedElm) this.dropdown.selectOption(selectedElm);else this.dropdown.hide();
if (selectedElm) this.dropdown.selectOption(selectedElm, e);else this.dropdown.hide();
}).catch(err => console.warn(err));
},

Expand Down Expand Up @@ -802,8 +830,9 @@ var _dropdown = {
/**
* Create a tag from the currently active suggestion option
* @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) {
selectOption(elm, event) {
var _this$settings$dropdo = this.settings.dropdown,
clearOnSelect = _this$settings$dropdo.clearOnSelect,
closeOnSelect = _this$settings$dropdo.closeOnSelect;
Expand All @@ -812,20 +841,23 @@ var _dropdown = {
this.addTags(this.state.inputText, true);
closeOnSelect && this.dropdown.hide();
return;
} // if in edit-mode, do not continue but instead replace the tag's text.
// the scenario is that "addTags" was called from a dropdown suggested option selected while editing
}

event = event || {}; // if in edit-mode, do not continue but instead replace the tag's text.
// the scenario is that "addTags" was called from a dropdown suggested option selected while editing

var tagifySuggestionIdx = elm.getAttribute('tagifySuggestionIdx'),
isNoMatch = tagifySuggestionIdx == 'noMatch',
tagData = this.suggestedListItems[+tagifySuggestionIdx];
tagData = this.suggestedListItems[+tagifySuggestionIdx]; // The below event must be triggered, regardless of anything else which might go wrong

this.trigger("dropdown:select", {
data: tagData,
elm
}); // The above event must be triggered, regardless of anything else which might go wrong
elm,
event
});

if (!tagifySuggestionIdx || !tagData && !isNoMatch) {
this.dropdown.hide();
closeOnSelect && setTimeout(this.dropdown.hide.bind(this));
return;
}

Expand All @@ -845,10 +877,15 @@ var _dropdown = {
this.DOM.input.focus();
this.toggleFocusClass(true);
});
closeOnSelect && setTimeout(this.dropdown.hide.bind(this)); // hide selected suggestion

if (closeOnSelect) {
setTimeout(this.dropdown.hide.bind(this));
} else this.dropdown.refilter();
elm.addEventListener('transitionend', () => {
this.dropdown.fillHeaderFooter();
setTimeout(() => elm.remove(), 100);
}, {
once: true
});
elm.classList.add(this.settings.classNames.dropdownItemHidden);
},

// adds all the suggested items, including the ones which are not currently rendered,
Expand Down Expand Up @@ -877,8 +914,6 @@ var _dropdown = {
var _s = this.settings,
_sd = _s.dropdown,
options = options || {},
value = _s.mode == 'select' && this.value.length && this.value[0][_s.tagTextProp] == value ? '' // do not filter if the tag, which is already selecetd in "select" mode, is the same as the typed text
: value,
list = [],
exactMatchesList = [],
whitelist = _s.whitelist,
Expand All @@ -890,6 +925,8 @@ var _dropdown = {
isDuplicate,
niddle,
i = 0;
value = _s.mode == 'select' && this.value.length && this.value[0][_s.tagTextProp] == value ? '' // do not filter if the tag, which is already selecetd in "select" mode, is the same as the typed text
: value;

if (!value || !searchKeys.length) {
list = _s.duplicates ? whitelist : whitelist.filter(item => !this.isTagDuplicate(isObject(item) ? item.value : item)); // don't include tags which have already been added.
Expand Down Expand Up @@ -963,11 +1000,11 @@ var _dropdown = {

/**
* Creates the dropdown items' HTML
* @param {Array} list [Array of Objects]
* @param {Array} sugegstionsList [Array of Objects]
* @return {String}
*/
createListHTML(optionsArr) {
return extend([], optionsArr).map((suggestion, idx) => {
createListHTML(sugegstionsList) {
return extend([], sugegstionsList).map((suggestion, idx) => {
if (typeof suggestion == 'string' || typeof suggestion == 'number') suggestion = {
value: suggestion
};
Expand Down Expand Up @@ -1074,7 +1111,7 @@ var templates = {
isManual = _sd.position == 'manual',
className = `${settings.classNames.dropdown}`;
return `<div class="${isManual ? "" : className} ${_sd.classname}" role="listbox" aria-labelledby="dropdown">
<div data-selector='tagify-dropdown-wrapper' class="${settings.classNames.dropdownWrapper}"></div>
<div data-selector='tagify-suggestions-wrapper' class="${settings.classNames.dropdownWrapper}"></div>
</div>`;
},

Expand All @@ -1096,11 +1133,10 @@ var templates = {
},

/**
* Example: <header data-selector='tagify-suggestions-header' class="${this.settings.classNames.dropdownHeader}"></header>
* @param {Array} suggestions An array of all the matched suggested items, including those which were sliced away due to the "dropdown.maxItems" setting
*/
dropdownHeader(suggestions) {
return '';
return `<header data-selector='tagify-suggestions-header' class="${this.settings.classNames.dropdownHeader}"></header>`;
},

dropdownFooter(suggestions) {
Expand All @@ -1113,6 +1149,19 @@ var templates = {
dropdownItemNoMatch: null
};

function cloneEvent(e) {
if (!e) return;
let clone = new Function();

for (let p in e) {
let d = Object.getOwnPropertyDescriptor(e, p);
if (d && (d.get || d.set)) Object.defineProperty(clone, p, d);else clone[p] = e[p];
}

Object.setPrototypeOf(clone, e);
return clone;
}

function EventDispatcher(instance) {
// Create a DOM EventTarget object
var target = document.createTextNode('');
Expand Down Expand Up @@ -1150,7 +1199,8 @@ function EventDispatcher(instance) {
value: data
};
eventData = opts.cloneData ? extend({}, eventData) : eventData;
eventData.tagify = this; // TODO: move the below to the "extend" function
eventData.tagify = this;
if (data.event) eventData.event = cloneEvent(data.event); // TODO: move the below to the "extend" function

if (data instanceof Object) for (var prop in data) if (data[prop] instanceof HTMLElement) eventData[prop] = data[prop];
e = new CustomEvent(eventName, {
Expand Down Expand Up @@ -2079,9 +2129,9 @@ function Tagify(input, settings) {
return mockInstance;
}

if (input.previousElementSibling && input.previousElementSibling.classList.contains('tagify')) {
console.warn('Tagify: ', 'input element is already Tagified', input);
return this;
if (input.__tagify) {
console.warn('Tagify: ', 'input element is already Tagified - Same instance is returned.', input);
return input.__tagify;
}

extend(this, EventDispatcher(this));
Expand Down Expand Up @@ -2116,6 +2166,7 @@ function Tagify(input, settings) {
this.events.customBinding.call(this);
this.events.binding.call(this);
input.autofocus && this.DOM.input.focus();
input.__tagify = this;
}

Tagify.prototype = {
Expand Down Expand Up @@ -2206,7 +2257,9 @@ Tagify.prototype = {
_s.pattern = new RegExp(input.pattern);
} catch (e) {} // Convert the "delimiters" setting into a REGEX object

if (this.settings.delimiters) {
if (_s.delimiters) {
_s._delimiters = _s.delimiters;

try {
_s.delimiters = new RegExp(this.settings.delimiters, "g");
} catch (e) {}
Expand Down Expand Up @@ -2357,6 +2410,7 @@ Tagify.prototype = {
this.events.unbindGlobal.call(this);
this.DOM.scope.parentNode.removeChild(this.DOM.scope);
this.DOM.originalInput.tabIndex = this.DOM.originalInput_tabIndex;
delete this.DOM.originalInput.__tagify;
this.dropdown.hide(true);
clearTimeout(this.dropdownHide__bindEventsTimeout);
clearInterval(this.listeners.main.originalInputValueObserverInterval);
Expand Down Expand Up @@ -2392,7 +2446,7 @@ Tagify.prototype = {
if (JSON.parse(value) instanceof Array) value = JSON.parse(value);
} catch (err) {}

this.addTags(value).forEach(tag => tag && tag.classList.add(_s.classNames.tagNoAnimation));
this.addTags(value, true).forEach(tag => tag && tag.classList.add(_s.classNames.tagNoAnimation));
}
} else this.postUpdate();

Expand Down Expand Up @@ -3182,6 +3236,7 @@ Tagify.prototype = {
addTags(tagsItems, clearInput, skipInvalid) {
var tagElems = [],
_s = this.settings,
aggregatedinvalidInput = [],
frag = document.createDocumentFragment();
skipInvalid = skipInvalid || _s.skipInvalid;

Expand Down Expand Up @@ -3226,6 +3281,11 @@ Tagify.prototype = {
});
if (tagData.__isValid == this.TEXTS.duplicate) // mark, for a brief moment, the tag (this this one) which THIS CURRENT tag is a duplcate of
this.flashTag(this.getTagElmByValue(tagData.value));

if (!_s.createInvalidTags) {
aggregatedinvalidInput.push(tagData.value);
return;
}
}

if ('readonly' in tagData) {
Expand Down Expand Up @@ -3270,7 +3330,8 @@ Tagify.prototype = {
this.update();

if (tagsItems.length && clearInput) {
this.input.set.call(this);
this.input.set.call(this, _s.createInvalidTags ? '' : aggregatedinvalidInput.join(_s._delimiters));
this.setRangeAtStartEnd();
}

_s.dropdown.enabled && this.dropdown.refilter();
Expand Down Expand Up @@ -3533,7 +3594,11 @@ Tagify.prototype = {
opts = opts || {};
this.value = [];
if (this.settings.mode == 'mix') this.DOM.input.innerHTML = '';else this.removeTagsFromDOM();
this.dropdown.refilter();
this.dropdown.position();
if (this.state.dropdown.visible) setTimeout(() => {
this.DOM.input.focus();
});

if (this.settings.mode == 'select') {
this.input.set.call(this);
Expand Down
Loading

0 comments on commit 9bc961b

Please sign in to comment.