Skip to content

Commit

Permalink
fixes #266
Browse files Browse the repository at this point in the history
  • Loading branch information
yairEO committed Aug 17, 2019
2 parents 2e26ae0 + f573fd1 commit 4e1d5fb
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 47 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,13 @@ with great performance and tiny code footprint.
> [See SCSS usecase & example](https://github.com/yairEO/tagify/pull/282)
## Selling points
* JS minified `~21kb` (`~7kb` GZIP)
* CSS minified `~7kb` (`~2kb` GZIP) - well-crafted flexible code
* Easily customized
* Easily change direction to RTL via the SCSS file only
* JS minified `~24kb` (`~7kb` GZIP)
* CSS minified `~5kb` (`~2kb` GZIP) - generated from SCSS with variables
* Easily customized, plenty of settings for common scenarios
* No other inputs are used beside the original, and its value is kept in sync
* ARIA accessibility support
* Exposed custom [events](#events)
* Easily change direction to RTL (via the SCSS file)
* Internet Explorer - A polyfill script can be used: `tagify.polyfills.min.js` in `/dist`

## What can Tagify do
Expand Down
2 changes: 1 addition & 1 deletion dist/jQuery.tagify.min.js

Large diffs are not rendered by default.

50 changes: 27 additions & 23 deletions dist/tagify.js
Original file line number Diff line number Diff line change
Expand Up @@ -841,7 +841,7 @@ Tagify.prototype = {
collect = false;
parsedMatch = match.slice(2).slice(0, -2);

if (this.isTagWhitelisted(parsedMatch) && !this.settings.duplicates && this.isTagDuplicate(parsedMatch) == -1) {
if (this.isTagWhitelisted(parsedMatch) && (this.settings.duplicates || this.isTagDuplicate(parsedMatch) === -1)) {
tagData = this.normalizeTags.call(this, parsedMatch)[0];
html = this.replaceMixStringWithTag(html, match, tagData).html; // value = value.replace(match, "[[" + tagData.value + "]]")
}
Expand Down Expand Up @@ -1037,6 +1037,8 @@ Tagify.prototype = {
* @param {Number} tranDuration [Transition duration in MS]
*/
removeTag: function removeTag() {
var _this6 = this;

var tagElm = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.getLastTag();
var silent = arguments.length > 1 ? arguments[1] : undefined;
var tranDuration = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 250;
Expand All @@ -1045,20 +1047,27 @@ Tagify.prototype = {
var tagData,
tagIdx = this.getNodeIndex(tagElm); // this.getTagIndexByValue(tagElm.textContent)

if (tranDuration && tranDuration > 10) animation();else removeNode();
var removeNode = function removeNode() {
if (!tagElm.parentNode) return;
tagElm.parentNode.removeChild(tagElm);

if (!silent) {
tagData = this.value.splice(tagIdx, 1)[0]; // remove the tag from the data object
if (!silent) {
tagData = _this6.value.splice(tagIdx, 1)[0]; // remove the tag from the data object

this.update(); // update the original input with the current value
_this6.update(); // update the original input with the current value

this.trigger('remove', {
tag: tagElm,
index: tagIdx,
data: tagData
});
this.dropdown.render.call(this);
}

_this6.trigger('remove', {
tag: tagElm,
index: tagIdx,
data: tagData
});

_this6.dropdown.render.call(_this6);
}
};

if (tranDuration && tranDuration > 10) animation();else removeNode();

function animation() {
tagElm.style.width = parseFloat(window.getComputedStyle(tagElm).width) + 'px';
Expand All @@ -1068,11 +1077,6 @@ Tagify.prototype = {

setTimeout(removeNode, 400);
}

function removeNode() {
if (!tagElm.parentNode) return;
tagElm.parentNode.removeChild(tagElm);
}
},
removeAllTags: function removeAllTags() {
this.value = [];
Expand Down Expand Up @@ -1230,7 +1234,7 @@ Tagify.prototype = {
},
callbacks: {
onKeyDown: function onKeyDown(e) {
var _this6 = this;
var _this7 = this;

// get the "active" element, and if there was none (yet) active, use first child
var activeListElm = this.DOM.dropdown.querySelector("[class$='--active']"),
Expand Down Expand Up @@ -1270,7 +1274,7 @@ Tagify.prototype = {
this.addTags([newValue], true);
this.dropdown.hide.call(this);
setTimeout(function () {
return _this6.DOM.input.focus();
return _this7.DOM.input.focus();
}, 100);
return false;
} else {
Expand All @@ -1284,7 +1288,7 @@ Tagify.prototype = {
if (e.target.className.includes('__item')) this.dropdown.highlightOption.call(this, e.target);
},
onClick: function onClick(e) {
var _this7 = this;
var _this8 = this;

var value, listItemElm;
if (e.button != 0 || e.target == this.DOM.dropdown) return; // allow only mouse left-clicks
Expand All @@ -1295,7 +1299,7 @@ Tagify.prototype = {
value = this.suggestedListItems[this.getNodeIndex(listItemElm)] || this.input.value;
this.addTags([value], true);
setTimeout(function () {
return _this7.DOM.input.focus();
return _this8.DOM.input.focus();
}, 100);
}

Expand Down Expand Up @@ -1330,7 +1334,7 @@ Tagify.prototype = {
* @return {[type]} [description]
*/
filterListItems: function filterListItems(value) {
var _this8 = this;
var _this9 = this;

var list = [],
whitelist = this.settings.whitelist,
Expand All @@ -1344,7 +1348,7 @@ Tagify.prototype = {

if (!value) {
return whitelist.filter(function (item) {
return _this8.isTagDuplicate(item.value || item) == -1;
return _this9.isTagDuplicate(item.value || item) == -1;
}) // don't include tags which have already been added.
.slice(0, suggestionsCount); // respect "maxItems" dropdown setting
}
Expand Down
2 changes: 1 addition & 1 deletion dist/tagify.min.js

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,10 @@ <h3>JAVASCRIPT</h3>
],
dropdown : {
enabled : 1
},
callbacks : {
add : console.log, // callback when adding a tag
remove : console.log // callback when removing a tag
}
})

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@yaireo/tagify",
"version": "2.23.4",
"homepage": "https://github.com/yairEO/tagify",
"description": "Convert an input field into a Tags element. Easy, customizable, with good performance and small code footprint.",
"description": "lightweight, efficient Tags input component in Vanilla JS / React / Angular [super customizable, tiny size & top performance]",
"_npmUser": {
"name": "vsync",
"email": "vsync.design@gmail.com"
Expand Down
28 changes: 14 additions & 14 deletions src/tagify.js
Original file line number Diff line number Diff line change
Expand Up @@ -856,7 +856,7 @@ Tagify.prototype = {

parsedMatch = match.slice(2).slice(0, -2);

if( this.isTagWhitelisted(parsedMatch) && !this.settings.duplicates && this.isTagDuplicate(parsedMatch) == -1 ){
if( this.isTagWhitelisted(parsedMatch) && (this.settings.duplicates || this.isTagDuplicate(parsedMatch) === -1) ){
tagData = this.normalizeTags.call(this, parsedMatch)[0];
html = this.replaceMixStringWithTag(html, match, tagData).html;
// value = value.replace(match, "[[" + tagData.value + "]]")
Expand Down Expand Up @@ -1058,19 +1058,24 @@ Tagify.prototype = {
if( !(tagElm instanceof HTMLElement) )
return;

var tagData,
let tagData,
tagIdx = this.getNodeIndex(tagElm); // this.getTagIndexByValue(tagElm.textContent)

if( tranDuration && tranDuration > 10 ) animation()
else removeNode();
const removeNode = () => {
if( !tagElm.parentNode ) return
tagElm.parentNode.removeChild(tagElm)

if( !silent ){
tagData = this.value.splice(tagIdx, 1)[0]; // remove the tag from the data object
this.update() // update the original input with the current value
this.trigger('remove', { tag:tagElm, index:tagIdx, data:tagData });
this.dropdown.render.call(this);
if( !silent ){
tagData = this.value.splice(tagIdx, 1)[0]; // remove the tag from the data object
this.update() // update the original input with the current value
this.trigger('remove', { tag:tagElm, index:tagIdx, data:tagData });
this.dropdown.render.call(this);
}
}

if( tranDuration && tranDuration > 10 ) animation()
else removeNode();

function animation(){
tagElm.style.width = parseFloat(window.getComputedStyle(tagElm).width) + 'px';
document.body.clientTop; // force repaint for the width to take affect before the "hide" class below
Expand All @@ -1079,11 +1084,6 @@ Tagify.prototype = {
// manual timeout (hack, since transitionend cannot be used because of hover)
setTimeout(removeNode, 400);
}

function removeNode(){
if( !tagElm.parentNode ) return
tagElm.parentNode.removeChild(tagElm)
}
},

removeAllTags(){
Expand Down
43 changes: 40 additions & 3 deletions test/tagify.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ let elmSelectors = {
firstTag : ".tagify__tag"
},

mixed : {
scope : ".tagify--mix",
input : ".tagify--mix .tagify__input",
firstTag : ".tagify--mix .tagify__tag",
originalInput : "[name=mix]",
},

countries : {
scope : ".countries",
input : ".tagify.countries .tagify__input",
Expand Down Expand Up @@ -47,6 +54,10 @@ afterEach(async () => {
await input.click({ clickCount: 3 })
await page.keyboard.press('Backspace')

await page.evaluate(() => {
location.reload(true)
})

// reset the tags
// TODO: use https://www.npmjs.com/package/jest-environment-puppeteer
})
Expand Down Expand Up @@ -189,14 +200,13 @@ describe("actions", () => {
}

let texts = await page.evaluate(getAllTagsTexts, elmSelectors);
console.log( texts )
expect(texts).toEqual(["css", "html", "javascript", "Java"]);
}, 0)

it("should add first dropdown suggestions item to tagify (Mouse click)", async () => {
await page.waitForSelector(elmSelectors.tagify.firstTag);
await input.type("ja");
// await page.type(elmSelectors.tagify.input, "ja");
// await input.type("ja");
await page.type(elmSelectors.tagify.input, "ja");
await page.click('.tagify__dropdown__item', { clickCount:1 });

function getAllTagsTexts(elmSelectors) {
Expand All @@ -212,6 +222,33 @@ describe("actions", () => {
// check events are fired...
})

describe("mixed-mode", () => {
it("should parse textarea into mixed-tags", async () => {
await page.waitForSelector(elmSelectors.mixed.originalInput);

const result = await page.$eval(elmSelectors.mixed.input, el => el.innerHTML);
const expected = `<tag title="Eric Cartman" contenteditable="false" spellcheck="false" class="tagify__tag borderd-blue" value="cartman"><x title="" class="tagify__tag__removeBtn" role="button" aria-label="remove tag"></x><div><span class="tagify__tag-text">cartman</span></div></tag>⁠ and <tag title="Kyle Broflovski" contenteditable="false" spellcheck="false" class="tagify__tag " value="kyle"><x title="" class="tagify__tag__removeBtn" role="button" aria-label="remove tag"></x><div><span class="tagify__tag-text">kyle</span></div></tag>⁠ do not know <tag title="Homer simpson" contenteditable="false" spellcheck="false" class="tagify__tag " value="Homer simpson"><x title="" class="tagify__tag__removeBtn" role="button" aria-label="remove tag"></x><div><span class="tagify__tag-text">Homer simpson</span></div></tag>⁠ because he's a relic.`

expect(result).toEqual(expected);
}, 0)

fit("should update textarea on deleted tag", async (done) => {
await page.waitForSelector(elmSelectors.mixed.firstTag);
await page.click(elmSelectors.mixed.firstTag + " .tagify__tag__removeBtn", { clickCount:1 });

const expectedTagifyInput = `⁠ and <tag title=\"Kyle Broflovski\" contenteditable=\"false\" spellcheck=\"false\" class=\"tagify__tag \" value=\"kyle\"><x title=\"\" class=\"tagify__tag__removeBtn\" role=\"button\" aria-label=\"remove tag\"></x><div><span class=\"tagify__tag-text\">kyle</span></div></tag>⁠ do not know <tag title=\"Homer simpson\" contenteditable=\"false\" spellcheck=\"false\" class=\"tagify__tag \" value=\"Homer simpson\"><x title=\"\" class=\"tagify__tag__removeBtn\" role=\"button\" aria-label=\"remove tag\"></x><div><span class=\"tagify__tag-text\">Homer simpson</span></div></tag>⁠ because he's a relic.`
const expectedTextareaValue = `⁠ and [[kyle]]⁠ do not know [[Homer simpson]]⁠ because he's a relic.`

setTimeout(async ()=>{
const tagifyInput = await page.$eval(elmSelectors.mixed.input, el => el.innerHTML);
const textareaValue = await page.$eval(elmSelectors.mixed.originalInput, el => el.value);
expect(tagifyInput).toEqual(expectedTagifyInput);
expect(textareaValue).toEqual(expectedTextareaValue);
done()
}, 100)
}, 0)
})

describe("unit tests", () => {
describe("dropdown", () => {
// it("filterListItems('j')", async () => {
Expand Down

0 comments on commit 4e1d5fb

Please sign in to comment.