├── .babelrc
├── .codeclimate.yml
├── .eslintrc.js
├── .gitignore
├── .hooks
└── pre-commit
├── .prettierrc.js
├── .travis.yml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── demo.gif
├── dist-modules
├── DragAndDropHelper.js
├── README.md
├── ReactTags.js
├── RemoveComponent.js
├── Suggestions.js
├── Tag.js
├── constants.js
└── utils.js
├── dist
├── README.md
├── ReactTags.min.js
└── ReactTags.min.js.map
├── docs
├── inline-false.png
└── inline-true.png
├── example
├── index.html
├── main.js
├── reactTags.css
└── vendor
│ ├── JSXTransformer.js
│ └── ReactDND.min.js
├── lib
├── DragAndDropHelper.js
├── ReactTags.js
├── RemoveComponent.js
├── Suggestions.js
├── Tag.js
├── constants.js
└── utils.js
├── package-lock.json
├── package.json
├── release.sh
├── set_up_hooks.sh
├── test
├── reactTags.test.js
├── setupTest.js
├── suggestions.test.js
├── tag.test.js
└── utils.test.js
├── webpack-dev-server.config.js
├── webpack-production.config.js
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["env", "stage-0", "react"]
3 | }
4 |
--------------------------------------------------------------------------------
/.codeclimate.yml:
--------------------------------------------------------------------------------
1 | version: "2"
2 | plugins:
3 | eslint:
4 | enabled: true
5 | channel: "eslint-4"
6 | config:
7 | config: .eslintrc.js
8 | exclude_patterns:
9 | - "dist/"
10 | - "dist-modules/"
11 | - "**/node_modules/"
12 | - "**/vendor/"
13 | - "**/docs/"
14 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /*
2 | "off" or 0 - turn the rule off
3 | "warn" or 1 - turn the rule on as a warning (doesn’t affect exit code)
4 | "error" or 2 - turn the rule on as an error (exit code is 1 when triggered)
5 | */
6 |
7 | module.exports = {
8 | env: {
9 | es6: true,
10 | node: true,
11 | browser: true
12 | },
13 | extends: ["eslint:recommended", "plugin:react/recommended"],
14 | parserOptions: {
15 | ecmaVersion: "6",
16 | ecmaFeatures: {
17 | jsx: true
18 | },
19 | sourceType: "module"
20 | },
21 | plugins: ["react", "jsx-a11y"],
22 | "parser": "babel-eslint",
23 | rules: {
24 | "indent": [2, 2],
25 | "linebreak-style": [2, "unix"],
26 | "quotes": [2, "single"],
27 | "semi": [2, "always"],
28 | "eqeqeq": [2, "smart"],
29 | "no-unused-vars": 1,
30 | "no-undef": 1,
31 | "default-case": 1,
32 | "comma-dangle": [2, "always-multiline"],
33 | "no-trailing-spaces": 2,
34 | "no-extra-bind": 1,
35 | "no-useless-escape": 1,
36 |
37 | /** react rules start here **/
38 |
39 | "react/jsx-no-duplicate-props": 2,
40 | "react/no-deprecated": 1,
41 | "react/no-is-mounted": 2,
42 | "react/no-unknown-property": 1,
43 | "react/prop-types": 2,
44 | "react/no-direct-mutation-state": 2,
45 | "react/jsx-key": 2,
46 | "react/no-children-prop": 1,
47 | "react/display-name": 1,
48 | "react/no-multi-comp": 1,
49 | "react/sort-comp": 1,
50 | "react/no-did-mount-set-state": 1,
51 | "react/no-did-update-set-state": 1,
52 | "react/no-typos": 2,
53 | "react/jsx-equals-spacing": 2,
54 | "react/jsx-no-undef": 2,
55 | "react/jsx-uses-vars": 1,
56 | "react/no-find-dom-node": 1,
57 | "react/no-render-return-value": 1,
58 |
59 | /** jsx-ally rules start here **/
60 | "jsx-a11y/alt-text": 2,
61 | "jsx-a11y/html-has-lang": 1,
62 | "jsx-a11y/iframe-has-title": 1,
63 | "jsx-a11y/click-events-have-key-events": 1
64 | },
65 | overrides: [
66 | {
67 | files: [
68 | "**/*.test.js"
69 | ],
70 | env: {
71 | jest: true // now **/*.test.js files' env has both es6 *and* jest
72 | },
73 | // Can't extend in overrides: https://github.com/eslint/eslint/issues/8813
74 | // "extends": ["plugin:jest/recommended"]
75 | plugins: ["jest"],
76 | rules: {
77 | "jest/no-disabled-tests": "warn",
78 | "jest/no-focused-tests": "error",
79 | "jest/no-identical-title": "error",
80 | "jest/prefer-to-have-length": "warn"
81 | }
82 | }
83 | ],
84 | };
85 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | .idea
4 | .vscode
5 | coverage
6 |
--------------------------------------------------------------------------------
/.hooks/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | printf "\nValidating Javascript files:\n"
3 |
4 | # Check for eslint
5 | which eslint &> /dev/null
6 | if [[ $? -eq 1 ]]; then
7 | printf "\t\033[41mPlease install Eslint\033[0m (npm i --save-dev eslint)"
8 | exit 1
9 | fi
10 |
11 | PASSED=TRUE
12 | for file in $(git diff --cached --name-only | grep -E '\.(js|jsx)$')
13 |
14 | do
15 | git show ":$file" | node_modules/.bin/eslint --stdin --stdin-filename "$file" # we only want to lint the staged changes, not any un-staged changes
16 | if [[ $? -eq 0 ]]; then
17 | printf "\t\033[32mEsLint Passed: $file\033[0m"
18 | else
19 | printf "\t\033[41mEsLint Failed: $FILE\033[0m"
20 | PASSED=false
21 | fi
22 | done
23 |
24 | if ! $PASSED; then
25 | printf "\033[41mCOMMIT FAILED:\033[0m Please fix the eslint errors in your commit"
26 | exit 1
27 | else
28 | printf "\033[42mCOMMIT SUCCEEDED\033[0m\n"
29 | fi
30 |
31 | exit $?
32 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | singleQuote: true,
3 | printWidth: 80,
4 | proseWrap: 'always',
5 | tabWidth: 2,
6 | useTabs: false,
7 | trailingComma: 'es5',
8 | jsxBracketSameLine: true,
9 | arrowParens: 'always',
10 | parser: 'flow',
11 | };
12 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "9"
4 | - "8"
5 | - "7"
6 | - "6"
7 | before_install:
8 | - npm install -g npm
9 | - npm install -g greenkeeper-lockfile
10 | install: npm install
11 | before_script: greenkeeper-lockfile-update
12 | # Only the node version 6 job will upload the lockfile
13 | after_script: greenkeeper-lockfile-upload
14 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ### Contributing
2 |
3 | 1. Have an idea to change the API - add new props, classnames, etc? Please raise an issue and discuss it before you start implementing it.
4 | 2. Want to submit a new pull request (PR)? Great! Make sure you add tests (or give an explanation why you won't be adding any). Try to get feedback early and often!
5 | 3. This project uses [prettier](https://github.com/prettier/prettier) to enforce a JS styleguide. Make sure you run `npm run format` before sending a PR.
6 | 4. **DO NOT** commit any code in the `dist` and `dist-modules` directory. Code in these files is auto-generated and is on released on Github for easy distribution. Once your PR is merged, one of the owners will release a build with your changes alongside publishing your change on NPM.
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Prakhar Srivastav
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | React-Tags
2 | ===
3 |
4 | [](https://github.com/prakhar1989/react-tags/blob/master/LICENSE)
5 | [](https://www.npmjs.com/package/react-tag-input)
6 | [](https://david-dm.org/yahoo/react-dnd-touch-backend)
7 | [](https://david-dm.org/yahoo/react-dnd-touch-backend#info=devDependencies)
8 | [](https://www.npmjs.com/package/react-tag-input)
9 | [](https://travis-ci.org/prakhar1989/react-tags)
10 | [](https://greenkeeper.io/)
11 | [](https://github.com/prettier/prettier)
12 |
13 |
14 |
15 | React-tags is a simple tagging component ready to drop in your React projects. The component is inspired by GMail's *To* field in the compose window.
16 |
17 | ### [Looking for Maintainers](https://github.com/prakhar1989/react-tags/issues/197)
18 | Interested in working on this library as an owner and working with the community to add more features? Let me [know!](https://github.com/prakhar1989/react-tags/issues/197)
19 |
20 | ### Features
21 | - Autocomplete based on a suggestion list
22 | - Keyboard friendly and mouse support
23 | - Reorder tags using drag and drop
24 |
25 | ### Why
26 | Because I was looking for an excuse to build a standalone component and publish it in the wild? To be honest, I needed a tagging component that provided the above features for my [React-Surveyman](http://github.com/prakhar1989/react-surveyman) project. Since I was unable to find one which met my requirements (and the fact that I generally enjoy re-inventing the wheel) this is what I came up with.
27 |
28 |
29 | ### Demo
30 | 
31 |
32 | Check it out [here](https://stackblitz.com/edit/react-tag-input-1nelrc)
33 |
34 | ### Installation
35 | The preferred way of using the component is via NPM
36 |
37 | ```
38 | npm install --save react-tag-input
39 | ```
40 | It is, however, also available to be used separately (`dist/ReactTags.min.js`). If you prefer this method remember to include [ReactDND](https://github.com/gaearon/react-dnd) as a dependancy. Refer to the [example](https://stackblitz.com/edit/react-tag-input) to see how this works.
41 |
42 | ### Usage
43 |
44 | Here's a sample implementation that initializes the component with a list of initial `tags` and `suggestions` list. Apart from this, there are multiple events, handlers for which need to be set. For more details, go through the [API](#Options).
45 |
46 |
47 | ```javascript
48 | import React from 'react';
49 | import ReactDOM from 'react-dom';
50 | import { WithContext as ReactTags } from 'react-tag-input';
51 |
52 | const KeyCodes = {
53 | comma: 188,
54 | enter: 13,
55 | };
56 |
57 | const delimiters = [KeyCodes.comma, KeyCodes.enter];
58 |
59 | class App extends React.Component {
60 | constructor(props) {
61 | super(props);
62 |
63 | this.state = {
64 | tags: [
65 | { id: "Thailand", text: "Thailand" },
66 | { id: "India", text: "India" }
67 | ],
68 | suggestions: [
69 | { id: 'USA', text: 'USA' },
70 | { id: 'Germany', text: 'Germany' },
71 | { id: 'Austria', text: 'Austria' },
72 | { id: 'Costa Rica', text: 'Costa Rica' },
73 | { id: 'Sri Lanka', text: 'Sri Lanka' },
74 | { id: 'Thailand', text: 'Thailand' }
75 | ]
76 | };
77 | this.handleDelete = this.handleDelete.bind(this);
78 | this.handleAddition = this.handleAddition.bind(this);
79 | this.handleDrag = this.handleDrag.bind(this);
80 | }
81 |
82 | handleDelete(i) {
83 | const { tags } = this.state;
84 | this.setState({
85 | tags: tags.filter((tag, index) => index !== i),
86 | });
87 | }
88 |
89 | handleAddition(tag) {
90 | this.setState(state => ({ tags: [...state.tags, tag] }));
91 | }
92 |
93 | handleDrag(tag, currPos, newPos) {
94 | const tags = [...this.state.tags];
95 | const newTags = tags.slice();
96 |
97 | newTags.splice(currPos, 1);
98 | newTags.splice(newPos, 0, tag);
99 |
100 | // re-render
101 | this.setState({ tags: newTags });
102 | }
103 |
104 | render() {
105 | const { tags, suggestions } = this.state;
106 | return (
107 |