├── .gitignore
├── README.md
├── dist
├── inputTags.jquery.min.js
└── inputTags.min.css
├── index.html
├── package-lock.json
├── package.json
├── src
├── index.js
├── inputTags.css
└── inputTags.less
└── webpack.mix.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | node_modules/*
3 | /.vs
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # inputTags.js
2 |
3 | ## Presentation
4 | [inputTags.js](http://betaweb.github.io/inputTags-jQuery-plugin/) is a simple jQuery plugin allowing to add, edit or remove tags in a tags list.
5 |
6 | ## Installation
7 | Make sure you include the script after the jQuery library.
8 | ```html
9 |
10 |
11 | ```
12 |
13 | ## Getting started
14 | Setting up [inputTags.js](http://betaweb.github.io/inputTags-jQuery-plugin/) is very easy. You just have to add this few lines below and that's it !
15 |
16 | **HTML**:
17 |
18 | ```html
19 |
20 | ```
21 |
22 | **JS**:
23 | ```js
24 | $('#tags').inputTags();
25 | ```
26 |
27 |
28 | ## Basic usage
29 |
30 | **Initialize plugin with custom tags**
31 | Allows you to add custom tags on plugin initialization.
32 | ```js
33 | $('#tags').inputTags({
34 | tags: ['jQuery', 'tags', 'plugin'] // Custom tags list
35 | });
36 | ```
37 |
38 | ## Advanced usage
39 |
40 | **Initialize plugin with tags autocomplete**
41 | Allows you to add a custom list from which the user can choose one or more tags.
42 | ```js
43 | $('#tags').inputTags({
44 | autocomplete: {
45 | values: ['Pellentesque', 'habitant', 'morbi', 'tristique', 'senectus'] // autocomplete list
46 | }
47 | });
48 | ```
49 |
50 | ### Methods
51 | **Coming soon...**
52 |
53 | ### Events
54 | **Add events listener on plugin initialization**
55 | ```js
56 | $('#tags').inputTags({
57 | init: function($elem) {
58 | console.log('Event called on plugin init', $elem);
59 | },
60 | create: function() {
61 | console.log('Event called when an item is created');
62 | },
63 | update: function() {
64 | console.log('Event called when an item is updated');
65 | },
66 | destroy: function() {
67 | console.log('Event called when an item is deleted');
68 | },
69 | selected: function() {
70 | console.log('Event called when an item is selected');
71 | },
72 | unselected: function() {
73 | console.log('Event called when an item is unselected');
74 | },
75 | change: function($elem) {
76 | console.log('Event called on item change', $elem);
77 | }
78 | });
79 | ```
80 |
81 | **Add events after plugin initialization**
82 | ```js
83 | $('#tags').inputTags().on('change', function($elem) {
84 | console.log('Event called on item change', $elem);
85 | });
86 | ```
87 | same as:
88 | ```js
89 | $('#tags').inputTags('event', 'change', function($elem) {
90 | console.log('Event called on item change', $elem);
91 | });
92 | ```
93 |
--------------------------------------------------------------------------------
/dist/inputTags.jquery.min.js:
--------------------------------------------------------------------------------
1 | (()=>{var t,e={419:()=>{function t(e){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(e)}var e;(e=jQuery).fn.inputTags=function(n){if("inputTags"in window||(window.inputTags={instances:[]}),window.inputTags.methods={tags:function(e,n){if(e){switch(t(e)){case"string":switch(e){case"_toString":var o=i.tags.toString();return n?n(o):o;case"_toObject":var s=i._toObject(i.tags);return n?n(s):s;case"_toJSON":s=i._toObject(i.tags);var a=JSON.stringify(s);return n?n(a):a;case"_toArray":return n?n(i.tags):i.tags}var r=e.split(",");if(r.length>1){var u=i.tags;i.tags=u.concat(r)}else i.tags.push(r[0]);break;case"object":u=i.tags,"[object Object]"===Object.prototype.toString.call(e)&&(e=Object.keys(e).map((function(t){return e[t]}))),i.tags=u.concat(e);break;case"function":return e(i.tags)}if(i._clean(),i._fill(),i._updateValue(),i.destroy(),i._setInstance(i),n)return n(i.tags)}return i.tags},event:function(t,e){i.options[t]=e,i._setInstance(i)},options:function(t,e){return t||e?e?(i.options[t]=e,void i._setInstance(i)):i.options[t]:i.options},destroy:function(){var t=e(this).attr("data-uniqid");delete window.inputTags.instances[t]}},"object"===t(n)||!n)return n=e.extend(!0,{},e.fn.inputTags.defaults,n),this.each((function(){var o=e(this);o.KEY_ENTER="Enter",o.KEY_COMMA=",",o.KEY_ESCAPE="Escape",o.UNIQID=Math.round(Date.now()/(494*Math.random()-54)),o.DEFAULT_CLASS="inputTags",o.ELEMENT_CLASS=o.DEFAULT_CLASS+"-"+o.UNIQID,o.LIST_CLASS=o.DEFAULT_CLASS+"-list",o.ITEM_CLASS=o.DEFAULT_CLASS+"-item",o.ITEM_CONTENT='%s×',o.FIELD_CLASS=o.DEFAULT_CLASS+"-field",o.ERROR_CLASS=o.DEFAULT_CLASS+"-error",o.ERROR_CONTENT='
%s
',o.AUTOCOMPLETE_LIST_CLASS=o.DEFAULT_CLASS+"-autocomplete-list",o.AUTOCOMPLETE_ITEM_CLASS=o.DEFAULT_CLASS+"-autocomplete-item",o.AUTOCOMPLETE_ITEM_CONTENT='%s',o.options=n,o.keys=[o.KEY_ENTER,o.KEY_COMMA,o.KEY_ESCAPE],o.tags=[],o.options.keys.length>0&&(o.keys=o.keys.concat(o.options.keys)),o.init=function(){o.addClass(o.ELEMENT_CLASS).attr("data-uniqid",o.UNIQID),o.$element=e("."+o.ELEMENT_CLASS),o.$element.hide(),o.build(),o.fill(),o.save(),o.edit(),o.destroy(),o._autocomplete._init(),o._focus()},o.build=function(){o.$html=e("").addClass(o.LIST_CLASS),o.$input=e("
").attr({type:"text",class:o.FIELD_CLASS}),o.$html.insertAfter(o.$element).prepend(o.$input),o.$list=o.$element.next("."+o.LIST_CLASS),o.$list.on("click",(function(t){if(e(t.target).hasClass("inputTags-field"))return!1;o.$input.focus()}))},o.fill=function(){if(o._getDefaultValues(),0===o.options.tags)return!1;o._concatenate(),o._updateValue(),o._fill()},o._fill=function(){o.tags.forEach((function(t,e){var n=o._validate(t,!1);(!0===n||"max"===n&&e+1<=o.options.max)&&o._buildItem(t)}))},o._clean=function(){e("."+o.ITEM_CLASS,o.$list).remove()},o.save=function(){o.$input.on("keyup",(function(t){t.preventDefault();var n=t.key,i=o.$input.val().trim();if(e.inArray(n,o.keys)<0)return o._autocomplete._init(!0),o._autocomplete._show(),!1;if(o.KEY_ESCAPE===n)return o._cancel(),o._autocomplete._hide(),!1;if(i=o.KEY_COMMA===n?i.slice(0,-1):i,!o._validate(i,!0))return!1;if(o.options.only&&o._exists(i))return o._errors("exists"),!1;if(o.$input.hasClass("is-edit")){var s=o.$input.attr("data-old-value");if(s===i)return o._cancel(),!0;o._update(s,i),o._clean(),o._fill()}else{if(o._autocomplete._isSet()&&o._autocomplete._get("only")&&e.inArray(i,o._autocomplete._get("values"))<0)return o._autocomplete._hide(),o._errors("autocomplete_only"),!1;if(o._exists(i)){o.$input.removeClass("is-autocomplete"),o._errors("exists");var a=e('[data-tag="'+i+'"]',o.$list);return a.addClass("is-exists"),setTimeout((function(){a.removeClass("is-exists")}),300),!1}o._buildItem(i),o._insert(i)}return o._cancel(),o._updateValue(),o.destroy(),o._autocomplete._build(),o._setInstance(o),o.$input.focus(),!1}))},o.edit=function(){o.$list.on("click","."+o.ITEM_CLASS,(function(t){if(e(t.target).hasClass("close-item")||!1===o.options.editable||o._autocomplete._isSet()&&o._autocomplete._get("only"))return o._cancel(),!0;var n=e(this).addClass("is-edit"),i=e(".value",n).text();o.$input.width(n.outerWidth()).insertAfter(n).addClass("is-edit").attr("data-old-value",i).val(i).focus(),o._bindEvent("selected"),o.$input.on("blur",(function(){o._cancel(),o._bindEvent("unselected")}))}))},o.destroy=function(){e("."+o.ITEM_CLASS,o.$list).off("click").on("click",".close-item",(function(){var t=e(this).parent("."+o.ITEM_CLASS),n=e(".value",t).text();t.addClass("is-closed"),setTimeout((function(){o._pop(n),o._updateValue(),t.remove(),o._autocomplete._build(),o.$input.focus(),o._setInstance(o)}),200)}))},o._buildItem=function(t){var n=e(o.ITEM_CONTENT.replace("%s",t));e("
").addClass(o.ITEM_CLASS+" is-closed").attr("data-tag",t).html(n).insertBefore(o.$input).delay(100).queue((function(){e(this).removeClass("is-closed")}))},o._getIndex=function(t){return o.tags.indexOf(t)},o._concatenate=function(){o.options.max>0&&o.options.tags.length>o.options.max&&o.options.tags.splice(-Math.abs(o.options.tags.length-o.options.max)),o.tags=o.tags.concat(o.options.tags)},o._getDefaultValues=function(){o.$element.val().length>0?o.tags=o.tags.concat(o.$element.val().split(o.KEY_COMMA)):o.$element.prop("value","")},o._insert=function(t){o.tags.push(t),o._bindEvent(["change","create"])},o._update=function(t,e){var n=o._getIndex(t);o.tags[n]=e,o._bindEvent(["change","update"])},o._pop=function(t){var e=o._getIndex(t);if(e<0)return!1;o.tags.splice(e,1),o._bindEvent(["change","destroy"])},o._cancel=function(){e("."+o.ITEM_CLASS).removeClass("is-edit"),o.$input.removeClass("is-edit is-autocomplete").removeAttr("data-old-value style").val("").appendTo(o.$list)},o._autocomplete={_isSet:function(){return o.options.autocomplete.values.length>0},_init:function(t){if(!o._autocomplete._isSet())return!1;o._autocomplete._build(t)},_build:function(t){var n=o.$input.val().trim().toLowerCase();o._autocomplete._exists()&&o.$autocomplete.remove(),o.$autocomplete=e("").addClass(o.AUTOCOMPLETE_LIST_CLASS),o._autocomplete._get("values").forEach((function(i){var s=o.AUTOCOMPLETE_ITEM_CONTENT.replace("%s",i),a=e(s);e.inArray(i,o.tags)>=0&&a.addClass("is-disabled"),t&&n.length>0&&!i.toLowerCase().startsWith(n)&&a.css({display:"none"}),a.appendTo(o.$autocomplete)})),o._autocomplete._bindClick(),e(document).not(o.$autocomplete).on("click",(function(){o._autocomplete._hide()}))},_bindClick:function(){e(o.$autocomplete).off("click").on("click","."+o.AUTOCOMPLETE_ITEM_CLASS,(function(t){if(e(t.target).hasClass("is-disabled"))return!1;o.$input.addClass("is-autocomplete").val(e(this).text()),o._autocomplete._hide(),o._bindEvent("autocompleteTagSelect"),(t=e.Event("keyup")).key=o.KEY_ENTER,o.$input.trigger(t)}))},_show:function(){if(!o._autocomplete._isSet())return!1;o.$autocomplete.css({left:o.$input[0].offsetLeft,minWidth:o.$input.width()}).insertAfter(o.$input),setTimeout((function(){o._autocomplete._bindClick(),o.$autocomplete.addClass("is-active")}),100)},_hide:function(){o.$autocomplete.removeClass("is-active")},_get:function(t){return o.options.autocomplete[t]},_exists:function(){return void 0!==o.$autocomplete}},o._updateValue=function(){o.$element.prop("value",o.tags.join(o.KEY_COMMA))},o._focus=function(){o.$input.on("focus",(function(){o._bindEvent("focus"),!o._autocomplete._isSet()||o.$input.hasClass("is-autocomplete")||o.$input.hasClass("is-edit")||(o._autocomplete._build(),o._autocomplete._show())}))},o._toObject=function(t){return t.reduce((function(t,e,n){return t[n]=e,t}),{})},o._validate=function(t,e){var n="";switch(!0){case!t:case void 0===t:case 0===t.length:o._cancel(),n="empty";break;case t.length>0&&t.lengtho.options.maxLength:n="maxLength";break;case o.options.max>0&&o.tags.length>=o.options.max:o.$input.hasClass("is-edit")||(n="max");break;case o.options.email:/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(t)||(n="email")}return!(n.length>0)||(e?o._errors(n):n)},o._exists=function(t){return e.inArray(t,o.tags)>=0},o._errors=function(t){return 0===t.length||(o._autocomplete._exists()&&o.$autocomplete.remove(),o._displayErrors(o.options.errors[t].replace("%s",o.options[t]),t)),!1},o._displayErrors=function(t,n){var i=e(o.ERROR_CONTENT.replace("%s",t)).attr("data-error",n),s=o.options.errors.timeout;return!e("."+o.ERROR_CLASS+'[data-error="'+n+'"]').length&&(i.hide().insertAfter(o.$list).slideDown(),!(!s||s<=0)&&(e("."+o.ERROR_CLASS).on("click",(function(){o._collapseErrors(e(this))})),void setTimeout((function(){o._collapseErrors()}),s)))},o._collapseErrors=function(t){var n=t||e("."+o.ERROR_CLASS);n.slideUp(300,(function(){n.remove()}))},o._getInstance=function(){return window.inputTags.instances[o.UNIQID]},o._setInstance=function(){window.inputTags.instances[o.UNIQID]=o},o._isSet=function(t){return!(void 0===o.options[t]||!1===o.options[t]||o.options[t].length)},o._callMethod=function(t,e){if(void 0===e.options[t]||"function"!=typeof e.options[t])return!1;e.options[t].apply(this,Array.prototype.slice.call(arguments,1))},o._initEvent=function(e,n){if(!e)return!1;switch(t(e)){case"string":n(e,o);break;case"object":e.forEach((function(t,e){n(t,o)}))}return!0},o._bindEvent=function(t){return o._initEvent(t,(function(t,e){o._callMethod(t,e)}))},o._unbindEvent=function(t){return o._initEvent(t,(function(t){o.options[t]=!1}))},o.init(),o._bindEvent("init"),o._setInstance(o)})),{on:function(t,e){window.inputTags.methods.event(t,e)}};if(window.inputTags.methods[n]){var o=e(this).attr("data-uniqid"),i=window.inputTags.instances[o];return void 0===i?e.error("[undefined instance] No inputTags instance found."):window.inputTags.methods[n].apply(this,Array.prototype.slice.call(arguments,1))}e.error("[undefined method] The method ["+n+"] does not exists.")},e.fn.inputTags.defaults={tags:[],keys:[],minLength:2,maxLength:30,max:6,email:!1,only:!0,init:!1,create:!1,update:!1,destroy:!1,focus:!1,selected:!1,unselected:!1,change:!1,autocompleteTagSelect:!1,editable:!0,autocomplete:{values:[],only:!1},errors:{empty:"Attention, vous ne pouvez pas ajouter un tag vide.",minLength:"Attention, votre tag doit avoir au minimum %s caractères.",maxLength:"Attention, votre tag ne doit pas dépasser %s caractères.",max:"Attention, le nombre de tags ne doit pas dépasser %s.",email:"Attention, l'adresse email que vous avez entré n'est pas valide",exists:"Attention, ce tag existe déjà !",autocomplete_only:"Attention, vous devez sélectionner une valeur dans la liste.",timeout:8e3}}},829:()=>{}},n={};function o(t){var i=n[t];if(void 0!==i)return i.exports;var s=n[t]={exports:{}};return e[t](s,s.exports,o),s.exports}o.m=e,t=[],o.O=(e,n,i,s)=>{if(!n){var a=1/0;for(c=0;c=s)&&Object.keys(o.O).every((t=>o.O[t](n[u])))?n.splice(u--,1):(r=!1,s0&&t[c-1][2]>s;c--)t[c]=t[c-1];t[c]=[n,i,s]},o.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),(()=>{var t={249:0,6:0};o.O.j=e=>0===t[e];var e=(e,n)=>{var i,s,[a,r,u]=n,l=0;if(a.some((e=>0!==t[e]))){for(i in r)o.o(r,i)&&(o.m[i]=r[i]);if(u)var c=u(o)}for(e&&e(n);lo(419)));var i=o.O(void 0,[6],(()=>o(829)));i=o.O(i)})();
2 |
--------------------------------------------------------------------------------
/dist/inputTags.min.css:
--------------------------------------------------------------------------------
1 | @font-face{font-family:Source Sans Pro;font-style:italic;font-weight:300;src:url(http://fonts.gstatic.com/s/sourcesanspro/v18/6xKwdSBYKcSV-LCoeQqfX1RYOo3qPZZMkids18E.ttf) format("truetype")}@font-face{font-family:Source Sans Pro;font-style:italic;font-weight:400;src:url(http://fonts.gstatic.com/s/sourcesanspro/v18/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7nsDc.ttf) format("truetype")}@font-face{font-family:Source Sans Pro;font-style:italic;font-weight:600;src:url(http://fonts.gstatic.com/s/sourcesanspro/v18/6xKwdSBYKcSV-LCoeQqfX1RYOo3qPZY4lCds18E.ttf) format("truetype")}@font-face{font-family:Source Sans Pro;font-style:normal;font-weight:300;src:url(http://fonts.gstatic.com/s/sourcesanspro/v18/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdr.ttf) format("truetype")}@font-face{font-family:Source Sans Pro;font-style:normal;font-weight:400;src:url(http://fonts.gstatic.com/s/sourcesanspro/v18/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7g.ttf) format("truetype")}@font-face{font-family:Source Sans Pro;font-style:normal;font-weight:600;src:url(http://fonts.gstatic.com/s/sourcesanspro/v18/6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwlxdr.ttf) format("truetype")}@font-face{font-family:Ubuntu Condensed;font-style:normal;font-weight:400;src:url(http://fonts.gstatic.com/s/ubuntucondensed/v15/u-4k0rCzjgs5J7oXnJcM_0kACGMtT-Dfrg.ttf) format("truetype")}*{box-sizing:border-box}.color{color:#19bc9c!important}body,html{background-color:#fff;font-family:Ubuntu Condensed,sans-serif;height:100%;margin:0;width:100%}h1{margin-bottom:32px;text-align:center}div.container{background-color:#fff;box-shadow:2px 2px 8px rgba(0,0,0,.1);display:block;margin:32px auto;padding:16px 32px;width:50%}div.inputTags-list{background-color:#f9f9f9;border:1px solid rgba(25,188,156,.35);border-radius:4px;box-shadow:1px 2px 2px hsla(0,0%,87%,.2);padding:6px;width:100%}div.inputTags-list,div.inputTags-list span.inputTags-item{-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;display:inline-block}div.inputTags-list span.inputTags-item{background-color:#19bc9c;border-radius:3px;color:#fff;margin:2px;opacity:1;padding:3px 22px 4px 8px;position:relative;text-align:center}div.inputTags-list span.inputTags-item.is-edit{display:none}div.inputTags-list span.inputTags-item.is-hidden{display:none!important}div.inputTags-list span.inputTags-item.is-exists{background-color:rgba(231,76,60,.7)}div.inputTags-list span.inputTags-item span.value{cursor:pointer}div.inputTags-list span.inputTags-item i{cursor:pointer;font-family:sans-serif;font-size:20px;font-style:normal;font-weight:400;line-height:1;position:absolute;right:6px;top:50%;-webkit-transform:translateY(-50%);-khtml-transform:translateY(-50%);-moz-transform:translateY(-50%);-ms-transform:translateY(-50%);-o-transform:translateY(-50%);-webkit-transition:color .2s;-khtml-transition:color .2s;-moz-transition:color .2s;-ms-transition:color .2s;-o-transition:color .2s;z-index:10}div.inputTags-list span.inputTags-item i:hover{color:#e74c3c}div.inputTags-list input.inputTags-field{background-color:transparent;border:none;margin-left:4px}div.inputTags-list input.inputTags-field:active,div.inputTags-list input.inputTags-field:focus{outline:0}div.inputTags-list input.inputTags-field.is-edit{-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px dashed #c4c4c4;border-radius:4px;margin:0 2px;padding:4px 8px 3px}div.inputTags-list ul.inputTags-autocomplete-list{-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;background-color:#fff;border:1px solid #ddd;border-radius:4px;list-style-type:none;margin:0;max-height:192px;opacity:0;overflow-y:auto;padding:0;position:absolute;-webkit-transform:scaleY(0);-khtml-transform:scaleY(0);-moz-transform:scaleY(0);-ms-transform:scaleY(0);-o-transform:scaleY(0);-webkit-transform-origin:50% 0;-khtml-transform-origin:50% 0;-moz-transform-origin:50% 0;-ms-transform-origin:50% 0;-o-transform-origin:50% 0;-webkit-transition-duration:.2s;-khtml-transition-duration:.2s;-moz-transition-duration:.2s;-ms-transition-duration:.2s;-o-transition-duration:.2s;z-index:100}div.inputTags-list ul.inputTags-autocomplete-list.is-active{opacity:1;-webkit-transform:scaleY(1);-khtml-transform:scaleY(1);-moz-transform:scaleY(1);-ms-transform:scaleY(1);-o-transform:scaleY(1)}div.inputTags-list ul.inputTags-autocomplete-list li{border-bottom:1px solid #ddd;cursor:pointer;height:32px;line-height:32px;padding:0 16px;-webkit-transition-duration:.3s;-khtml-transition-duration:.3s;-moz-transition-duration:.3s;-ms-transition-duration:.3s;-o-transition-duration:.3s;-webkit-transition-duration:.2s;-khtml-transition-duration:.2s;-moz-transition-duration:.2s;-ms-transition-duration:.2s;-o-transition-duration:.2s}div.inputTags-list ul.inputTags-autocomplete-list li:last-child{border:none}div.inputTags-list ul.inputTags-autocomplete-list li:hover{background-color:#19bc9c;color:#fff}div.inputTags-list ul.inputTags-autocomplete-list li.is-disabled{background-color:#f7f7f7;color:initial;cursor:default}p.inputTags-error{-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;background-color:rgba(231,76,60,.7);border-radius:4px;color:#fff;cursor:pointer;margin:0;padding:.5em 1em;position:relative}p.inputTags-error:first-of-type{margin-top:8px}p.inputTags-error:after{content:"\000D7";font-size:28px;position:absolute;right:.5em;top:50%;-webkit-transform:translateY(-50%);-khtml-transform:translateY(-50%);-moz-transform:translateY(-50%);-ms-transform:translateY(-50%);-o-transform:translateY(-50%)}
2 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | inputTags
6 |
7 |
8 |
9 |
51 |
52 |
53 |
54 |
inputTags jQuery plugin
55 |
56 |
57 | Event:
58 |
59 |
60 |
61 | Autocomplete:
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "inputTags.js",
3 | "version": "1.0.0",
4 | "description": "inputTags.js is a simple jQuery plugin allowing to add, edit or remove tags in a tags list.",
5 | "scripts": {
6 | "build": "cross-env NODE_ENV=production npx mix"
7 | },
8 | "devDependencies": {
9 | "cross-env": "^7.0.3",
10 | "laravel-mix": "^6.0.43",
11 | "less": "^4.1.2",
12 | "less-loader": "^10.2.0"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | (function ($) {
2 |
3 | $.fn.inputTags = function (options) {
4 | if (!('inputTags' in window)) {
5 | window.inputTags = {
6 | instances: []
7 | };
8 | }
9 |
10 | window.inputTags.methods = {
11 | tags: function (element, callback) {
12 | if (element) {
13 | switch (typeof element) {
14 | case 'string':
15 | switch (element) {
16 | case '_toString':
17 | var str = _instance.tags.toString();
18 |
19 | if (callback) {
20 | return callback(str);
21 | }
22 | return str;
23 | case '_toObject':
24 | var obj = _instance._toObject(_instance.tags);
25 |
26 | if (callback) {
27 | return callback(obj);
28 | }
29 | return obj;
30 | case '_toJSON':
31 | var obj = _instance._toObject(_instance.tags);
32 | var json = JSON.stringify(obj);
33 |
34 | if (callback) {
35 | return callback(json);
36 | }
37 | return json;
38 | case '_toArray':
39 | if (callback) {
40 | return callback(_instance.tags);
41 | }
42 | return _instance.tags;
43 | }
44 |
45 | var partials = element.split(',');
46 |
47 | if (partials.length > 1) {
48 | var current = _instance.tags;
49 | _instance.tags = current.concat(partials);
50 | } else {
51 | _instance.tags.push(partials[0]);
52 | }
53 | break;
54 | case 'object':
55 | var current = _instance.tags;
56 |
57 | if ('[object Object]' === Object.prototype.toString.call(element)) {
58 | element = Object.keys(element).map(function (k) {
59 | return element[k];
60 | });
61 | }
62 |
63 | _instance.tags = current.concat(element);
64 | break;
65 | case 'function':
66 | return element(_instance.tags);
67 | }
68 |
69 | _instance._clean();
70 | _instance._fill();
71 | _instance._updateValue();
72 |
73 | _instance.destroy();
74 |
75 | _instance._setInstance(_instance);
76 |
77 | if (callback) {
78 | return callback(_instance.tags);
79 | }
80 | }
81 |
82 | return _instance.tags;
83 | },
84 | event: function (method, callback) {
85 | _instance.options[method] = callback;
86 | _instance._setInstance(_instance);
87 | },
88 | options: function (key, value) {
89 | if (!key && !value) {
90 | return _instance.options;
91 | }
92 |
93 | if (value) {
94 | _instance.options[key] = value;
95 | _instance._setInstance(_instance);
96 | } else {
97 | return _instance.options[key];
98 | }
99 | },
100 | destroy: function () {
101 | var id = $(this).attr('data-uniqid');
102 | delete window.inputTags.instances[id];
103 | }
104 | };
105 |
106 | if ('object' === typeof options || !options) {
107 | var options = $.extend(true, {}, $.fn.inputTags.defaults, options);
108 |
109 | this.each(function () {
110 | var self = $(this);
111 |
112 | /* Constants */
113 | self.KEY_ENTER = 'Enter';
114 | self.KEY_COMMA = ',';
115 | self.KEY_ESCAPE = 'Escape';
116 | self.UNIQID = Math.round(Date.now() / (Math.random() * (548 - 54) - 54));
117 | self.DEFAULT_CLASS = 'inputTags';
118 | self.ELEMENT_CLASS = self.DEFAULT_CLASS + '-' + self.UNIQID;
119 | self.LIST_CLASS = self.DEFAULT_CLASS + '-list';
120 | self.ITEM_CLASS = self.DEFAULT_CLASS + '-item';
121 | self.ITEM_CONTENT = '%s×';
122 | self.FIELD_CLASS = self.DEFAULT_CLASS + '-field';
123 | self.ERROR_CLASS = self.DEFAULT_CLASS + '-error';
124 | self.ERROR_CONTENT = '%s
';
125 |
126 | self.AUTOCOMPLETE_LIST_CLASS = self.DEFAULT_CLASS + '-autocomplete-list';
127 | self.AUTOCOMPLETE_ITEM_CLASS = self.DEFAULT_CLASS + '-autocomplete-item';
128 | self.AUTOCOMPLETE_ITEM_CONTENT = '- %s
';
129 |
130 | /* Variables */
131 | self.options = options;
132 | self.keys = [self.KEY_ENTER, self.KEY_COMMA, self.KEY_ESCAPE];
133 | self.tags = [];
134 |
135 | if (self.options.keys.length > 0) {
136 | self.keys = self.keys.concat(self.options.keys);
137 | }
138 |
139 | self.init = function () {
140 | self.addClass(self.ELEMENT_CLASS).attr('data-uniqid', self.UNIQID);
141 |
142 | self.$element = $('.' + self.ELEMENT_CLASS);
143 | self.$element.hide();
144 |
145 | /* initialization */
146 | self.build();
147 | self.fill();
148 | self.save();
149 | self.edit();
150 | self.destroy();
151 | self._autocomplete._init();
152 | self._focus();
153 | };
154 |
155 | /**
156 | * Build plugin's HTML skeleton
157 | *
158 | * @returns {void}
159 | */
160 | self.build = function () {
161 | self.$html = $('').addClass(self.LIST_CLASS);
162 | self.$input = $('
').attr({
163 | 'type': 'text',
164 | 'class': self.FIELD_CLASS
165 | });
166 |
167 | self.$html.insertAfter(self.$element).prepend(self.$input);
168 |
169 | self.$list = self.$element.next('.' + self.LIST_CLASS);
170 |
171 | self.$list.on('click', function (e) {
172 | if ($(e.target).hasClass('inputTags-field')) {
173 | return false;
174 | }
175 | self.$input.focus();
176 | });
177 | };
178 |
179 | /**
180 | * Init tags list if present in options, otherwise returns false
181 | *
182 | * @returns {void | boolean}
183 | */
184 | self.fill = function () {
185 | self._getDefaultValues();
186 |
187 | if (0 === self.options.tags) {
188 | return false;
189 | }
190 |
191 | self._concatenate();
192 | self._updateValue();
193 |
194 | self._fill();
195 | };
196 |
197 | /**
198 | * Fills tag list
199 | *
200 | * @returns {void}
201 | */
202 | self._fill = function () {
203 | self.tags.forEach(function (value, i) {
204 | var validate = self._validate(value, false);
205 |
206 | if (true === validate || ('max' === validate && i + 1 <= self.options.max)) {
207 | self._buildItem(value);
208 | }
209 | });
210 | };
211 |
212 | /**
213 | * Clear HTML tags list
214 | *
215 | * @returns {void}
216 | */
217 | self._clean = function () {
218 | $('.' + self.ITEM_CLASS, self.$list).remove();
219 | };
220 |
221 | /**
222 | * Add or edit tag depends on key pressed
223 | *
224 | * @returns {void}
225 | */
226 | self.save = function () {
227 | self.$input.on('keyup', function (e) {
228 | e.preventDefault();
229 |
230 | var key = e.key;
231 | var value = self.$input.val().trim();
232 |
233 | if ($.inArray(key, self.keys) < 0) {
234 | self._autocomplete._init(true);
235 | self._autocomplete._show();
236 |
237 | return false;
238 | }
239 |
240 | if (self.KEY_ESCAPE === key) {
241 | self._cancel();
242 | self._autocomplete._hide();
243 |
244 | return false;
245 | }
246 |
247 | value = self.KEY_COMMA === key ? value.slice(0, -1) : value;
248 |
249 | if (!self._validate(value, true)) {
250 | return false;
251 | }
252 |
253 | if (self.options.only && self._exists(value)) {
254 | self._errors('exists');
255 |
256 | return false;
257 | }
258 |
259 | if (self.$input.hasClass('is-edit')) {
260 | var old_value = self.$input.attr('data-old-value');
261 |
262 | if (old_value === value) {
263 | self._cancel();
264 | return true;
265 | }
266 |
267 | self._update(old_value, value);
268 | self._clean();
269 | self._fill();
270 | } else {
271 | if (self._autocomplete._isSet() && self._autocomplete._get('only')) {
272 | if ($.inArray(value, self._autocomplete._get('values')) < 0) {
273 | self._autocomplete._hide();
274 | self._errors('autocomplete_only');
275 | return false;
276 | }
277 | }
278 |
279 | if (self._exists(value)) {
280 | self.$input.removeClass('is-autocomplete');
281 | self._errors('exists');
282 |
283 | var $tag = $('[data-tag="' + value + '"]', self.$list);
284 |
285 | $tag.addClass('is-exists');
286 |
287 | setTimeout(function () {
288 | $tag.removeClass('is-exists');
289 | }, 300);
290 | return false;
291 | }
292 |
293 | self._buildItem(value);
294 | self._insert(value);
295 | }
296 |
297 | self._cancel();
298 | self._updateValue();
299 | self.destroy();
300 | self._autocomplete._build();
301 |
302 | self._setInstance(self);
303 |
304 | self.$input.focus();
305 |
306 | return false;
307 | });
308 | };
309 |
310 | /**
311 | * Init edit input when a tag is focused
312 | *
313 | * @returns {void}
314 | */
315 | self.edit = function () {
316 | self.$list.on('click', '.' + self.ITEM_CLASS, function (e) {
317 | if ($(e.target).hasClass('close-item') || false === self.options.editable || (self._autocomplete._isSet() && self._autocomplete._get('only'))) {
318 | self._cancel();
319 | return true;
320 | }
321 |
322 | var $item = $(this).addClass('is-edit');
323 | var value = $('.value', $item).text();
324 |
325 | self.$input.width($item.outerWidth()).insertAfter($item).addClass('is-edit').attr('data-old-value', value).val(value).focus();
326 |
327 | self._bindEvent('selected');
328 |
329 | self.$input.on('blur', function () {
330 | self._cancel();
331 | self._bindEvent('unselected');
332 | });
333 | });
334 | };
335 |
336 | /**
337 | * Delete tag
338 | *
339 | * @returns {void}
340 | */
341 | self.destroy = function () {
342 | $('.' + self.ITEM_CLASS, self.$list).off('click').on('click', '.close-item', function () {
343 |
344 | var $item = $(this).parent('.' + self.ITEM_CLASS);
345 | var value = $('.value', $item).text();
346 |
347 | $item.addClass('is-closed');
348 |
349 | setTimeout(function () {
350 | self._pop(value);
351 | self._updateValue();
352 | $item.remove();
353 |
354 | self._autocomplete._build();
355 |
356 | self.$input.focus();
357 |
358 | self._setInstance(self);
359 | }, 200);
360 | });
361 | };
362 |
363 | /**
364 | * Build and inject tag into HTML list
365 | *
366 | * @returns {void}
367 | */
368 | self._buildItem = function (value) {
369 | var $content = $(self.ITEM_CONTENT.replace('%s', value));
370 | var $item = $('
').addClass(self.ITEM_CLASS + ' is-closed').attr('data-tag', value).html($content);
371 |
372 | $item.insertBefore(self.$input).delay(100).queue(function () {
373 | $(this).removeClass('is-closed');
374 | });
375 | };
376 |
377 | /**
378 | * Returns tag index
379 | *
380 | * @returns {number}
381 | */
382 | self._getIndex = function (value) {
383 | return self.tags.indexOf(value);
384 | };
385 |
386 | /**
387 | * Remove extra tags only if > max option and concat user tags
388 | *
389 | * @returns {void}
390 | */
391 | self._concatenate = function () {
392 | if (self.options.max > 0) {
393 | if (self.options.tags.length > self.options.max) {
394 | self.options.tags.splice(-Math.abs(self.options.tags.length - self.options.max));
395 | }
396 | }
397 |
398 | self.tags = self.tags.concat(self.options.tags);
399 | };
400 |
401 |
402 | /**
403 | * Get default values
404 | *
405 | * @returns {void}
406 | */
407 | self._getDefaultValues = function () {
408 | if (self.$element.val().length > 0) {
409 | self.tags = self.tags.concat(self.$element.val().split(self.KEY_COMMA));
410 | } else {
411 | self.$element.prop('value', '');
412 | }
413 | };
414 |
415 | /**
416 | * Insert item
417 | *
418 | * @param {string} item
419 | * @returns {void}
420 | */
421 | self._insert = function (item) {
422 | self.tags.push(item);
423 |
424 | self._bindEvent(['change', 'create']);
425 | };
426 |
427 | /**
428 | * Swap tag value
429 | *
430 | * @param {string} old_value
431 | * @param {string} new_value
432 | * @returns {void}
433 | */
434 | self._update = function (old_value, new_value) {
435 | var index = self._getIndex(old_value);
436 | self.tags[index] = new_value;
437 |
438 | self._bindEvent(['change', 'update']);
439 | };
440 |
441 | /**
442 | * Delete item based on value parameter
443 | *
444 | * @param {string} value
445 | * @returns {void}
446 | */
447 | self._pop = function (value) {
448 | var index = self._getIndex(value);
449 |
450 | if (index < 0) {
451 | return false;
452 | }
453 |
454 | self.tags.splice(index, 1);
455 |
456 | self._bindEvent(['change', 'destroy']);
457 | };
458 |
459 | /**
460 | * Reset input field
461 | *
462 | * @returns {void}
463 | */
464 | self._cancel = function () {
465 | $('.' + self.ITEM_CLASS).removeClass('is-edit');
466 |
467 | self.$input
468 | .removeClass('is-edit is-autocomplete')
469 | .removeAttr('data-old-value style')
470 | .val('')
471 | .appendTo(self.$list);
472 | };
473 |
474 | /**
475 | * Autocomplete object
476 | *
477 | * @returns {object}
478 | */
479 | self._autocomplete = {
480 |
481 | /**
482 | * Is autocomplete list have values
483 | *
484 | * @returns {boolean}
485 | */
486 | _isSet: function () {
487 | return self.options.autocomplete.values.length > 0;
488 | },
489 |
490 | /**
491 | * Init autocomplete
492 | *
493 | * @param {boolean | undefined} filter
494 | * @returns {boolean | void}
495 | */
496 | _init: function (filter) {
497 | if (!self._autocomplete._isSet()) {
498 | return false;
499 | }
500 |
501 | self._autocomplete._build(filter);
502 | },
503 |
504 | /**
505 | * Build autocomplete HTML list
506 | *
507 | * @param {boolean | undefined} filter
508 | * @returns {void}
509 | */
510 | _build: function (filter) {
511 | var value = self.$input.val().trim().toLowerCase();
512 |
513 | if (self._autocomplete._exists()) {
514 | self.$autocomplete.remove();
515 | }
516 |
517 | self.$autocomplete = $('').addClass(self.AUTOCOMPLETE_LIST_CLASS);
518 |
519 | self._autocomplete._get('values').forEach(function (v) {
520 | var li = self.AUTOCOMPLETE_ITEM_CONTENT.replace('%s', v);
521 | var $item = $(li)
522 |
523 | if ($.inArray(v, self.tags) >= 0) {
524 | $item.addClass('is-disabled')
525 | }
526 |
527 | if (filter && value.length > 0 && !v.toLowerCase().startsWith(value)) {
528 | $item.css({ display: 'none' })
529 | }
530 |
531 | $item.appendTo(self.$autocomplete);
532 | });
533 |
534 | self._autocomplete._bindClick();
535 |
536 | $(document)
537 | .not(self.$autocomplete)
538 | .on('click', function () {
539 | self._autocomplete._hide();
540 | });
541 | },
542 |
543 | /**
544 | * Bind click event on list item
545 | *
546 | * @returns {void}
547 | */
548 | _bindClick: function () {
549 | $(self.$autocomplete).off('click').on('click', '.' + self.AUTOCOMPLETE_ITEM_CLASS, function (e) {
550 | if ($(e.target).hasClass('is-disabled')) {
551 | return false;
552 | }
553 |
554 | self.$input.addClass('is-autocomplete').val($(this).text());
555 | self._autocomplete._hide();
556 | self._bindEvent('autocompleteTagSelect');
557 |
558 | var e = $.Event("keyup");
559 | e.key = self.KEY_ENTER;
560 | self.$input.trigger(e);
561 | });
562 | },
563 |
564 | /**
565 | * Show autocomplete list
566 | *
567 | * @returns {boolean | void}
568 | */
569 | _show: function () {
570 | if (!self._autocomplete._isSet()) {
571 | return false;
572 | }
573 |
574 | self.$autocomplete
575 | .css({
576 | 'left': self.$input[0].offsetLeft,
577 | 'minWidth': self.$input.width()
578 | })
579 | .insertAfter(self.$input);
580 |
581 | setTimeout(function () {
582 | self._autocomplete._bindClick();
583 | self.$autocomplete.addClass('is-active');
584 | }, 100);
585 | },
586 |
587 | /**
588 | * Hide autocomplete list
589 | *
590 | * @returns {void}
591 | */
592 | _hide: function () {
593 | self.$autocomplete.removeClass('is-active');
594 | },
595 |
596 | /**
597 | * Get autocomplete item based on key parameter
598 | *
599 | * @param {string} key
600 | * @returns {boolean | void}
601 | */
602 | _get: function (key) {
603 | return self.options.autocomplete[key];
604 | },
605 |
606 | /**
607 | * Returns true if autocomplete is defined, false otherwise
608 | *
609 | * @returns {boolean}
610 | */
611 | _exists: function () {
612 | return undefined !== self.$autocomplete;
613 | }
614 | };
615 |
616 | /**
617 | * Update plugin binded input value
618 | *
619 | * @returns {void}
620 | */
621 | self._updateValue = function () {
622 | self.$element.prop('value', self.tags.join(self.KEY_COMMA));
623 | };
624 |
625 | /**
626 | * Define focus events on input
627 | *
628 | * @returns {void}
629 | */
630 | self._focus = function () {
631 | self.$input.on('focus', function () {
632 | self._bindEvent('focus');
633 |
634 | if (self._autocomplete._isSet() && !self.$input.hasClass('is-autocomplete') && !self.$input.hasClass('is-edit')) {
635 | self._autocomplete._build();
636 | self._autocomplete._show();
637 | }
638 | });
639 | };
640 |
641 | /**
642 | * Convert array into object
643 | *
644 | * @param {string[]} arr
645 | * @returns {object}
646 | */
647 | self._toObject = function (arr) {
648 | return arr.reduce(function (o, v, i) {
649 | o[i] = v;
650 | return o;
651 | }, {});
652 | };
653 |
654 | /**
655 | * Input validation
656 | *
657 | * @param {string} value
658 | * @param {boolean} alert
659 | * @returns {boolean}
660 | */
661 | self._validate = function (value, alert) {
662 | var type = '', re;
663 |
664 | switch (true) {
665 | case !value:
666 | case undefined === value:
667 | case 0 === value.length:
668 | self._cancel();
669 | type = 'empty';
670 | break;
671 | case value.length > 0 && value.length < self.options.minLength:
672 | type = 'minLength';
673 | break;
674 | case value.length > self.options.maxLength:
675 | type = 'maxLength';
676 | break;
677 | case self.options.max > 0 && self.tags.length >= self.options.max:
678 | if (!self.$input.hasClass('is-edit')) {
679 | type = 'max';
680 | }
681 | break;
682 | case self.options.email:
683 | re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
684 |
685 | if (!re.test(value)) {
686 | type = 'email';
687 | }
688 | break;
689 | }
690 |
691 | if (type.length > 0) {
692 | return alert ? self._errors(type) : type;
693 | }
694 |
695 | return true;
696 | };
697 |
698 | /**
699 | * Returns true if value is in tags list, false otherwise
700 | *
701 | * @param {string} value
702 | * @returns {boolean}
703 | */
704 | self._exists = function (value) {
705 | return $.inArray(value, self.tags) >= 0;
706 | }
707 |
708 | /**
709 | * Get error message
710 | *
711 | * @param {string} type
712 | * @returns {boolean}
713 | */
714 | self._errors = function (type) {
715 | if (0 === type.length) {
716 | return false;
717 | }
718 |
719 | if (self._autocomplete._exists()) {
720 | self.$autocomplete.remove();
721 | }
722 |
723 | self._displayErrors(self.options.errors[type].replace('%s', self.options[type]), type);
724 |
725 | return false;
726 | };
727 |
728 | /**
729 | * Display errors(s) if any
730 | *
731 | * @param {string} error
732 | * @param {string} type
733 | * @returns {void}
734 | */
735 | self._displayErrors = function (error, type) {
736 | var $error = $(self.ERROR_CONTENT.replace('%s', error)).attr('data-error', type);
737 | var timeout = self.options.errors.timeout;
738 |
739 | if ($('.' + self.ERROR_CLASS + '[data-error="' + type + '"]').length) {
740 | return false;
741 | }
742 |
743 | $error.hide().insertAfter(self.$list).slideDown();
744 |
745 | if (!timeout || timeout <= 0) {
746 | return false;
747 | }
748 |
749 | $('.' + self.ERROR_CLASS).on('click', function () {
750 | self._collapseErrors($(this));
751 | });
752 |
753 | setTimeout(function () {
754 | self._collapseErrors();
755 | }, timeout);
756 | };
757 |
758 | /**
759 | * Clears error(s) if any
760 | *
761 | * @param {object} $elem
762 | * @returns {void}
763 | */
764 | self._collapseErrors = function ($elem) {
765 |
766 | var $obj = $elem ? $elem : $('.' + self.ERROR_CLASS);
767 |
768 | $obj.slideUp(300, function () {
769 | $obj.remove();
770 | });
771 | };
772 |
773 | /**
774 | * Retrieve inputTags instance based on uniqid
775 | *
776 | * @returns {object}
777 | */
778 | self._getInstance = function () {
779 | return window.inputTags.instances[self.UNIQID];
780 | };
781 |
782 | /**
783 | * Store instance based on uniqid
784 | *
785 | * @returns {void}
786 | */
787 | self._setInstance = function () {
788 | window.inputTags.instances[self.UNIQID] = self;
789 | };
790 |
791 | /**
792 | * Returns true if elem exists on options object, false otherwise
793 | *
794 | * @returns {boolean}
795 | */
796 | self._isSet = function (elem) {
797 | return !(undefined === self.options[elem] || false === self.options[elem] || self.options[elem].length);
798 | };
799 |
800 | /**
801 | * Call method based on method_name parameter if exists on options, returns false otherwise
802 | *
803 | * @param {string} method_name
804 | * @param {object} self
805 | * @returns {void}
806 | */
807 | self._callMethod = function (method_name, self) {
808 | if (undefined === self.options[method_name] || 'function' !== typeof self.options[method_name]) {
809 | return false;
810 | }
811 |
812 | self.options[method_name].apply(this, Array.prototype.slice.call(arguments, 1));
813 | }
814 |
815 | /**
816 | * Init an event
817 | *
818 | * @param {string | object} method
819 | * @param {function} callback
820 | * @returns {boolean}
821 | */
822 | self._initEvent = function (method, callback) {
823 | if (!method) {
824 | return false;
825 | }
826 |
827 | switch (typeof method) {
828 | case 'string':
829 | callback(method, self);
830 | break;
831 | case 'object':
832 | method.forEach(function (m, i) {
833 | callback(m, self);
834 | });
835 | break;
836 | }
837 |
838 | return true;
839 | };
840 |
841 | /**
842 | * Bind an event
843 | *
844 | * @param {string | object} method
845 | * @returns {boolean}
846 | */
847 | self._bindEvent = function (method) {
848 | return self._initEvent(method, function (m, s) {
849 | self._callMethod(m, s);
850 | });
851 | };
852 |
853 | /**
854 | * Unbind an event
855 | *
856 | * @param {string | object} method
857 | * @returns {boolean}
858 | */
859 | self._unbindEvent = function (method) {
860 | return self._initEvent(method, function (m) {
861 | self.options[m] = false;
862 | });
863 | };
864 |
865 | self.init();
866 |
867 | self._bindEvent('init');
868 |
869 | self._setInstance(self);
870 | });
871 |
872 | return {
873 | on: function (method, callback) {
874 | window.inputTags.methods.event(method, callback);
875 | }
876 | };
877 |
878 | } else if (window.inputTags.methods[options]) {
879 | var id = $(this).attr('data-uniqid');
880 | var _instance = window.inputTags.instances[id];
881 |
882 | if (undefined === _instance) {
883 | return $.error("[undefined instance] No inputTags instance found.");
884 | }
885 |
886 | return window.inputTags.methods[options].apply(this, Array.prototype.slice.call(arguments, 1));
887 | } else {
888 | $.error("[undefined method] The method [" + options + "] does not exists.");
889 | }
890 | };
891 |
892 | $.fn.inputTags.defaults = {
893 | tags: [],
894 | keys: [],
895 | minLength: 2,
896 | maxLength: 30,
897 | max: 6,
898 | email: false,
899 | only: true,
900 | init: false,
901 | create: false,
902 | update: false,
903 | destroy: false,
904 | focus: false,
905 | selected: false,
906 | unselected: false,
907 | change: false,
908 | autocompleteTagSelect: false,
909 | editable: true,
910 | autocomplete: {
911 | values: [],
912 | only: false
913 | },
914 | errors: {
915 | empty: 'Attention, vous ne pouvez pas ajouter un tag vide.',
916 | minLength: 'Attention, votre tag doit avoir au minimum %s caractères.',
917 | maxLength: 'Attention, votre tag ne doit pas dépasser %s caractères.',
918 | max: 'Attention, le nombre de tags ne doit pas dépasser %s.',
919 | email: 'Attention, l\'adresse email que vous avez entré n\'est pas valide',
920 | exists: 'Attention, ce tag existe déjà !',
921 | autocomplete_only: 'Attention, vous devez sélectionner une valeur dans la liste.',
922 | timeout: 8000
923 | }
924 | };
925 |
926 | })(jQuery);
927 |
--------------------------------------------------------------------------------
/src/inputTags.css:
--------------------------------------------------------------------------------
1 | @font-face{font-family:Source Sans Pro;font-style:italic;font-weight:300;src:url(http://fonts.gstatic.com/s/sourcesanspro/v18/6xKwdSBYKcSV-LCoeQqfX1RYOo3qPZZMkids18E.ttf) format("truetype")}@font-face{font-family:Source Sans Pro;font-style:italic;font-weight:400;src:url(http://fonts.gstatic.com/s/sourcesanspro/v18/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7nsDc.ttf) format("truetype")}@font-face{font-family:Source Sans Pro;font-style:italic;font-weight:600;src:url(http://fonts.gstatic.com/s/sourcesanspro/v18/6xKwdSBYKcSV-LCoeQqfX1RYOo3qPZY4lCds18E.ttf) format("truetype")}@font-face{font-family:Source Sans Pro;font-style:normal;font-weight:300;src:url(http://fonts.gstatic.com/s/sourcesanspro/v18/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdr.ttf) format("truetype")}@font-face{font-family:Source Sans Pro;font-style:normal;font-weight:400;src:url(http://fonts.gstatic.com/s/sourcesanspro/v18/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7g.ttf) format("truetype")}@font-face{font-family:Source Sans Pro;font-style:normal;font-weight:600;src:url(http://fonts.gstatic.com/s/sourcesanspro/v18/6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwlxdr.ttf) format("truetype")}@font-face{font-family:Ubuntu Condensed;font-style:normal;font-weight:400;src:url(http://fonts.gstatic.com/s/ubuntucondensed/v15/u-4k0rCzjgs5J7oXnJcM_0kACGMtT-Dfrg.ttf) format("truetype")}*{box-sizing:border-box}.color{color:#19bc9c!important}body,html{background-color:#fff;font-family:Ubuntu Condensed,sans-serif;height:100%;margin:0;width:100%}h1{margin-bottom:32px;text-align:center}div.container{background-color:#fff;box-shadow:2px 2px 8px rgba(0,0,0,.1);display:block;margin:32px auto;padding:16px 32px;width:50%}div.inputTags-list{background-color:#f9f9f9;border:1px solid rgba(25,188,156,.35);border-radius:4px;box-shadow:1px 2px 2px hsla(0,0%,87%,.2);padding:6px;width:100%}div.inputTags-list,div.inputTags-list span.inputTags-item{-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;display:inline-block}div.inputTags-list span.inputTags-item{background-color:#19bc9c;border-radius:3px;color:#fff;margin:2px;opacity:1;padding:3px 22px 4px 8px;position:relative;text-align:center}div.inputTags-list span.inputTags-item.is-edit{display:none}div.inputTags-list span.inputTags-item.is-hidden{display:none!important}div.inputTags-list span.inputTags-item.is-exists{background-color:rgba(231,76,60,.7)}div.inputTags-list span.inputTags-item span.value{cursor:pointer}div.inputTags-list span.inputTags-item i{cursor:pointer;font-family:sans-serif;font-size:20px;font-style:normal;font-weight:400;line-height:1;position:absolute;right:6px;top:50%;-webkit-transform:translateY(-50%);-khtml-transform:translateY(-50%);-moz-transform:translateY(-50%);-ms-transform:translateY(-50%);-o-transform:translateY(-50%);-webkit-transition:color .2s;-khtml-transition:color .2s;-moz-transition:color .2s;-ms-transition:color .2s;-o-transition:color .2s;z-index:10}div.inputTags-list span.inputTags-item i:hover{color:#e74c3c}div.inputTags-list input.inputTags-field{background-color:transparent;border:none;margin-left:4px}div.inputTags-list input.inputTags-field:active,div.inputTags-list input.inputTags-field:focus{outline:none}div.inputTags-list input.inputTags-field.is-edit{-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px dashed #c4c4c4;border-radius:4px;margin:0 2px;padding:4px 8px 3px}div.inputTags-list ul.inputTags-autocomplete-list{-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;background-color:#fff;border:1px solid #ddd;border-radius:4px;list-style-type:none;margin:0;max-height:192px;opacity:0;overflow-y:auto;padding:0;position:absolute;-webkit-transform:scaleY(0);-khtml-transform:scaleY(0);-moz-transform:scaleY(0);-ms-transform:scaleY(0);-o-transform:scaleY(0);-webkit-transform-origin:50% 0;-khtml-transform-origin:50% 0;-moz-transform-origin:50% 0;-ms-transform-origin:50% 0;-o-transform-origin:50% 0;-webkit-transition-duration:.2s;-khtml-transition-duration:.2s;-moz-transition-duration:.2s;-ms-transition-duration:.2s;-o-transition-duration:.2s;z-index:100}div.inputTags-list ul.inputTags-autocomplete-list.is-active{opacity:1;-webkit-transform:scaleY(1);-khtml-transform:scaleY(1);-moz-transform:scaleY(1);-ms-transform:scaleY(1);-o-transform:scaleY(1)}div.inputTags-list ul.inputTags-autocomplete-list li{border-bottom:1px solid #ddd;cursor:pointer;height:32px;line-height:32px;padding:0 16px;-webkit-transition-duration:.3s;-khtml-transition-duration:.3s;-moz-transition-duration:.3s;-ms-transition-duration:.3s;-o-transition-duration:.3s;-webkit-transition-duration:.2s;-khtml-transition-duration:.2s;-moz-transition-duration:.2s;-ms-transition-duration:.2s;-o-transition-duration:.2s}div.inputTags-list ul.inputTags-autocomplete-list li:last-child{border:none}div.inputTags-list ul.inputTags-autocomplete-list li:hover{background-color:#19bc9c;color:#fff}div.inputTags-list ul.inputTags-autocomplete-list li.is-disabled{background-color:#f7f7f7;color:initial;cursor:default}p.inputTags-error{-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;background-color:rgba(231,76,60,.7);border-radius:4px;color:#fff;cursor:pointer;margin:0;padding:.5em 1em;position:relative}p.inputTags-error:first-of-type{margin-top:8px}p.inputTags-error:after{content:"\000D7";font-size:28px;position:absolute;right:.5em;top:50%;-webkit-transform:translateY(-50%);-khtml-transform:translateY(-50%);-moz-transform:translateY(-50%);-ms-transform:translateY(-50%);-o-transform:translateY(-50%)}
2 |
--------------------------------------------------------------------------------
/src/inputTags.less:
--------------------------------------------------------------------------------
1 | @import url(http://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,300italic,400italic,600italic);
2 | @import url(http://fonts.googleapis.com/css?family=Ubuntu+Condensed);
3 |
4 | @border: #ddd;
5 | @black: #000;
6 | @white: #fff;
7 | @red: #e74c3c;
8 | @error: fade(@red, 70%);
9 | @blue: #3498db;
10 | @main: #19BC9C;
11 | @container: #f9f9f9;
12 |
13 | .transform(@string) {
14 | -webkit-transform: @string;
15 | -khtml-transform: @string;
16 | -moz-transform: @string;
17 | -ms-transform: @string;
18 | -o-transform: @string;
19 | }
20 |
21 | .transform-origin(@x:center, @y:center) {
22 | -webkit-transform-origin: @x @y;
23 | -khtml-transform-origin: @x @y;
24 | -moz-transform-origin: @x @y;
25 | -ms-transform-origin: @x @y;
26 | -o-transform-origin: @x @y;
27 | }
28 |
29 | .transition(@transition) {
30 | -webkit-transition: @transition;
31 | -khtml-transition: @transition;
32 | -moz-transition: @transition;
33 | -ms-transition: @transition;
34 | -o-transition: @transition;
35 | }
36 |
37 | .transition-duration(@duration:0) {
38 | -webkit-transition-duration: @duration;
39 | -khtml-transition-duration: @duration;
40 | -moz-transition-duration: @duration;
41 | -ms-transition-duration: @duration;
42 | -o-transition-duration: @duration;
43 | }
44 |
45 | .border-radius(@radius: 4px) {
46 | -webkit-border-radius: @radius;
47 | -moz-border-radius: @radius;
48 | border-radius: @radius;
49 |
50 | -moz-background-clip: padding;
51 | -webkit-background-clip: padding-box;
52 | background-clip: padding-box;
53 | }
54 |
55 | @radius: 4px;
56 |
57 | * { box-sizing: border-box; }
58 | .color{ color: @main !important; }
59 |
60 | html, body{
61 | width: 100%; height: 100%;
62 | margin: 0;
63 | font-family: 'Ubuntu Condensed', sans-serif;
64 | background-color: @white;
65 | }
66 |
67 | h1{
68 | text-align: center;
69 | margin-bottom: 32px;
70 | }
71 |
72 | div.container{
73 | display: block;
74 | width: 50%;
75 | margin: 32px auto;
76 | padding: 16px 32px;
77 | background-color: @white;
78 | box-shadow: 2px 2px 8px fade(@black, 10%);
79 | }
80 |
81 | div.inputTags-list{
82 | display: inline-block;
83 | width: 100%;
84 | padding: 6px;
85 | border: 1px solid fade(@main, 35%);
86 | background-color: @container;
87 | box-shadow: 1px 2px 2px fade(@border, 20%);
88 | .border-radius(@radius);
89 |
90 | span.inputTags-item{
91 | position: relative;
92 | display: inline-block;
93 | margin: 2px; padding: 3px 22px 4px 8px;
94 | background-color: @main;
95 | text-align: center;
96 | color: @white;
97 | opacity: 1;
98 | .border-radius(3px);
99 |
100 | &.is-edit{ display: none; }
101 | &.is-hidden{ display: none !important; }
102 | &.is-exists{ background-color: @error; }
103 |
104 | span.value{ cursor: pointer; }
105 |
106 | i{
107 | position: absolute;
108 | top: 50%; right: 6px;
109 | font-size: 20px;
110 | cursor: pointer;
111 | z-index: 10;
112 | font-weight: 400;
113 | font-family: sans-serif;
114 | line-height: 1;
115 | font-style: normal;
116 | .transition(color .2s);
117 | .transform(translateY(-50%));
118 |
119 | &:hover{ color: @red; }
120 | }
121 | }
122 | input.inputTags-field{
123 | border: none;
124 | margin-left: 4px;
125 | background-color: transparent;
126 |
127 | &:focus, &:active{ outline: none; }
128 | &.is-edit{
129 | margin: 0 2px;
130 | padding: 4px 8px 3px 8px;
131 | border: 1px dashed darken(@border, 10%);
132 | .border-radius(@radius);
133 | }
134 | }
135 |
136 | ul.inputTags-autocomplete-list{
137 | position: absolute;
138 | max-height: 192px;
139 | margin: 0; padding: 0;
140 | list-style-type: none;
141 | background-color: @white;
142 | border: 1px solid @border;
143 | overflow-y: auto;
144 | z-index: 100;
145 | opacity: 0;
146 | .border-radius(@radius);
147 | .transform(scaleY(0));
148 | .transform-origin(50%, 0);
149 | .transition-duration(.2s);
150 |
151 | &.is-active{
152 | opacity: 1;
153 | .transform(scaleY(1));
154 | }
155 |
156 | li{
157 | height: 32px; line-height: 32px;
158 | padding: 0 16px;
159 | cursor: pointer;
160 | border-bottom: 1px solid @border;
161 | .transition-duration(.3s);
162 | .transition-duration(.2s);
163 |
164 | &:last-child{ border: none; }
165 |
166 | &:hover{
167 | background-color: @main;
168 | color: @white;
169 | }
170 |
171 | &.is-disabled{
172 | cursor: default;
173 | background-color: lighten(@border, 10%);
174 | color: initial;
175 | }
176 | }
177 | }
178 | }
179 |
180 | p.inputTags-error{
181 | position: relative;
182 | margin: 0; padding: 0.5em 1em;
183 | color: @white;
184 | background-color: @error;
185 | cursor: pointer;
186 | .border-radius(@radius);
187 |
188 | &:first-of-type{ margin-top: 8px; }
189 | &:after{
190 | position: absolute;
191 | content: "\000D7";
192 | top: 50%; right: 0.5em;
193 | .transform(translateY(-50%));
194 | font-size: 28px;
195 | }
196 | }
--------------------------------------------------------------------------------
/webpack.mix.js:
--------------------------------------------------------------------------------
1 | // webpack.mix.js
2 |
3 | let mix = require('laravel-mix')
4 | let { rmSync, renameSync } = require('fs')
5 |
6 | mix
7 | .disableNotifications()
8 | .disableSuccessNotifications()
9 | .before(() => {
10 | try {
11 | rmSync('src/inputTags.css')
12 | } catch {}
13 |
14 | try {
15 | rmSync('dist/', { recursive: true, force: true })
16 | } catch {}
17 | })
18 | .js('src/index', 'dist/inputTags.jquery')
19 | .less('src/inputTags.less', 'src/inputTags.css')
20 | .minify([
21 | 'dist/inputTags.jquery.js',
22 | 'src/inputTags.css'
23 | ])
24 | .options({ manifest: false })
25 | .after(() => {
26 | setTimeout(() => {
27 | rmSync('dist/inputTags.jquery.js')
28 | renameSync('src/inputTags.min.css', 'dist/inputTags.min.css')
29 | })
30 | })
31 |
--------------------------------------------------------------------------------