├── src ├── index.js ├── lib.js ├── assets │ └── ic_close_black_16px.svg ├── tag.vue ├── webpack.config.js ├── input.vue └── typing.vue ├── anim.gif ├── index.html ├── example ├── index.js ├── minimal.vue └── full.vue ├── .eslintrc.yml ├── .gitignore ├── LICENSE ├── README.md ├── webpack.config.js ├── package.json └── dist ├── vue-tagsinput.js └── vue-tagsinput.js.map /src/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./input.vue') 2 | -------------------------------------------------------------------------------- /anim.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ginhing/vue-tagsinput/HEAD/anim.gif -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | vue-tagsinput demo 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib.js: -------------------------------------------------------------------------------- 1 | export const KEY_CODE = { 2 | LEFT: 37, 3 | RIGHT: 39, 4 | TAB: 9, 5 | BACKSPACE: 8 6 | } 7 | 8 | export const klass = { 9 | container: 'tags-input', 10 | input: 'input', 11 | gap: 'gap', 12 | tag: 'tag', 13 | placeholder: 'placeholder' 14 | } 15 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import MinimalExample from './minimal.vue' 3 | import FullExample from './full.vue' 4 | 5 | new Vue({ 6 | el: '#example', 7 | render(h) { 8 | return h('div', [ 9 | MinimalExample, 10 | FullExample 11 | ].map(h)) 12 | } 13 | }) 14 | -------------------------------------------------------------------------------- /src/assets/ic_close_black_16px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # parser: babel-eslint 3 | parserOptions: 4 | sourceType: module 5 | ecmaVersion: 7 6 | ecmaFeatures: 7 | jsx: true 8 | plugins: 9 | - react 10 | - vue 11 | extends: 12 | - eslint:recommended 13 | - plugin:react/recommended 14 | env: 15 | jest: true 16 | es6: true 17 | rules: 18 | no-var: 2 19 | no-unused-vars: 1 20 | semi: [1, never] 21 | -------------------------------------------------------------------------------- /example/minimal.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | node_modules 28 | 29 | # Optional npm cache directory 30 | .npm 31 | 32 | # Optional REPL history 33 | .node_repl_history 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Ginhing 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/tag.vue: -------------------------------------------------------------------------------- 1 | 12 | 36 | 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | >## This Project Is Deprecated 2 | # vue-tagsinput 3 | [![][npm-image]][npm-url] 4 | 5 | [npm-image]: https://img.shields.io/npm/v/vue-tagsinput.svg 6 | [npm-url]: https://npmjs.org/package/vue-tagsinput 7 | 8 | >kerboard supported(left, right, backspace, tab). 9 | ![](anim.gif) 10 | 11 | ## Requirement 12 | - vue: `^2.1.0` 13 | 14 | ## Usage 15 | ```html 16 |
17 | 21 |
22 | ``` 23 | 24 | ## Example 25 | [Live example](http://www.webpackbin.com/Vyc_H61fG) 26 | 27 | ## Props 28 | - tags(array\<{ text: string, readOnly: ?boolean, invalid: ?boolean }>, required): 29 | your tags to render 30 | - placeholder(string): a hint to the user 31 | - klass(object): override the class name (see below) 32 | 33 | ## Events 34 | - tags-change: (index: number, text: ?string) => void 35 | `text` will be undefined if the tag was removed 36 | - focus: (index: number) => void 37 | - blur: (index: number) => void 38 | 39 | ## Class Names 40 | ```javascript 41 | { 42 | container: 'tags-input', 43 | input: 'input', 44 | placeholder: 'placeholder', 45 | gap: 'gap', 46 | tag: 'tag' 47 | } 48 | ``` 49 | 50 | ## Migration from 0.x 51 | Because of the two-way props was obsoleted in Vue@2, we should deal with tags by one-way props and onChange callback explicitly. 52 | So most states will be handled in your scope. See the [full example](./example/full.vue). 53 | 54 | ## License 55 | 56 | [MIT](LICENSE) 57 | -------------------------------------------------------------------------------- /src/webpack.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | const path = require('path') 3 | const webpack = require('webpack') 4 | 5 | const {NODE_ENV} = process.env 6 | 7 | let config = { 8 | entry: './src/index.js', 9 | output: { 10 | path: path.resolve(__dirname, 'dist/'), 11 | filename: 'vue-tagsinput.js', 12 | libraryTarget: 'commonjs' 13 | }, 14 | module: { 15 | loaders: [ 16 | {test: /\.js$/, exclude: /node_modules/, loader: 'babel'}, 17 | {test: /\.css$/, loader: 'style!css'}, 18 | {test: /\.vue$/, loader: 'vue'}, 19 | ] 20 | }, 21 | plugins: [], 22 | devtool: 'source-map', 23 | babel: { 24 | presets: ['es2015', 'stage-2'], 25 | plugins: [] 26 | }, 27 | vue: { 28 | loaders: { 29 | scss: 'style!css' 30 | } 31 | } 32 | } 33 | 34 | // for production build 35 | if (NODE_ENV === 'production') { 36 | config.externals = { 37 | vue: 'commonjs vue' 38 | }, 39 | // config.babel.plugins.push('transform-runtime') 40 | config.plugins.push( 41 | new webpack.DefinePlugin({ 42 | 'process.env': { 43 | NODE_ENV: '"production"' 44 | } 45 | }) 46 | ) 47 | } else if (NODE_ENV === 'development') { 48 | config = Object.assign(config, { 49 | entry: './example/index.js', 50 | output: { 51 | path: path.resolve(__dirname, 'example'), 52 | filename: 'example.js' 53 | }, 54 | devtool: 'eval' 55 | }) 56 | } 57 | 58 | module.exports = config 59 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | const path = require('path') 3 | const webpack = require('webpack') 4 | 5 | const {NODE_ENV} = process.env 6 | 7 | let config = { 8 | entry: './src/index.js', 9 | output: { 10 | path: path.resolve(__dirname, 'dist/'), 11 | filename: 'vue-tagsinput.js', 12 | libraryTarget: 'commonjs' 13 | }, 14 | module: { 15 | loaders: [ 16 | {test: /\.js$/, exclude: /node_modules/, loader: 'babel'}, 17 | {test: /\.css$/, loader: 'style!css'}, 18 | {test: /\.vue$/, loader: 'vue'}, 19 | {test: /\.svg$/, loader: 'url?limit=1000'} 20 | ] 21 | }, 22 | plugins: [], 23 | devtool: 'source-map', 24 | babel: { 25 | presets: ['es2015', 'stage-2'], 26 | plugins: [] 27 | }, 28 | vue: { 29 | loaders: { 30 | scss: 'style!css' 31 | } 32 | } 33 | } 34 | 35 | // for production build 36 | if (NODE_ENV === 'production') { 37 | config.externals = { 38 | vue: 'commonjs vue' 39 | }, 40 | // config.babel.plugins.push('transform-runtime') 41 | config.plugins.push( 42 | new webpack.DefinePlugin({ 43 | 'process.env': { 44 | NODE_ENV: '"production"' 45 | } 46 | }) 47 | ) 48 | } else if (NODE_ENV === 'development') { 49 | config = Object.assign(config, { 50 | entry: './example/index.js', 51 | output: { 52 | path: path.resolve(__dirname, 'example'), 53 | filename: 'example.js' 54 | }, 55 | devtool: 'eval' 56 | }) 57 | } 58 | 59 | module.exports = config 60 | -------------------------------------------------------------------------------- /example/full.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 62 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-tagsinput", 3 | "version": "2.0.1", 4 | "description": "a tags input component based on Vue.js", 5 | "main": "dist/vue-tagsinput.js", 6 | "files": [ 7 | "dist/", 8 | "src/" 9 | ], 10 | "scripts": { 11 | "test": "eslint src/ --ext vue --ext js", 12 | "build": "NODE_ENV=production webpack", 13 | "prepublish": "npm run build", 14 | "dev": "NODE_ENV=development webpack-dev-server --hot --inline" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/Ginhing/vue-tagsinput.git" 19 | }, 20 | "keywords": [ 21 | "tagsinput", 22 | "vue", 23 | "input" 24 | ], 25 | "author": "Ginhing", 26 | "license": "MIT", 27 | "bugs": { 28 | "url": "https://github.com/Ginhing/vue-tagsinput/issues" 29 | }, 30 | "homepage": "https://github.com/Ginhing/vue-tagsinput#readme", 31 | "peerDependencies": { 32 | "vue": "^2.1.10" 33 | }, 34 | "devDependencies": { 35 | "babel-core": "^6.8.0", 36 | "babel-loader": "^6.2.4", 37 | "babel-plugin-transform-runtime": "^6.8.0", 38 | "babel-preset-es2015": "^6.6.0", 39 | "babel-preset-stage-2": "^6.5.0", 40 | "babel-runtime": "^6.0.0", 41 | "css-loader": "^0.23.1", 42 | "eslint": "^3.10.2", 43 | "eslint-plugin-vue": "^1.0.0", 44 | "file-loader": "^0.10.0", 45 | "style-loader": "^0.13.0", 46 | "url-loader": "^0.5.7", 47 | "vue": "^2.1.10", 48 | "vue-hot-reload-api": "^1.2.2", 49 | "vue-html-loader": "^1.0.0", 50 | "vue-loader": "^10.0.1", 51 | "vue-style-loader": "^1.0.0", 52 | "vue-template-compiler": "^2.1.10", 53 | "webpack": "^1.12.9", 54 | "webpack-dev-server": "^1.14.0" 55 | }, 56 | "dependencies": { 57 | "vuept": "^1.2.0" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/input.vue: -------------------------------------------------------------------------------- 1 | 21 | 39 | 108 | -------------------------------------------------------------------------------- /src/typing.vue: -------------------------------------------------------------------------------- 1 | 15 | 29 | 112 | -------------------------------------------------------------------------------- /dist/vue-tagsinput.js: -------------------------------------------------------------------------------- 1 | (function(e, a) { for(var i in a) e[i] = a[i]; }(exports, /******/ (function(modules) { // webpackBootstrap 2 | /******/ // The module cache 3 | /******/ var installedModules = {}; 4 | /******/ 5 | /******/ // The require function 6 | /******/ function __webpack_require__(moduleId) { 7 | /******/ 8 | /******/ // Check if module is in cache 9 | /******/ if(installedModules[moduleId]) 10 | /******/ return installedModules[moduleId].exports; 11 | /******/ 12 | /******/ // Create a new module (and put it into the cache) 13 | /******/ var module = installedModules[moduleId] = { 14 | /******/ exports: {}, 15 | /******/ id: moduleId, 16 | /******/ loaded: false 17 | /******/ }; 18 | /******/ 19 | /******/ // Execute the module function 20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 21 | /******/ 22 | /******/ // Flag the module as loaded 23 | /******/ module.loaded = true; 24 | /******/ 25 | /******/ // Return the exports of the module 26 | /******/ return module.exports; 27 | /******/ } 28 | /******/ 29 | /******/ 30 | /******/ // expose the modules object (__webpack_modules__) 31 | /******/ __webpack_require__.m = modules; 32 | /******/ 33 | /******/ // expose the module cache 34 | /******/ __webpack_require__.c = installedModules; 35 | /******/ 36 | /******/ // __webpack_public_path__ 37 | /******/ __webpack_require__.p = ""; 38 | /******/ 39 | /******/ // Load entry module and return exports 40 | /******/ return __webpack_require__(0); 41 | /******/ }) 42 | /************************************************************************/ 43 | /******/ ([ 44 | /* 0 */ 45 | /***/ function(module, exports, __webpack_require__) { 46 | 47 | 'use strict'; 48 | 49 | module.exports = __webpack_require__(1); 50 | 51 | /***/ }, 52 | /* 1 */ 53 | /***/ function(module, exports, __webpack_require__) { 54 | 55 | 56 | /* styles */ 57 | __webpack_require__(2) 58 | 59 | var Component = __webpack_require__(7)( 60 | /* script */ 61 | __webpack_require__(8), 62 | /* template */ 63 | __webpack_require__(22), 64 | /* scopeId */ 65 | "data-v-4a2297bc", 66 | /* cssModules */ 67 | null 68 | ) 69 | 70 | module.exports = Component.exports 71 | 72 | 73 | /***/ }, 74 | /* 2 */ 75 | /***/ function(module, exports, __webpack_require__) { 76 | 77 | // style-loader: Adds some css to the DOM by adding a \n\n\n\n\n// WEBPACK FOOTER //\n// input.vue?718e3e85","'use strict';\n\nvar types = {\n num: Number,\n str: String,\n bool: Boolean,\n func: Function,\n obj: Object,\n arr: Array,\n any: null\n};\n\nvar props = {\n validator: function validator(func) {\n return { validator: func };\n }\n};\nvar proto = {\n get required() {\n return {\n type: this._type,\n required: true\n };\n },\n default: function _default(_default2) {\n return {\n type: this._type,\n default: _default2\n };\n }\n};\n\nObject.keys(types).forEach(function (t) {\n props[t] = Object.create(proto, {\n _type: {\n value: types[t]\n }\n });\n});\n\nmodule.exports = props;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./~/vuept/dist/vuept.js\n// module id = 9\n// module chunks = 0","export const KEY_CODE = {\n LEFT: 37,\n RIGHT: 39,\n TAB: 9,\n BACKSPACE: 8\n}\n\nexport const klass = {\n container: 'tags-input',\n input: 'input',\n gap: 'gap',\n tag: 'tag',\n placeholder: 'placeholder'\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/lib.js","\n/* styles */\nrequire(\"!!vue-loader/node_modules/vue-style-loader!css-loader?minimize!../node_modules/vue-loader/lib/style-rewriter?id=data-v-bf3d33e8&scoped=true!../node_modules/vue-loader/lib/selector?type=styles&index=0!./tag.vue\")\n\nvar Component = require(\"!../node_modules/vue-loader/lib/component-normalizer\")(\n /* script */\n require(\"!!babel-loader!../node_modules/vue-loader/lib/selector?type=script&index=0!./tag.vue\"),\n /* template */\n require(\"!!../node_modules/vue-loader/lib/template-compiler?id=data-v-bf3d33e8!../node_modules/vue-loader/lib/selector?type=template&index=0!./tag.vue\"),\n /* scopeId */\n \"data-v-bf3d33e8\",\n /* cssModules */\n null\n)\n\nmodule.exports = Component.exports\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./src/tag.vue\n// module id = 11\n// module chunks = 0","// style-loader: Adds some css to the DOM by adding a \n\n\n\n\n// WEBPACK FOOTER //\n// tag.vue?ab9a0dc2","module.exports = \"data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjMDAwMDAwIiBoZWlnaHQ9IjE2IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgIDxwYXRoIGQ9Ik0xOSA2LjQxTDE3LjU5IDUgMTIgMTAuNTkgNi40MSA1IDUgNi40MSAxMC41OSAxMiA1IDE3LjU5IDYuNDEgMTkgMTIgMTMuNDEgMTcuNTkgMTkgMTkgMTcuNTkgMTMuNDEgMTJ6Ii8+CiAgICA8cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIi8+Cjwvc3ZnPg==\"\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./src/assets/ic_close_black_16px.svg\n// module id = 15\n// module chunks = 0","module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('span', {\n class: _vm.klass.tag,\n attrs: {\n \"invalid\": _vm.invalid\n }\n }, [_vm._v(\"\\n \" + _vm._s(_vm.text) + \"\\n \"), (_vm.remove) ? _c('img', {\n staticClass: \"hl-click\",\n attrs: {\n \"src\": _vm.crossIcon,\n \"alt\": \"close\"\n },\n on: {\n \"click\": _vm.remove\n }\n }) : _vm._e()])\n},staticRenderFns: []}\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./~/vue-loader/lib/template-compiler.js?id=data-v-bf3d33e8!./~/vue-loader/lib/selector.js?type=template&index=0!./src/tag.vue\n// module id = 16\n// module chunks = 0","\n/* styles */\nrequire(\"!!vue-loader/node_modules/vue-style-loader!css-loader?minimize!../node_modules/vue-loader/lib/style-rewriter?id=data-v-5643ad75&scoped=true!../node_modules/vue-loader/lib/selector?type=styles&index=0!./typing.vue\")\n\nvar Component = require(\"!../node_modules/vue-loader/lib/component-normalizer\")(\n /* script */\n require(\"!!babel-loader!../node_modules/vue-loader/lib/selector?type=script&index=0!./typing.vue\"),\n /* template */\n require(\"!!../node_modules/vue-loader/lib/template-compiler?id=data-v-5643ad75!../node_modules/vue-loader/lib/selector?type=template&index=0!./typing.vue\"),\n /* scopeId */\n \"data-v-5643ad75\",\n /* cssModules */\n null\n)\n\nmodule.exports = Component.exports\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./src/typing.vue\n// module id = 17\n// module chunks = 0","// style-loader: Adds some css to the DOM by adding a \n\n\n\n\n// WEBPACK FOOTER //\n// typing.vue?ffa0e1d8","module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('span', {\n class: _vm.klass.gap,\n on: {\n \"click\": function($event) {\n _vm.$emit('click')\n }\n }\n }, [_c('input', {\n directives: [{\n name: \"model\",\n rawName: \"v-model\",\n value: (_vm.text),\n expression: \"text\"\n }],\n ref: \"input\",\n class: _vm.klass.input,\n style: ({\n width: _vm.baseWidth + _vm.charLen(_vm.text) + 'ch'\n }),\n attrs: {\n \"type\": \"text\"\n },\n domProps: {\n \"value\": _vm._s(_vm.text)\n },\n on: {\n \"mousedown\": _vm.preventNativeActive,\n \"blur\": _vm.finishEditing,\n \"keydown\": _vm.keyPress,\n \"input\": function($event) {\n if ($event.target.composing) { return; }\n _vm.text = $event.target.value\n }\n }\n }), _vm._v(\" \"), (!_vm.typing) ? _vm._t(\"default\") : _vm._e()], 2)\n},staticRenderFns: []}\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./~/vue-loader/lib/template-compiler.js?id=data-v-5643ad75!./~/vue-loader/lib/selector.js?type=template&index=0!./src/typing.vue\n// module id = 21\n// module chunks = 0","module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n class: _vm.klass.container\n }, [_vm._l((_vm.normalizeTagItems), function(item, index) {\n return [_c('typing', {\n attrs: {\n \"index\": index,\n \"typing\": index === _vm.typingIndex,\n \"handle-insert\": _vm.insertTag,\n \"handle-remove\": _vm.removeTag,\n \"active-other\": _vm.activeOther\n },\n on: {\n \"click\": function($event) {\n _vm.focus(index)\n },\n \"blur\": function($event) {\n _vm.blur(index)\n }\n }\n }, [_c('span', {\n directives: [{\n name: \"show\",\n rawName: \"v-show\",\n value: (index === _vm.length && _vm.showPlaceholder),\n expression: \"index === length && showPlaceholder\"\n }],\n class: _vm.klass.placeholder\n }, [_vm._v(_vm._s(_vm.placeholder))])]), _vm._v(\" \"), (index !== _vm.length) ? _c('tag', {\n attrs: {\n \"text\": item.text,\n \"remove\": _vm.getRemoveHandle(item, index),\n \"invalid\": item.invalid\n }\n }) : _vm._e()]\n })], 2)\n},staticRenderFns: []}\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./~/vue-loader/lib/template-compiler.js?id=data-v-4a2297bc!./~/vue-loader/lib/selector.js?type=template&index=0!./src/input.vue\n// module id = 22\n// module chunks = 0"],"sourceRoot":""} --------------------------------------------------------------------------------